Reuse thread activation action

This commit is contained in:
murashit 2026-05-29 05:39:33 +09:00
parent 89a5be5774
commit fd5ccb4598
3 changed files with 38 additions and 17 deletions

View file

@ -6,7 +6,6 @@ import {
upsertMcpServerDiagnostic,
type CapabilityProbeMethod,
} from "../../app-server/compatibility";
import { parseServiceTier } from "../../app-server/service-tier";
import type { McpServerStatus } from "../../generated/app-server/v2/McpServerStatus";
import type { Model } from "../../generated/app-server/v2/Model";
import type { RateLimitSnapshot } from "../../generated/app-server/v2/RateLimitSnapshot";
@ -14,6 +13,7 @@ import type { SkillMetadata } from "../../generated/app-server/v2/SkillMetadata"
import type { SharedAppServerMetadata } from "../../runtime/shared-app-server-state";
import { requestedOrConfiguredServiceTier, type RuntimeSnapshot } from "../../runtime/state";
import type { ChatAction, ChatState, ChatStateStore } from "./chat-state";
import { resumedThreadAction } from "./thread-resume";
export interface ChatAppServerControllerHost {
stateStore: ChatStateStore;
@ -99,18 +99,7 @@ export class ChatAppServerController {
if (!client) return null;
const serviceTier = requestedOrConfiguredServiceTier(this.host.runtimeSnapshot());
const response = await client.startThread(this.host.vaultPath, serviceTier);
this.dispatch({
type: "thread/resumed",
thread: response.thread,
cwd: response.cwd,
model: response.model,
reasoningEffort: response.reasoningEffort,
serviceTier: parseServiceTier(response.serviceTier),
approvalPolicy: response.approvalPolicy,
approvalsReviewer: response.approvalsReviewer,
activePermissionProfile: response.activePermissionProfile,
forceMessagesToBottom: true,
});
this.dispatch(resumedThreadAction({ response, forceMessagesToBottom: true }));
this.host.forceMessagesToBottom();
return response;
}

View file

@ -1,14 +1,29 @@
import { parseServiceTier } from "../../app-server/service-tier";
import { upsertThread } from "../../domain/threads/model";
import type { ReasoningEffort } from "../../generated/app-server/ReasoningEffort";
import type { ActivePermissionProfile } from "../../generated/app-server/v2/ActivePermissionProfile";
import type { ApprovalsReviewer } from "../../generated/app-server/v2/ApprovalsReviewer";
import type { AskForApproval } from "../../generated/app-server/v2/AskForApproval";
import type { Thread } from "../../generated/app-server/v2/Thread";
import type { ThreadResumeResponse } from "../../generated/app-server/v2/ThreadResumeResponse";
import type { ChatAction } from "./chat-state";
import type { DisplayItem } from "./display/types";
export interface ThreadActivationResponse {
thread: Thread;
cwd: string;
model: string;
serviceTier: string | null;
approvalPolicy: AskForApproval | null;
approvalsReviewer: ApprovalsReviewer | null;
activePermissionProfile: ActivePermissionProfile | null;
reasoningEffort: ReasoningEffort | null;
}
export interface ResumedThreadActionParams {
response: ThreadResumeResponse;
listedThreads: readonly Thread[];
response: ThreadActivationResponse;
listedThreads?: readonly Thread[];
displayItems?: readonly DisplayItem[];
forceMessagesToBottom?: boolean;
}
export function resumedThreadAction(params: ResumedThreadActionParams): Extract<ChatAction, { type: "thread/resumed" }> {
@ -24,6 +39,7 @@ export function resumedThreadAction(params: ResumedThreadActionParams): Extract<
approvalsReviewer: response.approvalsReviewer,
activePermissionProfile: response.activePermissionProfile,
...(params.displayItems ? { displayItems: params.displayItems } : {}),
listedThreads: upsertThread(params.listedThreads, response.thread),
...(params.listedThreads ? { listedThreads: upsertThread(params.listedThreads, response.thread) } : {}),
...(params.forceMessagesToBottom !== undefined ? { forceMessagesToBottom: params.forceMessagesToBottom } : {}),
};
}

View file

@ -30,6 +30,22 @@ describe("chat thread resume helpers", () => {
});
expect(action.listedThreads?.map((thread) => thread.id)).toEqual(["thread", "existing"]);
});
it("can build thread start actions without mutating the thread list", () => {
const resumed = threadFixture("thread", "Started");
const action = resumedThreadAction({
response: responseFixture(resumed),
forceMessagesToBottom: true,
});
expect(action).toMatchObject({
type: "thread/resumed",
thread: resumed,
forceMessagesToBottom: true,
});
expect(action.listedThreads).toBeUndefined();
});
});
function responseFixture(thread: Thread): ThreadResumeResponse {