From a38111519bbd4b16678ec2912e35376a34dc907f Mon Sep 17 00:00:00 2001 From: HiranoMasaaki Date: Mon, 2 Mar 2026 19:13:00 +0000 Subject: [PATCH 1/3] feat: add model field to startRun and resumeFromStop events Add resolved model name to startRun and resumeFromStop event payloads so wintermute can record the actual model used per run in the DB. Co-Authored-By: Claude Opus 4.6 --- .changeset/add-model-to-start-run-event.md | 6 ++++++ packages/core/src/adapters/event-creators.test.ts | 2 +- packages/core/src/adapters/event-creators.ts | 2 ++ packages/core/src/schemas/runtime.test.ts | 1 + packages/core/src/schemas/runtime.ts | 4 ++++ .../runtime/src/state-machine/states/init.test.ts | 3 +++ packages/runtime/src/state-machine/states/init.ts | 12 ++++++++++++ 7 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/add-model-to-start-run-event.md diff --git a/.changeset/add-model-to-start-run-event.md b/.changeset/add-model-to-start-run-event.md new file mode 100644 index 00000000..9f4fadd3 --- /dev/null +++ b/.changeset/add-model-to-start-run-event.md @@ -0,0 +1,6 @@ +--- +"@perstack/core": patch +"@perstack/runtime": patch +--- + +feat: add model field to startRun and resumeFromStop event payloads diff --git a/packages/core/src/adapters/event-creators.test.ts b/packages/core/src/adapters/event-creators.test.ts index 368e6351..f2e1e84a 100644 --- a/packages/core/src/adapters/event-creators.test.ts +++ b/packages/core/src/adapters/event-creators.test.ts @@ -74,7 +74,7 @@ describe("@perstack/core: event-creators", () => { output: "", }) const initCheckpoint = { ...checkpoint, status: "init" as const, stepNumber: 0 } - const event = createStartRunEvent("job-1", "run-1", "expert-key", initCheckpoint) + const event = createStartRunEvent("job-1", "run-1", "expert-key", initCheckpoint, "claude-sonnet-4-20250514") expect(event.type).toBe("startRun") expect(event.jobId).toBe("job-1") expect(event.runId).toBe("run-1") diff --git a/packages/core/src/adapters/event-creators.ts b/packages/core/src/adapters/event-creators.ts index a8341e55..fcbaf3f9 100644 --- a/packages/core/src/adapters/event-creators.ts +++ b/packages/core/src/adapters/event-creators.ts @@ -49,6 +49,7 @@ export function createStartRunEvent( runId: string, expertKey: string, checkpoint: Checkpoint, + model: string, ): RunEvent { return { type: "startRun", @@ -60,6 +61,7 @@ export function createStartRunEvent( stepNumber: checkpoint.stepNumber, initialCheckpoint: checkpoint, inputMessages: [], + model, } } diff --git a/packages/core/src/schemas/runtime.test.ts b/packages/core/src/schemas/runtime.test.ts index 82963cee..c1926bb6 100644 --- a/packages/core/src/schemas/runtime.test.ts +++ b/packages/core/src/schemas/runtime.test.ts @@ -78,6 +78,7 @@ describe("@perstack/core: createEvent", () => { const event = startRun(mockSetting, mockCheckpoint, { initialCheckpoint: mockCheckpoint, inputMessages: [], + model: "test-model", }) expect(event.type).toBe("startRun") expect(event.jobId).toBe("job-123") diff --git a/packages/core/src/schemas/runtime.ts b/packages/core/src/schemas/runtime.ts index 4009d391..3dfe2a59 100644 --- a/packages/core/src/schemas/runtime.ts +++ b/packages/core/src/schemas/runtime.ts @@ -235,10 +235,14 @@ type ExpertStatePayloads = { startRun: { initialCheckpoint: Checkpoint inputMessages: (InstructionMessage | UserMessage | ToolMessage)[] + /** Resolved model name used for this run */ + model: string } /** Resume from stoppedByDelegate or stoppedByInteractiveTool */ resumeFromStop: { checkpoint: Checkpoint + /** Resolved model name used for this run */ + model: string } /** Proceed to CallingInteractiveTools from ResumingFromStop */ proceedToInteractiveTools: { diff --git a/packages/runtime/src/state-machine/states/init.test.ts b/packages/runtime/src/state-machine/states/init.test.ts index 064e7e5d..f5d35a13 100644 --- a/packages/runtime/src/state-machine/states/init.test.ts +++ b/packages/runtime/src/state-machine/states/init.test.ts @@ -50,6 +50,7 @@ describe("@perstack/runtime: StateMachineLogic['Init']", () => { contents: [{ type: "textPart", id: expect.any(String), text: "How are you?" }], }, ], + model: setting.model, }) }) @@ -208,6 +209,7 @@ describe("@perstack/runtime: StateMachineLogic['Init']", () => { contents: [{ type: "textPart", id: expect.any(String), text: "test-text" }], }, ], + model: setting.model, }) }) @@ -264,6 +266,7 @@ describe("@perstack/runtime: StateMachineLogic['Init']", () => { contents: [{ type: "textPart", id: expect.any(String), text: "resume-text" }], }, ], + model: setting.model, }) }) }) diff --git a/packages/runtime/src/state-machine/states/init.ts b/packages/runtime/src/state-machine/states/init.ts index 8500bd5b..05022436 100644 --- a/packages/runtime/src/state-machine/states/init.ts +++ b/packages/runtime/src/state-machine/states/init.ts @@ -28,12 +28,16 @@ export async function initLogic({ if (!setting.input.text) { throw new Error("Input message is undefined") } + if (!setting.model) { + throw new Error("Model is not resolved") + } return startRun(setting, checkpoint, { initialCheckpoint: checkpoint, inputMessages: [ createInstructionMessage(expert, setting.startedAt), createUserMessage([{ type: "textPart", text: setting.input.text }]), ], + model: setting.model, }) } case "stoppedByDelegate": @@ -41,6 +45,9 @@ export async function initLogic({ if (!setting.input.interactiveToolCallResult) { throw new Error("Interactive tool call result is undefined") } + if (!setting.model) { + throw new Error("Model is not resolved") + } const { toolCallId, toolName, skillName, text } = setting.input.interactiveToolCallResult const pendingToolCalls = checkpoint.pendingToolCalls ?? [] const newToolResult: ToolResult = { @@ -58,15 +65,20 @@ export async function initLogic({ } return resumeFromStop(setting, checkpoint, { checkpoint: updatedCheckpoint, + model: setting.model, }) } default: if (!setting.input.text) { throw new Error("Input message is undefined") } + if (!setting.model) { + throw new Error("Model is not resolved") + } return startRun(setting, checkpoint, { initialCheckpoint: checkpoint, inputMessages: [createUserMessage([{ type: "textPart", text: setting.input.text }])], + model: setting.model, }) } } From a2103ac8a51ef6ed08a99aa71a30973fbdf2d3a0 Mon Sep 17 00:00:00 2001 From: HiranoMasaaki Date: Mon, 2 Mar 2026 19:15:12 +0000 Subject: [PATCH 2/3] fix: format event-creators test Co-Authored-By: Claude Opus 4.6 --- packages/core/src/adapters/event-creators.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/core/src/adapters/event-creators.test.ts b/packages/core/src/adapters/event-creators.test.ts index f2e1e84a..00f40699 100644 --- a/packages/core/src/adapters/event-creators.test.ts +++ b/packages/core/src/adapters/event-creators.test.ts @@ -74,7 +74,13 @@ describe("@perstack/core: event-creators", () => { output: "", }) const initCheckpoint = { ...checkpoint, status: "init" as const, stepNumber: 0 } - const event = createStartRunEvent("job-1", "run-1", "expert-key", initCheckpoint, "claude-sonnet-4-20250514") + const event = createStartRunEvent( + "job-1", + "run-1", + "expert-key", + initCheckpoint, + "claude-sonnet-4-20250514", + ) expect(event.type).toBe("startRun") expect(event.jobId).toBe("job-1") expect(event.runId).toBe("run-1") From f62cd59da68b251eae4a025bbb54b3bb0c54016d Mon Sep 17 00:00:00 2001 From: HiranoMasaaki Date: Tue, 3 Mar 2026 05:05:50 +0000 Subject: [PATCH 3/3] chore: add perstack patch bump to changeset Co-Authored-By: Claude Opus 4.6 --- .changeset/add-model-to-start-run-event.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.changeset/add-model-to-start-run-event.md b/.changeset/add-model-to-start-run-event.md index 9f4fadd3..e07e5e52 100644 --- a/.changeset/add-model-to-start-run-event.md +++ b/.changeset/add-model-to-start-run-event.md @@ -1,6 +1,7 @@ --- "@perstack/core": patch "@perstack/runtime": patch +"perstack": patch --- feat: add model field to startRun and resumeFromStop event payloads