refactor(prompt-async-gate): collapse dispatch into mode-based entrypoint
Use one dispatchInternalPrompt surface with mode: async | sync so source, settle, hold, timeout, status checks, reservations, and release semantics stay in one runner. Keep the old helper names temporarily so caller migration can land atomically in follow-up commits. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -38,6 +38,30 @@ type PromptClient<TInput> = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InternalPromptDispatchClient<TInput> = {
|
||||||
|
session?: {
|
||||||
|
status?: () => Promise<unknown>
|
||||||
|
messages?: (input: { path: { id: string }; query: PromptMessagesQuery }) => Promise<unknown>
|
||||||
|
promptAsync?: (input: TInput) => Promise<unknown>
|
||||||
|
prompt?: (input: TInput) => Promise<unknown>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InternalPromptDispatchMode = "async" | "sync"
|
||||||
|
|
||||||
|
export type InternalPromptDispatchArgs<TInput = PromptAsyncInput> = {
|
||||||
|
mode: InternalPromptDispatchMode
|
||||||
|
client: InternalPromptDispatchClient<TInput>
|
||||||
|
sessionID: string
|
||||||
|
input: TInput
|
||||||
|
source: string
|
||||||
|
settleMs?: number
|
||||||
|
postDispatchHoldMs?: number
|
||||||
|
dispatchTimeoutMs?: number
|
||||||
|
checkStatus?: boolean
|
||||||
|
checkToolState?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
type PromptAsyncReservation = {
|
type PromptAsyncReservation = {
|
||||||
source: string
|
source: string
|
||||||
reservedAt: number
|
reservedAt: number
|
||||||
@@ -45,18 +69,20 @@ type PromptAsyncReservation = {
|
|||||||
expiresAt?: number
|
expiresAt?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
declare function setTimeout(callback: () => void, delay?: number): ReturnType<typeof globalThis.setTimeout>
|
declare function setTimeout(callback: () => void, delay?: number): unknown
|
||||||
declare function clearTimeout(timeout: ReturnType<typeof globalThis.setTimeout>): void
|
declare function clearTimeout(timeout: unknown): void
|
||||||
|
|
||||||
let promptGateMessagesFetchTimeoutMsForTesting: number | undefined
|
let promptGateMessagesFetchTimeoutMsForTesting: number | undefined
|
||||||
|
|
||||||
export type PromptAsyncGateResult =
|
export type InternalPromptDispatchResult =
|
||||||
| { status: "dispatched"; response: unknown }
|
| { status: "dispatched"; response: unknown }
|
||||||
| { status: "active" }
|
| { status: "active" }
|
||||||
| { status: "reserved"; reservedBy: string }
|
| { status: "reserved"; reservedBy: string }
|
||||||
| { status: "unavailable" }
|
| { status: "unavailable" }
|
||||||
| { status: "failed"; error: unknown }
|
| { status: "failed"; error: unknown }
|
||||||
|
|
||||||
|
export type PromptAsyncGateResult = InternalPromptDispatchResult
|
||||||
|
|
||||||
type PromptAsyncReservationReleaseOptions = {
|
type PromptAsyncReservationReleaseOptions = {
|
||||||
reservedBy?: string | readonly string[]
|
reservedBy?: string | readonly string[]
|
||||||
reservedByPrefix?: string | readonly string[]
|
reservedByPrefix?: string | readonly string[]
|
||||||
@@ -121,7 +147,7 @@ async function withDispatchTimeout<T>(
|
|||||||
return operation
|
return operation
|
||||||
}
|
}
|
||||||
|
|
||||||
let timeoutID: ReturnType<typeof globalThis.setTimeout> | undefined
|
let timeoutID: unknown
|
||||||
const timeoutPromise = new Promise<never>((_, reject) => {
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
||||||
timeoutID = setTimeout(() => {
|
timeoutID = setTimeout(() => {
|
||||||
reject(new Error(`${operationName} timed out after ${dispatchTimeoutMs}ms`))
|
reject(new Error(`${operationName} timed out after ${dispatchTimeoutMs}ms`))
|
||||||
@@ -260,7 +286,7 @@ async function dispatchAfterSessionIdle<TInput>(args: {
|
|||||||
checkStatus: boolean
|
checkStatus: boolean
|
||||||
checkToolState: boolean
|
checkToolState: boolean
|
||||||
dispatch: (input: TInput) => Promise<unknown>
|
dispatch: (input: TInput) => Promise<unknown>
|
||||||
}): Promise<PromptAsyncGateResult> {
|
}): Promise<InternalPromptDispatchResult> {
|
||||||
const {
|
const {
|
||||||
sessionName,
|
sessionName,
|
||||||
client,
|
client,
|
||||||
@@ -360,6 +386,68 @@ async function dispatchAfterSessionIdle<TInput>(args: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getInternalPromptDispatcher<TInput>(
|
||||||
|
mode: InternalPromptDispatchMode,
|
||||||
|
session: InternalPromptDispatchClient<TInput>["session"],
|
||||||
|
): {
|
||||||
|
sessionName: "promptAsync" | "prompt"
|
||||||
|
dispatch?: (input: TInput) => Promise<unknown>
|
||||||
|
} {
|
||||||
|
if (mode === "async") {
|
||||||
|
if (typeof session?.promptAsync !== "function") {
|
||||||
|
return { sessionName: "promptAsync" }
|
||||||
|
}
|
||||||
|
const dispatchPromptAsync = session.promptAsync.bind(session)
|
||||||
|
return {
|
||||||
|
sessionName: "promptAsync",
|
||||||
|
dispatch: (input) => dispatchPromptAsync(input),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof session?.prompt !== "function") {
|
||||||
|
return { sessionName: "prompt" }
|
||||||
|
}
|
||||||
|
const dispatchPrompt = session.prompt.bind(session)
|
||||||
|
return {
|
||||||
|
sessionName: "prompt",
|
||||||
|
dispatch: (input) => dispatchPrompt(input),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function dispatchInternalPrompt<TInput = PromptAsyncInput>(
|
||||||
|
args: InternalPromptDispatchArgs<TInput>,
|
||||||
|
): Promise<InternalPromptDispatchResult> {
|
||||||
|
const {
|
||||||
|
client,
|
||||||
|
sessionID,
|
||||||
|
input,
|
||||||
|
source,
|
||||||
|
settleMs = DEFAULT_SESSION_IDLE_SETTLE_MS,
|
||||||
|
} = args
|
||||||
|
const postDispatchHoldMs = args.postDispatchHoldMs ?? DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS
|
||||||
|
const dispatchTimeoutMs = args.dispatchTimeoutMs ?? DEFAULT_PROMPT_DISPATCH_TIMEOUT_MS
|
||||||
|
const { sessionName, dispatch } = getInternalPromptDispatcher(args.mode, client.session)
|
||||||
|
|
||||||
|
if (!dispatch) {
|
||||||
|
log(`[prompt-async-gate] ${sessionName} unavailable`, { sessionID, source })
|
||||||
|
return { status: "unavailable" }
|
||||||
|
}
|
||||||
|
|
||||||
|
return dispatchAfterSessionIdle({
|
||||||
|
sessionName,
|
||||||
|
client,
|
||||||
|
sessionID,
|
||||||
|
input,
|
||||||
|
source,
|
||||||
|
settleMs,
|
||||||
|
postDispatchHoldMs,
|
||||||
|
dispatchTimeoutMs,
|
||||||
|
checkStatus: args.checkStatus !== false,
|
||||||
|
checkToolState: args.checkToolState !== false,
|
||||||
|
dispatch,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export async function promptAsyncAfterSessionIdle<TInput = PromptAsyncInput>(args: {
|
export async function promptAsyncAfterSessionIdle<TInput = PromptAsyncInput>(args: {
|
||||||
client: PromptAsyncClient<TInput>
|
client: PromptAsyncClient<TInput>
|
||||||
sessionID: string
|
sessionID: string
|
||||||
@@ -371,35 +459,9 @@ export async function promptAsyncAfterSessionIdle<TInput = PromptAsyncInput>(arg
|
|||||||
checkStatus?: boolean
|
checkStatus?: boolean
|
||||||
checkToolState?: boolean
|
checkToolState?: boolean
|
||||||
}): Promise<PromptAsyncGateResult> {
|
}): Promise<PromptAsyncGateResult> {
|
||||||
const {
|
return dispatchInternalPrompt({
|
||||||
client,
|
...args,
|
||||||
sessionID,
|
mode: "async",
|
||||||
input,
|
|
||||||
source,
|
|
||||||
settleMs = DEFAULT_SESSION_IDLE_SETTLE_MS,
|
|
||||||
} = args
|
|
||||||
const postDispatchHoldMs = args.postDispatchHoldMs ?? DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS
|
|
||||||
const dispatchTimeoutMs = args.dispatchTimeoutMs ?? DEFAULT_PROMPT_DISPATCH_TIMEOUT_MS
|
|
||||||
const session = client.session
|
|
||||||
|
|
||||||
if (typeof session?.promptAsync !== "function") {
|
|
||||||
log("[prompt-async-gate] promptAsync unavailable", { sessionID, source })
|
|
||||||
return { status: "unavailable" }
|
|
||||||
}
|
|
||||||
const dispatchPromptAsync = session.promptAsync.bind(session)
|
|
||||||
|
|
||||||
return dispatchAfterSessionIdle({
|
|
||||||
sessionName: "promptAsync",
|
|
||||||
client,
|
|
||||||
sessionID,
|
|
||||||
input,
|
|
||||||
source,
|
|
||||||
settleMs,
|
|
||||||
postDispatchHoldMs,
|
|
||||||
dispatchTimeoutMs,
|
|
||||||
checkStatus: args.checkStatus !== false,
|
|
||||||
checkToolState: args.checkToolState !== false,
|
|
||||||
dispatch: (dispatchInput) => dispatchPromptAsync(dispatchInput),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,35 +476,9 @@ export async function promptAfterSessionIdle<TInput = PromptAsyncInput>(args: {
|
|||||||
checkStatus?: boolean
|
checkStatus?: boolean
|
||||||
checkToolState?: boolean
|
checkToolState?: boolean
|
||||||
}): Promise<PromptAsyncGateResult> {
|
}): Promise<PromptAsyncGateResult> {
|
||||||
const {
|
return dispatchInternalPrompt({
|
||||||
client,
|
...args,
|
||||||
sessionID,
|
mode: "sync",
|
||||||
input,
|
|
||||||
source,
|
|
||||||
settleMs = DEFAULT_SESSION_IDLE_SETTLE_MS,
|
|
||||||
} = args
|
|
||||||
const postDispatchHoldMs = args.postDispatchHoldMs ?? DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS
|
|
||||||
const dispatchTimeoutMs = args.dispatchTimeoutMs ?? DEFAULT_PROMPT_DISPATCH_TIMEOUT_MS
|
|
||||||
const session = client.session
|
|
||||||
|
|
||||||
if (typeof session?.prompt !== "function") {
|
|
||||||
log("[prompt-async-gate] prompt unavailable", { sessionID, source })
|
|
||||||
return { status: "unavailable" }
|
|
||||||
}
|
|
||||||
const dispatchPrompt = session.prompt.bind(session)
|
|
||||||
|
|
||||||
return dispatchAfterSessionIdle({
|
|
||||||
sessionName: "prompt",
|
|
||||||
client,
|
|
||||||
sessionID,
|
|
||||||
input,
|
|
||||||
source,
|
|
||||||
settleMs,
|
|
||||||
postDispatchHoldMs,
|
|
||||||
dispatchTimeoutMs,
|
|
||||||
checkStatus: args.checkStatus !== false,
|
|
||||||
checkToolState: args.checkToolState !== false,
|
|
||||||
dispatch: (dispatchInput) => dispatchPrompt(dispatchInput),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user