fix(prompt-gate): harden sync and team prompt dispatch

This commit is contained in:
YeonGyu-Kim
2026-05-19 17:43:37 +09:00
parent 1492bffd20
commit bcea4a9d28
44 changed files with 688 additions and 140 deletions
@@ -33,6 +33,7 @@ function createContext(promptAsync: ReturnType<typeof mock>) {
return {
client: {
session: {
prompt: promptAsync,
promptAsync,
},
},
@@ -87,6 +87,7 @@ function createContext(
return {
client: {
session: {
prompt: promptAsync,
promptAsync,
...(status ? { status } : {}),
},
@@ -142,7 +143,7 @@ describe("executeSync", () => {
expect(promptInput?.body.agent).toBe("Sisyphus - Ultraworker")
})
test("#given subagent_type is the lowercase config key 'hephaestus' #when executeSync runs #then promptAsync receives the registered display name 'Hephaestus - Deep Agent'", async () => {
test("#given subagent_type is the lowercase config key 'hephaestus' #when executeSync runs #then prompt receives the registered display name 'Hephaestus - Deep Agent'", async () => {
//#given
const executeSync = await importExecuteSync()
const deps = createDependencies()
@@ -163,7 +164,7 @@ describe("executeSync", () => {
expect(promptInput?.body.agent).toBe("Hephaestus - Deep Agent")
})
test("#given subagent_type is the lowercase config key 'sisyphus-junior' #when executeSync runs #then promptAsync receives the registered display name 'Sisyphus-Junior'", async () => {
test("#given subagent_type is the lowercase config key 'sisyphus-junior' #when executeSync runs #then prompt receives the registered display name 'Sisyphus-Junior'", async () => {
//#given
const executeSync = await importExecuteSync()
const deps = createDependencies()
@@ -184,7 +185,7 @@ describe("executeSync", () => {
expect(promptInput?.body.agent).toBe("Sisyphus-Junior")
})
test("#given subagent_type is already a display name like 'explore' (config key == display name) #when executeSync runs #then promptAsync receives 'explore' unchanged", async () => {
test("#given subagent_type is already a display name like 'explore' (config key == display name) #when executeSync runs #then prompt receives 'explore' unchanged", async () => {
//#given a same-keyed agent must not be double-translated
const executeSync = await importExecuteSync()
const deps = createDependencies()
@@ -536,7 +537,7 @@ describe("executeSync", () => {
//#then
expect(first).toContain("agent response")
expect(second).toContain("promptAsync skipped by gate: reserved")
expect(second).toContain("prompt skipped by gate: reserved")
expect(recorder.promptAsync).toHaveBeenCalledTimes(1)
expect(deps.waitForCompletion).toHaveBeenCalledTimes(1)
expect(deps.processMessages).toHaveBeenCalledTimes(1)
@@ -576,6 +577,7 @@ describe("executeSync", () => {
const ctx = {
client: {
session: {
prompt: mock(async () => ({ data: {} })),
promptAsync: mock(async () => ({ data: {} })),
},
},
+8 -8
View File
@@ -16,12 +16,12 @@ import { processMessages } from "./message-processor"
import { createOrGetSession } from "./session-creator"
import type { CallOmoAgentArgs } from "./types"
type SessionWithPromptAsync = {
promptAsync: (opts: { path: { id: string }; body: Record<string, unknown> }) => Promise<unknown>
type SessionWithPrompt = {
prompt: (opts: { path: { id: string }; body: Record<string, unknown> }) => Promise<unknown>
}
function hasPromptAsync(session: PluginInput["client"]["session"]): session is PluginInput["client"]["session"] & SessionWithPromptAsync {
return "promptAsync" in session && typeof session.promptAsync === "function"
function hasPrompt(session: PluginInput["client"]["session"]): session is PluginInput["client"]["session"] & SessionWithPrompt {
return "prompt" in session && typeof session.prompt === "function"
}
type ExecuteSyncDeps = {
@@ -130,12 +130,12 @@ export async function executeSync(
})
try {
if (!hasPromptAsync(ctx.client.session)) {
return `Error: Failed to send prompt: promptAsync is not available on this OpenCode client.\n\n<task_metadata>\nsession_id: ${sessionID}\n</task_metadata>`
if (!hasPrompt(ctx.client.session)) {
return `Error: Failed to send prompt: prompt is not available on this OpenCode client.\n\n<task_metadata>\nsession_id: ${sessionID}\n</task_metadata>`
}
const promptResult = await dispatchInternalPrompt({
mode: "async",
mode: "sync",
client: ctx.client,
sessionID,
source: "call-omo-agent:sync",
@@ -157,7 +157,7 @@ export async function executeSync(
throw promptResult.error
}
if (!isInternalPromptDispatchAccepted(promptResult)) {
throw new Error(`promptAsync skipped by gate: ${promptResult.status}`)
throw new Error(`prompt skipped by gate: ${promptResult.status}`)
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)