From 160bdd05c3bdeed5f4aa4ea5262df7e6c45c9ad6 Mon Sep 17 00:00:00 2001 From: can1357 Date: Sun, 2 Aug 2026 20:48:49 +0200 Subject: [PATCH] fix(agent): keep session scout notices live --- packages/coding-agent/CHANGELOG.md | 2 +- packages/coding-agent/src/prompts/agents/init.md | 2 +- packages/coding-agent/src/sdk.ts | 5 +---- .../src/session/agent-session-types.ts | 4 ++-- .../coding-agent/src/session/agent-session.ts | 13 +++++++++---- .../test/agent-session-magic-keywords.test.ts | 15 +++++++++++++++ .../coding-agent/test/task/spawn-policy.test.ts | 8 ++++++++ 7 files changed, 37 insertions(+), 12 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 0e1a2201c..dada3a577 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -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 diff --git a/packages/coding-agent/src/prompts/agents/init.md b/packages/coding-agent/src/prompts/agents/init.md index 1e8fd85b7..2f11b4a60 100644 --- a/packages/coding-agent/src/prompts/agents/init.md +++ b/packages/coding-agent/src/prompts/agents/init.md @@ -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. - **Project Overview**: Brief description of project purpose diff --git a/packages/coding-agent/src/sdk.ts b/packages/coding-agent/src/sdk.ts index bb7050f95..a9d7270a0 100644 --- a/packages/coding-agent/src/sdk.ts +++ b/packages/coding-agent/src/sdk.ts @@ -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 diff --git a/packages/coding-agent/src/session/agent-session-types.ts b/packages/coding-agent/src/session/agent-session-types.ts index fc1c4c85a..544fb0319 100644 --- a/packages/coding-agent/src/session/agent-session-types.ts +++ b/packages/coding-agent/src/session/agent-session-types.ts @@ -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). */ diff --git a/packages/coding-agent/src/session/agent-session.ts b/packages/coding-agent/src/session/agent-session.ts index 7ff7449be..aaf29a14e 100644 --- a/packages/coding-agent/src/session/agent-session.ts +++ b/packages/coding-agent/src/session/agent-session.ts @@ -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 { 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", diff --git a/packages/coding-agent/test/agent-session-magic-keywords.test.ts b/packages/coding-agent/test/agent-session-magic-keywords.test.ts index fda5cbfa2..e1b8a50a7 100644 --- a/packages/coding-agent/test/agent-session-magic-keywords.test.ts +++ b/packages/coding-agent/test/agent-session-magic-keywords.test.ts @@ -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; diff --git a/packages/coding-agent/test/task/spawn-policy.test.ts b/packages/coding-agent/test/task/spawn-policy.test.ts index f963733bc..17c2cd01b 100644 --- a/packages/coding-agent/test/task/spawn-policy.test.ts +++ b/packages/coding-agent/test/task/spawn-policy.test.ts @@ -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"); + }); +});