fix(tui): refreshed prewalked subagent model
Tracked active subagent session model changes in progress snapshots so prewalk handoffs replace the starting-model badge. Added regression coverage for a prewalk handoff and documented the fix. Fixes #6083
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
</structure>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
Reference in New Issue
Block a user