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..e07e5e52 --- /dev/null +++ b/.changeset/add-model-to-start-run-event.md @@ -0,0 +1,7 @@ +--- +"@perstack/core": patch +"@perstack/runtime": patch +"perstack": 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..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) + 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, }) } }