diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 6f32dc885..5a4bcc0ef 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed prewalked subagents continuing to display their starting model after switching to the target model. ([#6083](https://github.com/can1357/oh-my-pi/issues/6083)) + ## [17.0.5] - 2026-07-18 ### Added diff --git a/packages/coding-agent/src/prompts/system/workflow-notice.md b/packages/coding-agent/src/prompts/system/workflow-notice.md index d15ad9127..09a849bef 100644 --- a/packages/coding-agent/src/prompts/system/workflow-notice.md +++ b/packages/coding-agent/src/prompts/system/workflow-notice.md @@ -47,7 +47,7 @@ For independent per-item chains (review → verify, fetch → extract → score) schema: FINDINGS_SCHEMA, }); return await parallel(found.findings.map((f) => async () => ({ - ...f, + …f, verdict: await agent( `Refute if you can (default refuted when unsure): ${f.title}`, { label: `verify:${f.file}`, schema: VERDICT_SCHEMA }, @@ -57,8 +57,6 @@ For independent per-item chains (review → verify, fetch → extract → score) phase("Review"); const results = await parallel(DIMENSIONS.map((d) => async () => reviewAndVerify(d))); const confirmed = results.flat().filter((f) => f.verdict.is_real); - - Reach for `pipeline()` only when a stage genuinely needs ALL of the previous stage first — dedup/merge across the whole set, early-exit on zero, or "compare against the other findings" — because its inter-stage barrier makes every item wait for the slowest peer: **Python (`eval`, Python backend):** @@ -80,8 +78,6 @@ Reach for `pipeline()` only when a stage genuinely needs ALL of the previous sta const verdicts = await parallel(findings.map((f) => async () => await agent(verifyPrompt(f), { schema: VERDICT_SCHEMA }), )); - - Use ordinary code between calls to flatten/map/filter; don't add a barrier just for that. Nested `parallel()` pools each cap independently, so keep total fan-out sane. diff --git a/packages/coding-agent/src/task/executor.ts b/packages/coding-agent/src/task/executor.ts index c5cd02694..1828d3750 100644 --- a/packages/coding-agent/src/task/executor.ts +++ b/packages/coding-agent/src/task/executor.ts @@ -1477,9 +1477,16 @@ function createSubagentRunMonitor(args: RunMonitorArgs): SubagentRunMonitor { scheduleProgress(flushProgress); }; - const attach = (session: AgentSession): (() => void) => - session.subscribe(event => { + const attach = (session: AgentSession): (() => void) => { + let activeModel = session.model ? formatModelStringWithRouting(session.model) : undefined; + return session.subscribe(event => { emitSubagentEvent(event); + const nextModel = session.model ? formatModelStringWithRouting(session.model) : undefined; + if (nextModel && nextModel !== activeModel) { + activeModel = nextModel; + progress.resolvedModel = nextModel; + scheduleProgress(true); + } if (event.type === "auto_retry_start") { progress.retryState = { attempt: event.attempt, @@ -1530,6 +1537,7 @@ function createSubagentRunMonitor(args: RunMonitorArgs): SubagentRunMonitor { return; } }); + }; const captureSalvage = (session: AgentSession): void => { // Best-effort salvage: capture the last assistant text so diff --git a/packages/coding-agent/test/task/executor-prewalk.test.ts b/packages/coding-agent/test/task/executor-prewalk.test.ts index b2ef997d9..dfdf8fb83 100644 --- a/packages/coding-agent/test/task/executor-prewalk.test.ts +++ b/packages/coding-agent/test/task/executor-prewalk.test.ts @@ -24,13 +24,16 @@ import type { AgentDefinition, SingleResult } from "@oh-my-pi/pi-coding-agent/ta import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools"; import { EventBus } from "@oh-my-pi/pi-coding-agent/utils/event-bus"; -function yieldEmittingSession(initialTools: string[] = ["read", "yield"]): AgentSession { +function yieldEmittingSession( + initialTools: string[] = ["read", "yield"], + modelSwitch?: { from: Model; to: Model }, +): AgentSession { const listeners: Array<(event: AgentSessionEvent) => void> = []; let activeTools = initialTools; const session = { state: { messages: [] }, agent: { state: { systemPrompt: ["test"] } }, - model: undefined, + model: modelSwitch?.from, extensionRunner: undefined, sessionManager: { appendSessionInit: () => {} }, getActiveToolNames: () => activeTools, @@ -47,6 +50,12 @@ function yieldEmittingSession(initialTools: string[] = ["read", "yield"]): Agent }; }, prompt: async (_text: string, _options?: PromptOptions) => { + if (modelSwitch) { + session.model = modelSwitch.to; + for (const listener of listeners) { + listener({ type: "notice", level: "info", message: "Prewalk switched", source: "prewalk" }); + } + } for (const listener of listeners) { listener({ type: "tool_execution_end", @@ -140,6 +149,28 @@ describe("runSubprocess per-agent prewalk", () => { expect(forwarded?.prewalk?.target.provider).toBe(target.provider); }); + it("reports the prewalk target as the active model after handoff", async () => { + const progressModels: string[] = []; + vi.spyOn(sdkModule, "createAgentSession").mockResolvedValue( + createSessionResult(yieldEmittingSession(["read", "yield"], { from: primary, to: target })), + ); + + const result = await runSubprocess({ + ...baseOptions("subagent-prewalk-progress-model", Settings.isolated()), + agent: { + ...baseAgent, + model: [`${primary.provider}/${primary.id}`], + prewalk: `${target.provider}/${target.id}`, + }, + onProgress: progress => { + if (progress.resolvedModel) progressModels.push(progress.resolvedModel); + }, + }); + + expect(result.exitCode).toBe(0); + expect(progressModels.at(-1)).toBe(`${target.provider}/${target.id}`); + }); + it("resolves prewalk: true through the smol role default target", async () => { const settings = Settings.isolated(); settings.setModelRole("smol", `${target.provider}/${target.id}`);