diff --git a/src/tools/call-omo-agent/sync-executor.test.ts b/src/tools/call-omo-agent/sync-executor.test.ts index 3b0e0db5f..58d56a16d 100644 --- a/src/tools/call-omo-agent/sync-executor.test.ts +++ b/src/tools/call-omo-agent/sync-executor.test.ts @@ -1,5 +1,5 @@ import { unsafeTestValue } from "../../../test-support/unsafe-test-value" -const { describe, test, expect, mock } = require("bun:test") +import { describe, test, expect, mock } from "bun:test" type ExecuteSync = typeof import("./sync-executor").executeSync @@ -13,6 +13,7 @@ type PromptAsyncInput = { variant?: string temperature?: number topP?: number + maxOutputTokens?: number options?: Record } } @@ -342,6 +343,64 @@ describe("executeSync", () => { expect(deps.setSessionFallbackChain).toHaveBeenCalledWith("ses-fallback", fallbackChain) }) + test("registers child-session bootstrap and tracked prompt state before sync prompt dispatch", async () => { + //#given + const executeSync = await importExecuteSync() + const { _resetForTesting, getSessionAgent } = require("../../features/claude-code-session-state") + const { clearAllDelegatedChildSessionBootstrap, getDelegatedChildSessionBootstrap } = require("../../shared/delegated-child-session-bootstrap") + const { clearSessionTools, getSessionTools } = require("../../shared/session-tools-store") + const deps = createDependencies({ + createOrGetSession: mock(async () => ({ sessionID: "ses-call-bootstrap", isNew: true })), + }) + const toolContext = createToolContext() + const observed: Array<{ + agent: string | undefined + tools: Record | undefined + bootstrap: ReturnType + }> = [] + const recorder = createPromptAsyncRecorder(async () => { + observed.push({ + agent: getSessionAgent("ses-call-bootstrap"), + tools: getSessionTools("ses-call-bootstrap"), + bootstrap: getDelegatedChildSessionBootstrap("ses-call-bootstrap"), + }) + return { data: {} } + }) + const args = { + subagent_type: "explore", + description: "bootstrap state", + prompt: "collect bootstrap evidence", + run_in_background: false, + } + const fallbackChain = [ + { providers: ["openai"], model: "gpt-5.4", variant: "high" }, + ] + + try { + //#when + await executeSync( + args, + toolContext, + createContext(recorder.promptAsync) as never, + deps, + fallbackChain + ) + + //#then + expect(observed[0]?.agent).toBe("explore") + expect(observed[0]?.tools?.question).toBe(false) + expect(observed[0]?.tools?.task).toBe(false) + expect(observed[0]?.bootstrap?.retryParts[0]?.text).toContain("collect bootstrap evidence") + expect(observed[0]?.bootstrap?.tools?.question).toBe(false) + expect(observed[0]?.bootstrap?.fallbackChain?.[0]?.model).toBe("gpt-5.4") + expect(getDelegatedChildSessionBootstrap("ses-call-bootstrap")).toBeUndefined() + } finally { + clearAllDelegatedChildSessionBootstrap() + clearSessionTools() + _resetForTesting() + } + }) + test("returns dedicated agent-not-found error with task metadata", async () => { //#given const executeSync = await importExecuteSync() diff --git a/src/tools/call-omo-agent/sync-executor.ts b/src/tools/call-omo-agent/sync-executor.ts index dfb47b62b..59a802263 100644 --- a/src/tools/call-omo-agent/sync-executor.ts +++ b/src/tools/call-omo-agent/sync-executor.ts @@ -1,15 +1,20 @@ -import type { CallOmoAgentArgs } from "./types" import type { PluginInput } from "@opencode-ai/plugin" -import { subagentSessions, syncSubagentSessions } from "../../features/claude-code-session-state" -import { getAgentToolRestrictions, log } from "../../shared" -import { applySessionPromptParams } from "../../shared/session-prompt-params-helpers" -import type { DelegatedModelConfig } from "../../shared/model-resolution-types" -import type { FallbackEntry } from "../../shared/model-requirements" -import { getAgentDisplayName, stripAgentListSortPrefix } from "../../shared/agent-display-names" +import { setSessionAgent, subagentSessions, syncSubagentSessions } from "../../features/claude-code-session-state" import { promptAsyncAfterSessionIdle } from "../../hooks/shared/prompt-async-gate" +import { getAgentToolRestrictions, log } from "../../shared" +import { getAgentDisplayName, stripAgentListSortPrefix } from "../../shared/agent-display-names" +import { + clearDelegatedChildSessionBootstrap, + registerDelegatedChildSessionBootstrap, +} from "../../shared/delegated-child-session-bootstrap" +import type { FallbackEntry } from "../../shared/model-requirements" +import type { DelegatedModelConfig } from "../../shared/model-resolution-types" +import { applySessionPromptParams } from "../../shared/session-prompt-params-helpers" +import { deleteSessionTools, setSessionTools } from "../../shared/session-tools-store" import { waitForCompletion } from "./completion-poller" import { processMessages } from "./message-processor" import { createOrGetSession } from "./session-creator" +import type { CallOmoAgentArgs } from "./types" type SessionWithPromptAsync = { promptAsync: (opts: { path: { id: string }; body: Record }) => Promise @@ -58,6 +63,14 @@ function buildPromptGenerationParams(model: DelegatedModelConfig | undefined): R } } +function buildSyncPromptTools(agent: string): Record { + return { + ...getAgentToolRestrictions(agent), + task: false, + question: false, + } +} + export async function executeSync( args: CallOmoAgentArgs, toolContext: { @@ -105,6 +118,16 @@ export async function executeSync( log(`[call_omo_agent] Sending prompt to session ${sessionID}`) log(`[call_omo_agent] Prompt text:`, args.prompt.substring(0, 100)) const normalizedSubagentType = stripAgentListSortPrefix(args.subagent_type) + const promptAgent = getAgentDisplayName(normalizedSubagentType) + const promptTools = buildSyncPromptTools(normalizedSubagentType) + setSessionAgent(sessionID, promptAgent) + setSessionTools(sessionID, promptTools) + registerDelegatedChildSessionBootstrap({ + sessionID, + promptText: args.prompt, + fallbackChain, + tools: promptTools, + }) try { if (!hasPromptAsync(ctx.client.session)) { @@ -119,12 +142,8 @@ export async function executeSync( input: { path: { id: sessionID }, body: { - agent: getAgentDisplayName(normalizedSubagentType), - tools: { - ...getAgentToolRestrictions(normalizedSubagentType), - task: false, - question: false, - }, + agent: promptAgent, + tools: promptTools, parts: [{ type: "text", text: args.prompt }], ...(model ? { model: { providerID: model.providerID, modelID: model.modelID } } : {}), ...(model?.variant ? { variant: model.variant } : {}), @@ -160,9 +179,14 @@ export async function executeSync( deps.clearSessionFallbackChain(sessionID) } + if (sessionID) { + clearDelegatedChildSessionBootstrap(sessionID) + } + if (sessionID && createdSessionForExecution) { subagentSessions.delete(sessionID) syncSubagentSessions.delete(sessionID) + deleteSessionTools(sessionID) } } }