fix(agent): keep session scout notices live

This commit is contained in:
can1357
2026-08-02 20:48:49 +02:00
parent 3de8c3a476
commit 160bdd05c3
7 changed files with 37 additions and 12 deletions
+1 -1
View File
@@ -40,7 +40,7 @@
- Fixed the vibe pre-init-kill test racing `registry.kill()` against the worker's job-body dispatch (the mock only registered its AgentRef after the abort signal landed, so a loaded runner could observe no registration); the test now waits for the worker to be mid-initialization before killing.
### Fixed
- Removed hard-coded `scout` references from system and tool prompts that leaked into the model even when the scout agent was disabled (`task.disabledAgents`) or absent from the spawn list: the task tool description, delegation gates, plan-mode and workflowz notices, and the glob/grep/ast-grep guidance now only mention scout when it is actually spawnable ([#7313](https://github.com/can1357/oh-my-pi/issues/7313)).
- Removed hard-coded `scout` references from agent, system, and tool prompts that leaked into the model even when the scout agent was disabled (`task.disabledAgents`) or absent from the spawn list: the init agent prompt, task tool description, delegation gates, plan-mode and workflowz notices, and glob/grep/ast-grep guidance no longer recommend an unavailable agent, including after live settings changes ([#7313](https://github.com/can1357/oh-my-pi/issues/7313)).
## [17.2.4] - 2026-08-01
@@ -4,7 +4,7 @@ description: Generate AGENTS.md for current codebase
thinking-level: medium
---
Generate AGENTS.md by launching multiple `scout` agents in parallel (via `task` tool) scanning different areas (core src, tests, configs/build, scripts/docs), then synthesize findings into a single file.
Generate AGENTS.md by launching multiple research agents in parallel (via `task` tool) to scan different areas (core src, tests, configs/build, scripts/docs), then synthesize findings into a single file.
<structure>
- **Project Overview**: Brief description of project purpose
+1 -4
View File
@@ -3347,10 +3347,7 @@ async function createAgentSessionScoped(options: CreateAgentSessionOptions): Pro
initialAdvisorCosts,
settings,
autoApprove: options.autoApprove,
scoutAvailable: isScoutSpawnable(
settings.get("task.disabledAgents") as string[] | undefined,
options.spawns ?? "*",
),
scoutAllowedBySpawnPolicy: isScoutSpawnable(undefined, options.spawns ?? "*"),
evalKernelOwnerId,
// Defined only for top-level sessions (creation is gated above).
// AgentSession uses this to decide whether it may dispose the global
@@ -109,8 +109,8 @@ export interface AgentSessionConfig {
agent: Agent;
sessionManager: SessionManager;
settings: Settings;
/** Whether the read-only `scout` subagent is spawnable (not disabled, allowed by spawn policy). Defaults to true. */
scoutAvailable?: boolean;
/** Whether the session spawn policy permits the read-only `scout` subagent. Defaults to true. */
scoutAllowedBySpawnPolicy?: boolean;
/** Whether the caller explicitly requested yolo/auto-approve behavior for this session. */
autoApprove?: boolean;
/** Models to cycle through with Ctrl+P (from --models flag). */
@@ -518,7 +518,7 @@ export class AgentSession {
// Agent identity (registry id) used for IRC routing and job ownership.
#agentId: string | undefined;
#agentKind: "main" | "sub" = "main";
#scoutAvailable = true;
#scoutAllowedBySpawnPolicy = true;
#providerSessionId: string | undefined;
#freshProviderSessionId: string | undefined;
#inheritedProviderPromptCacheKey: string | undefined;
@@ -1238,7 +1238,7 @@ export class AgentSession {
this.#loopGuards = new LoopGuards(streamGuardsHost);
this.#agentId = config.agentId;
this.#agentKind = config.agentKind ?? "main";
this.#scoutAvailable = config.scoutAvailable ?? true;
this.#scoutAllowedBySpawnPolicy = config.scoutAllowedBySpawnPolicy ?? true;
this.#providerSessionId = config.providerSessionId;
this.#inheritedProviderPromptCacheKey =
config.providerPromptCacheKeySource === "fork" ? this.agent.promptCacheKey : undefined;
@@ -4637,6 +4637,11 @@ export class AgentSession {
};
}
#isScoutAvailable(): boolean {
const disabledAgents = this.settings.get("task.disabledAgents") as string[] | undefined;
return this.#scoutAllowedBySpawnPolicy && !disabledAgents?.includes("scout");
}
async #buildPlanModeMessage(): Promise<CustomMessage | null> {
const state = this.#planModeState;
if (!state?.enabled) return null;
@@ -4660,7 +4665,7 @@ export class AgentSession {
isHashlineEditMode: this.#resolveActiveEditMode() === "hashline",
reentry: state.reentry ?? false,
iterative: state.workflow === "iterative",
scoutAvailable: this.#scoutAvailable,
scoutAvailable: this.#isScoutAvailable(),
});
return {
@@ -4794,7 +4799,7 @@ export class AgentSession {
customType: "workflow-notice",
content: renderWorkflowNotice({
taskBatch: this.settings.get("task.batch"),
scoutAvailable: this.#scoutAvailable,
scoutAvailable: this.#isScoutAvailable(),
}),
display: false,
attribution: "user",
@@ -144,6 +144,21 @@ describe("AgentSession magic keyword settings", () => {
expect(notice?.content).toContain("**JavaScript (`eval`, JavaScript backend):**");
});
it("updates the workflowz notice when scout is disabled during the session", async () => {
const created = await createMagicKeywordSession(root);
session = created.session;
authStorage = created.authStorage;
created.settings.set("task.disabledAgents", ["scout"]);
const promptSpy = vi.spyOn(session.agent, "prompt").mockResolvedValue(undefined);
await session.prompt("please workflowz this");
const promptMessages = promptSpy.mock.calls[0]![0] as unknown as Array<{ content?: string; customType?: string }>;
const notice = promptMessages.find(message => message.customType === "workflow-notice")?.content ?? "";
expect(notice.toLowerCase()).not.toContain("scout");
expect(notice).toContain("Explore inline FIRST");
});
it("skips workflowz notice when the task tool is inactive", async () => {
const created = await createMagicKeywordSession(root, []);
session = created.session;
@@ -1,5 +1,6 @@
import { afterEach, describe, expect, it, vi } from "bun:test";
import { Settings } from "../../src/config/settings";
import initAgentPrompt from "../../src/prompts/agents/init.md" with { type: "text" };
import * as taskDiscovery from "../../src/task/discovery";
import { TaskTool } from "../../src/task/index";
import { isScoutSpawnable } from "../../src/task/spawn-policy";
@@ -130,3 +131,10 @@ describe("task tool description scout gating", () => {
expect(description).toContain("### reviewer");
});
});
describe("bundled agent prompt scout gating", () => {
it("does not hard-code scout in the init agent prompt", () => {
expect(initAgentPrompt.toLowerCase()).not.toContain("scout");
expect(initAgentPrompt).toContain("multiple research agents");
});
});