fix(call-omo-agent): gate reused sync prompts

This commit is contained in:
YeonGyu-Kim
2026-05-15 11:49:58 +09:00
parent 439e72839b
commit 0b48f80569
2 changed files with 66 additions and 13 deletions
+40 -1
View File
@@ -79,11 +79,15 @@ function createToolContext(): ToolContext {
}
}
function createContext(promptAsync: ReturnType<typeof mock>) {
function createContext(
promptAsync: ReturnType<typeof mock>,
status?: () => Promise<unknown>,
) {
return {
client: {
session: {
promptAsync,
...(status ? { status } : {}),
},
},
}
@@ -350,6 +354,41 @@ describe("executeSync", () => {
expect(deps.processMessages).not.toHaveBeenCalled()
})
test("does not send a duplicate sync prompt when a reused session is active", async () => {
//#given
const executeSync = await importExecuteSync()
const deps = createDependencies({
createOrGetSession: mock(async () => ({ sessionID: "ses-active-reuse", isNew: false })),
})
const toolContext = createToolContext()
const recorder = createPromptAsyncRecorder()
const args = {
subagent_type: "explore",
description: "active reuse",
prompt: "find something",
run_in_background: false,
session_id: "ses-active-reuse",
}
//#when
const result = await executeSync(
args,
toolContext,
createContext(
recorder.promptAsync,
async () => ({ data: { "ses-active-reuse": { type: "busy" } } }),
) as never,
deps,
)
//#then
expect(recorder.promptAsync).toHaveBeenCalledTimes(0)
expect(result).toContain("Error: Failed to send prompt")
expect(result).toContain("session_id: ses-active-reuse")
expect(deps.waitForCompletion).not.toHaveBeenCalled()
expect(deps.processMessages).not.toHaveBeenCalled()
})
test("commits reserved descendant quota after creating a new sync session", async () => {
//#given
const { executeSync } = require("./sync-executor")
+26 -12
View File
@@ -6,6 +6,7 @@ import { applySessionPromptParams } from "../../shared/session-prompt-params-hel
import type { DelegatedModelConfig } from "../../shared/model-resolution-types"
import type { FallbackEntry } from "../../shared/model-requirements"
import { stripAgentListSortPrefix } from "../../shared/agent-display-names"
import { promptAsyncAfterSessionIdle } from "../../hooks/shared/prompt-async-gate"
import { waitForCompletion } from "./completion-poller"
import { processMessages } from "./message-processor"
import { createOrGetSession } from "./session-creator"
@@ -110,21 +111,34 @@ export async function executeSync(
return `Error: Failed to send prompt: promptAsync is not available on this OpenCode client.\n\n<task_metadata>\nsession_id: ${sessionID}\n</task_metadata>`
}
await ctx.client.session.promptAsync({
path: { id: sessionID },
body: {
agent: normalizedSubagentType,
tools: {
...getAgentToolRestrictions(normalizedSubagentType),
task: false,
question: false,
const promptResult = await promptAsyncAfterSessionIdle({
client: ctx.client,
sessionID,
source: "call-omo-agent:sync",
settleMs: 0,
postDispatchHoldMs: 0,
input: {
path: { id: sessionID },
body: {
agent: normalizedSubagentType,
tools: {
...getAgentToolRestrictions(normalizedSubagentType),
task: false,
question: false,
},
parts: [{ type: "text", text: args.prompt }],
...(model ? { model: { providerID: model.providerID, modelID: model.modelID } } : {}),
...(model?.variant ? { variant: model.variant } : {}),
...buildPromptGenerationParams(model),
},
parts: [{ type: "text", text: args.prompt }],
...(model ? { model: { providerID: model.providerID, modelID: model.modelID } } : {}),
...(model?.variant ? { variant: model.variant } : {}),
...buildPromptGenerationParams(model),
},
})
if (promptResult.status === "failed") {
throw promptResult.error
}
if (promptResult.status !== "dispatched") {
throw new Error(`promptAsync skipped by gate: ${promptResult.status}`)
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
log(`[call_omo_agent] Prompt error:`, errorMessage)