mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Allow failed agent turns to be forked (#2063)
* fix(chat): allow failed turns to be forked Failed turns can end in Paseo-generated timeline items without provider message IDs. Anchor forks to canonical timeline positions while retaining legacy boundaries for compatibility. Place the fork summary before the new prompt and delimit it as structured chat history. * fix(chat): tighten fork boundary handling
This commit is contained in:
@@ -1,12 +1,55 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
AgentForkContextRequestMessageSchema,
|
||||
AgentForkContextResponseMessageSchema,
|
||||
CreateAgentRequestMessageSchema,
|
||||
CreatePaseoWorktreeRequestSchema,
|
||||
SendAgentMessageRequestSchema,
|
||||
} from "./messages.js";
|
||||
|
||||
describe("shared messages attachments", () => {
|
||||
it("preserves an optional timeline cursor on fork-context messages", () => {
|
||||
const boundaryCursor = { epoch: "timeline-1", seq: 42 };
|
||||
const request = AgentForkContextRequestMessageSchema.parse({
|
||||
type: "agent.fork_context.request",
|
||||
agentId: "agent-1",
|
||||
requestId: "fork-1",
|
||||
boundaryCursor,
|
||||
});
|
||||
const response = AgentForkContextResponseMessageSchema.parse({
|
||||
type: "agent.fork_context.response",
|
||||
payload: {
|
||||
requestId: "fork-1",
|
||||
agentId: "agent-1",
|
||||
attachment: null,
|
||||
itemCount: 2,
|
||||
boundaryMessageId: null,
|
||||
boundaryCursor,
|
||||
error: null,
|
||||
},
|
||||
});
|
||||
|
||||
expect(request.boundaryCursor).toEqual(boundaryCursor);
|
||||
expect(response.payload.boundaryCursor).toEqual(boundaryCursor);
|
||||
});
|
||||
|
||||
it("accepts a legacy fork-context response without a timeline cursor", () => {
|
||||
expect(
|
||||
AgentForkContextResponseMessageSchema.parse({
|
||||
type: "agent.fork_context.response",
|
||||
payload: {
|
||||
requestId: "fork-1",
|
||||
agentId: "agent-1",
|
||||
attachment: null,
|
||||
itemCount: 2,
|
||||
boundaryMessageId: "assistant-1",
|
||||
error: null,
|
||||
},
|
||||
}).payload.boundaryCursor,
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps valid review attachments", () => {
|
||||
const parsed = SendAgentMessageRequestSchema.parse({
|
||||
type: "send_agent_message_request",
|
||||
|
||||
@@ -1321,6 +1321,7 @@ export const ProviderSubagentTimelineRequestMessageSchema = z.object({
|
||||
export const AgentForkContextRequestMessageSchema = z.object({
|
||||
type: z.literal("agent.fork_context.request"),
|
||||
agentId: z.string(),
|
||||
boundaryCursor: AgentTimelineCursorSchema.optional(),
|
||||
boundaryMessageId: z.string().optional(),
|
||||
requestId: z.string(),
|
||||
});
|
||||
@@ -2429,6 +2430,8 @@ export const ServerInfoStatusPayloadSchema = z
|
||||
daemonSelfUpdate: z.boolean().optional(),
|
||||
// COMPAT(agentForkContext): added in v0.1.102, remove gate after 2026-12-28.
|
||||
agentForkContext: z.boolean().optional(),
|
||||
// COMPAT(agentForkContextCursor): added in v0.1.108, remove gate after 2027-01-14.
|
||||
agentForkContextCursor: z.boolean().optional(),
|
||||
// COMPAT(providerSubagents): added in v0.1.107, remove gate after 2027-01-12.
|
||||
providerSubagents: z.boolean().optional(),
|
||||
// COMPAT(workspacePinning): added in v0.1.107, remove gate after 2027-01-12.
|
||||
@@ -3129,6 +3132,7 @@ export const AgentForkContextResponseMessageSchema = z.object({
|
||||
attachment: TextAttachmentSchema.nullable(),
|
||||
itemCount: z.number().int().nonnegative(),
|
||||
boundaryMessageId: z.string().nullable(),
|
||||
boundaryCursor: AgentTimelineCursorSchema.nullable().optional(),
|
||||
error: z.string().nullable(),
|
||||
}),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user