fix(call-omo-agent): gate reused sync prompts
This commit is contained in:
@@ -79,11 +79,15 @@ function createToolContext(): ToolContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createContext(promptAsync: ReturnType<typeof mock>) {
|
function createContext(
|
||||||
|
promptAsync: ReturnType<typeof mock>,
|
||||||
|
status?: () => Promise<unknown>,
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
client: {
|
client: {
|
||||||
session: {
|
session: {
|
||||||
promptAsync,
|
promptAsync,
|
||||||
|
...(status ? { status } : {}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -350,6 +354,41 @@ describe("executeSync", () => {
|
|||||||
expect(deps.processMessages).not.toHaveBeenCalled()
|
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 () => {
|
test("commits reserved descendant quota after creating a new sync session", async () => {
|
||||||
//#given
|
//#given
|
||||||
const { executeSync } = require("./sync-executor")
|
const { executeSync } = require("./sync-executor")
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { applySessionPromptParams } from "../../shared/session-prompt-params-hel
|
|||||||
import type { DelegatedModelConfig } from "../../shared/model-resolution-types"
|
import type { DelegatedModelConfig } from "../../shared/model-resolution-types"
|
||||||
import type { FallbackEntry } from "../../shared/model-requirements"
|
import type { FallbackEntry } from "../../shared/model-requirements"
|
||||||
import { stripAgentListSortPrefix } from "../../shared/agent-display-names"
|
import { stripAgentListSortPrefix } from "../../shared/agent-display-names"
|
||||||
|
import { promptAsyncAfterSessionIdle } from "../../hooks/shared/prompt-async-gate"
|
||||||
import { waitForCompletion } from "./completion-poller"
|
import { waitForCompletion } from "./completion-poller"
|
||||||
import { processMessages } from "./message-processor"
|
import { processMessages } from "./message-processor"
|
||||||
import { createOrGetSession } from "./session-creator"
|
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>`
|
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({
|
const promptResult = await promptAsyncAfterSessionIdle({
|
||||||
path: { id: sessionID },
|
client: ctx.client,
|
||||||
body: {
|
sessionID,
|
||||||
agent: normalizedSubagentType,
|
source: "call-omo-agent:sync",
|
||||||
tools: {
|
settleMs: 0,
|
||||||
...getAgentToolRestrictions(normalizedSubagentType),
|
postDispatchHoldMs: 0,
|
||||||
task: false,
|
input: {
|
||||||
question: false,
|
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) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
log(`[call_omo_agent] Prompt error:`, errorMessage)
|
log(`[call_omo_agent] Prompt error:`, errorMessage)
|
||||||
|
|||||||
Reference in New Issue
Block a user