Fork assistant turns into new drafts (#1788)
* feat(chat): fork assistant turns into new drafts Builds chat-history attachments on the daemon and routes them into draft composers so users can continue an assistant turn in an existing workspace tab or New Workspace. * fix(chat): clean up fork draft handling * fix(chat): address fork draft review feedback * fix(server): build fork context from bounded timeline * fix(app): restore assistant turn footer in streams * fix(app): preserve fork draft setup * fix(app): tighten assistant fork footers * fix(app): lock active fork draft handoff * fix(app): preserve attachment-only draft retries * fix(app): unify composer attachment scopes * fix(app): align chat history attachment labels * fix(app): centralize attachment pill content * fix(app): trim fork context for workspace naming
This commit is contained in:
@@ -26,6 +26,7 @@ import type {
|
||||
FileUploadResponse,
|
||||
FileExplorerResponse,
|
||||
FetchAgentTimelineResponseMessage,
|
||||
AgentForkContextResponseMessage,
|
||||
GitSetupOptions,
|
||||
CheckoutStatusResponse,
|
||||
CheckoutCommitResponse,
|
||||
@@ -456,6 +457,7 @@ type ScheduleUpdatePayload = Extract<
|
||||
{ type: "schedule/update/response" }
|
||||
>["payload"];
|
||||
export type FetchAgentTimelinePayload = FetchAgentTimelineResponseMessage["payload"];
|
||||
export type AgentForkContextPayload = AgentForkContextResponseMessage["payload"];
|
||||
|
||||
export type FetchAgentTimelineDirection = FetchAgentTimelinePayload["direction"];
|
||||
export type FetchAgentTimelineProjection = FetchAgentTimelinePayload["projection"];
|
||||
@@ -502,6 +504,10 @@ function normalizeListCommandsOptions(
|
||||
}
|
||||
return { agentId: input, ...legacyOptions };
|
||||
}
|
||||
export interface AgentForkContextOptions {
|
||||
boundaryMessageId?: string;
|
||||
requestId?: string;
|
||||
}
|
||||
|
||||
type AgentRefreshedStatusPayload = z.infer<typeof AgentRefreshedStatusPayloadSchema>;
|
||||
type RestartRequestedStatusPayload = z.infer<typeof RestartRequestedStatusPayloadSchema>;
|
||||
@@ -2405,6 +2411,41 @@ export class DaemonClient {
|
||||
return payload;
|
||||
}
|
||||
|
||||
async buildAgentForkContext(
|
||||
agentId: string,
|
||||
options: AgentForkContextOptions = {},
|
||||
): Promise<AgentForkContextPayload> {
|
||||
const resolvedRequestId = this.createRequestId(options.requestId);
|
||||
const message = SessionInboundMessageSchema.parse({
|
||||
type: "agent.fork_context.request",
|
||||
agentId,
|
||||
requestId: resolvedRequestId,
|
||||
...(options.boundaryMessageId ? { boundaryMessageId: options.boundaryMessageId } : {}),
|
||||
});
|
||||
|
||||
const payload = await this.sendRequest({
|
||||
requestId: resolvedRequestId,
|
||||
message,
|
||||
timeout: 15000,
|
||||
options: { skipQueue: true },
|
||||
select: (msg) => {
|
||||
if (msg.type !== "agent.fork_context.response") {
|
||||
return null;
|
||||
}
|
||||
if (msg.payload.requestId !== resolvedRequestId) {
|
||||
return null;
|
||||
}
|
||||
return msg.payload;
|
||||
},
|
||||
});
|
||||
|
||||
if (payload.error) {
|
||||
throw new Error(payload.error);
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Agent Interaction
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user