mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
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:
@@ -226,6 +226,47 @@ describe("shared messages attachments", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps known text attachment context kinds and ignores future ones", () => {
|
||||
const parsed = SendAgentMessageRequestSchema.parse({
|
||||
type: "send_agent_message_request",
|
||||
requestId: "req-text-context",
|
||||
agentId: "agent-1",
|
||||
text: "Continue",
|
||||
attachments: [
|
||||
{
|
||||
type: "text",
|
||||
mimeType: "text/plain",
|
||||
contextKind: "chat_history",
|
||||
title: "Chat history",
|
||||
text: "Earlier context",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
mimeType: "text/plain",
|
||||
contextKind: "future_context",
|
||||
title: "Future context",
|
||||
text: "Future client hint",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(parsed.attachments).toEqual([
|
||||
{
|
||||
type: "text",
|
||||
mimeType: "text/plain",
|
||||
contextKind: "chat_history",
|
||||
title: "Chat history",
|
||||
text: "Earlier context",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
mimeType: "text/plain",
|
||||
title: "Future context",
|
||||
text: "Future client hint",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps known firstAgentContext attachments and drops unknown ones", () => {
|
||||
const parsed = CreatePaseoWorktreeRequestSchema.parse({
|
||||
type: "create_paseo_worktree_request",
|
||||
|
||||
@@ -843,12 +843,18 @@ export const GitHubIssueAttachmentSchema = z.object({
|
||||
body: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export const TextAttachmentSchema = z.object({
|
||||
type: z.literal("text"),
|
||||
mimeType: z.literal("text/plain"),
|
||||
title: z.string().nullable().optional(),
|
||||
text: z.string(),
|
||||
});
|
||||
export const TextAttachmentSchema = z
|
||||
.object({
|
||||
type: z.literal("text"),
|
||||
mimeType: z.literal("text/plain"),
|
||||
contextKind: z.string().optional(),
|
||||
title: z.string().nullable().optional(),
|
||||
text: z.string(),
|
||||
})
|
||||
.transform(({ contextKind, ...attachment }) => ({
|
||||
...attachment,
|
||||
...(contextKind === "chat_history" ? { contextKind } : {}),
|
||||
}));
|
||||
|
||||
export const ReviewAttachmentContextLineSchema = z.object({
|
||||
oldLineNumber: z.number().int().positive().nullable(),
|
||||
@@ -1274,6 +1280,13 @@ export const FetchAgentTimelineRequestMessageSchema = z.object({
|
||||
projection: z.enum(["projected", "canonical"]).optional(),
|
||||
});
|
||||
|
||||
export const AgentForkContextRequestMessageSchema = z.object({
|
||||
type: z.literal("agent.fork_context.request"),
|
||||
agentId: z.string(),
|
||||
boundaryMessageId: z.string().optional(),
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const SetAgentModeRequestMessageSchema = z.object({
|
||||
type: z.literal("set_agent_mode_request"),
|
||||
agentId: z.string(),
|
||||
@@ -2060,6 +2073,7 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
|
||||
RestartServerRequestMessageSchema,
|
||||
DaemonUpdateRequestMessageSchema,
|
||||
FetchAgentTimelineRequestMessageSchema,
|
||||
AgentForkContextRequestMessageSchema,
|
||||
SetAgentModeRequestMessageSchema,
|
||||
SetAgentModelRequestMessageSchema,
|
||||
SetAgentThinkingRequestMessageSchema,
|
||||
@@ -2335,6 +2349,8 @@ export const ServerInfoStatusPayloadSchema = z
|
||||
daemonDiagnostics: z.boolean().optional(),
|
||||
// COMPAT(daemonSelfUpdate): added in v0.1.93, remove gate after 2026-12-13.
|
||||
daemonSelfUpdate: z.boolean().optional(),
|
||||
// COMPAT(agentForkContext): added in v0.1.102, remove gate after 2026-12-28.
|
||||
agentForkContext: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
@@ -2926,6 +2942,18 @@ export const FetchAgentTimelineResponseMessageSchema = z.object({
|
||||
}),
|
||||
});
|
||||
|
||||
export const AgentForkContextResponseMessageSchema = z.object({
|
||||
type: z.literal("agent.fork_context.response"),
|
||||
payload: z.object({
|
||||
requestId: z.string(),
|
||||
agentId: z.string(),
|
||||
attachment: TextAttachmentSchema.nullable(),
|
||||
itemCount: z.number().int().nonnegative(),
|
||||
boundaryMessageId: z.string().nullable(),
|
||||
error: z.string().nullable(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const CancelAgentResponseMessageSchema = z.object({
|
||||
type: z.literal("cancel_agent_response"),
|
||||
payload: z.object({
|
||||
@@ -4158,6 +4186,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
|
||||
ArchiveWorkspaceResponseMessageSchema,
|
||||
FetchAgentResponseMessageSchema,
|
||||
FetchAgentTimelineResponseMessageSchema,
|
||||
AgentForkContextResponseMessageSchema,
|
||||
CancelAgentResponseMessageSchema,
|
||||
ClearAgentAttentionResponseMessageSchema,
|
||||
WorkspaceCreateResponseSchema,
|
||||
@@ -4320,6 +4349,7 @@ export type FetchAgentResponseMessage = z.infer<typeof FetchAgentResponseMessage
|
||||
export type FetchAgentTimelineResponseMessage = z.infer<
|
||||
typeof FetchAgentTimelineResponseMessageSchema
|
||||
>;
|
||||
export type AgentForkContextResponseMessage = z.infer<typeof AgentForkContextResponseMessageSchema>;
|
||||
export type CancelAgentResponseMessage = z.infer<typeof CancelAgentResponseMessageSchema>;
|
||||
export type SendAgentMessageResponseMessage = z.infer<typeof SendAgentMessageResponseMessageSchema>;
|
||||
export type SetVoiceModeResponseMessage = z.infer<typeof SetVoiceModeResponseMessageSchema>;
|
||||
@@ -4410,6 +4440,7 @@ export type FetchRecentProviderSessionsRequestMessage = z.infer<
|
||||
>;
|
||||
export type FetchWorkspacesRequestMessage = z.infer<typeof FetchWorkspacesRequestMessageSchema>;
|
||||
export type FetchAgentRequestMessage = z.infer<typeof FetchAgentRequestMessageSchema>;
|
||||
export type AgentForkContextRequestMessage = z.infer<typeof AgentForkContextRequestMessageSchema>;
|
||||
export type SendAgentMessageRequest = z.infer<typeof SendAgentMessageRequestSchema>;
|
||||
export type WaitForFinishRequest = z.infer<typeof WaitForFinishRequestSchema>;
|
||||
export type DictationStreamStartMessage = z.infer<typeof DictationStreamStartMessageSchema>;
|
||||
|
||||
Reference in New Issue
Block a user