UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

185 lines (184 loc) 7.87 kB
import { t as createAbortError } from "./abort-signal-D2k14JsD.js"; import { D as tryResolveLegacyCompatibilityAgentId } from "./agent-scope-config-DcbEhP0R.js"; import { O as parseAgentSessionKey } from "./session-key-BnWWjqNc.js"; import "./legacy.default-agent-owner-BGwEdQRe.js"; import { y as describeAgentsWaitTool } from "./tool-description-presets-1n9mie5B.js"; import { n as resolvePersistedSessionStoreOwnerForKey } from "./session-store-owner-CR2Ag3xK.js"; import { n as ToolInputError } from "./tool-input-error-mjW74R8m.js"; import { t as jsonResult } from "./tool-results-BCM3fdVS.js"; import "./common-Bm6UTDDA.js"; import { a as markCollectorReaderTool } from "./swarm-collector-capability-CNerMvSC.js"; import { x as onSubagentRegistryPersisted } from "./subagent-registry-read-DeFf_9Nz.js"; import { t as resolveSubagentCompletionResultText } from "./subagent-completion-result-D8RxJ29O.js"; import { u as getSubagentRunsByRunIds } from "./subagent-registry-BMJDw92y.js"; import { t as resolveSwarmConfig } from "./swarm-config-XTe8M2h4.js"; import { Type } from "typebox"; //#region src/agents/tools/agents-wait-tool.ts const MAX_WAIT_IDS = 1e3; const AgentsWaitToolSchema = Type.Object({ ids: Type.Array(Type.String({ minLength: 1 }), { minItems: 1, maxItems: MAX_WAIT_IDS }), timeoutSeconds: Type.Optional(Type.Number({ minimum: 0 })) }); function ownsRun(entry, currentSessionKeys, currentAgentId, config) { const owner = entry.swarmRequesterSessionKey?.trim(); if (!owner) return false; return (entry.swarmWaitOwnerSessionKeys && entry.swarmWaitOwnerSessionKeys.length > 0 ? entry.swarmWaitOwnerSessionKeys : [owner]).some((sessionKey) => { if (!currentSessionKeys.has(sessionKey)) return false; const ownerAgentId = parseAgentSessionKey(sessionKey)?.agentId ?? entry.requesterAgentId ?? paramsOwner(config, sessionKey); return Boolean(ownerAgentId && (!currentAgentId || ownerAgentId === currentAgentId)); }); } function paramsOwner(config, sessionKey) { if (!config) return; const persisted = resolvePersistedSessionStoreOwnerForKey(config, sessionKey); return persisted.kind === "configured" ? persisted.agentId : persisted.kind === "none" ? tryResolveLegacyCompatibilityAgentId(config) : void 0; } function completionResult(entry) { const completion = entry.collectorCompletion; if (!completion) return; return { runId: entry.swarmRunId ?? entry.runId, status: completion.status, result: resolveSubagentCompletionResultText(entry) ?? "", ...completion.structured !== void 0 ? { structured: completion.structured } : {}, ...entry.execution.outcome?.status === "error" ? { error: entry.execution.outcome.error } : {}, ...completion.schemaError ? { schemaError: completion.schemaError } : {}, sessionKey: entry.childSessionKey, ...entry.label ? { label: entry.label } : {}, ...completion.usage ? { usage: completion.usage } : {} }; } /** Park one host bridge until its collector completes; registry writes wake it without polling. */ async function waitForCollectorCompletion(params) { const readCompletion = () => { const state = readWaitState([params.runId], params.currentSessionKeys, params.currentAgentId, params.config); const error = state.errors?.[0]; if (error) throw new ToolInputError(`agents.run ${error.error}: ${error.runId}`); return state.completed[0]; }; const immediate = readCompletion(); if (immediate) return immediate; if (params.signal?.aborted) throw new ToolInputError("agents.run wait aborted."); return await new Promise((resolve, reject) => { let settled = false; const finish = (result) => { if (settled) return; settled = true; unsubscribe(); params.signal?.removeEventListener("abort", onAbort); if (result instanceof Error) reject(result); else resolve(result); }; const check = () => { try { const completion = readCompletion(); if (completion) finish(completion); } catch (error) { finish(error instanceof Error ? error : new Error(String(error))); } }; const onAbort = () => finish(new ToolInputError("agents.run wait aborted.")); const unsubscribe = onSubagentRegistryPersisted(check); params.signal?.addEventListener("abort", onAbort, { once: true }); if (params.signal?.aborted) onAbort(); else check(); }); } function readWaitState(ids, currentSessionKeys, currentAgentId, config) { const errors = []; const completed = []; const pending = []; const snapshot = getSubagentRunsByRunIds(ids); for (const [inputIndex, runId] of ids.entries()) { const entry = snapshot.entries.get(runId); if (!entry?.collect) { errors.push({ runId, error: "not_found" }); continue; } if (!ownsRun(entry, currentSessionKeys, currentAgentId, config)) { errors.push({ runId, error: "not_owner" }); continue; } const result = completionResult(entry); if (result) completed.push({ result, completedAt: entry.completion?.capturedAt ?? entry.execution.endedAt ?? Number.MAX_SAFE_INTEGER, inputIndex }); else pending.push(runId); } completed.sort((left, right) => left.completedAt - right.completedAt || left.inputIndex - right.inputIndex); return { completed: completed.map((entry) => entry.result), pending, ...errors.length > 0 ? { errors } : {} }; } async function waitForCollector(params) { const deadline = performance.now() + params.timeoutMs; for (;;) { if (params.signal?.aborted) throw createAbortError("agents_wait aborted."); const state = readWaitState(params.ids, params.currentSessionKeys, params.currentAgentId, params.config); if (state.completed.length > 0 || state.pending.length === 0 || performance.now() >= deadline) return state; await new Promise((resolve, reject) => { const finish = (error) => { clearTimeout(timer); unsubscribe(); params.signal?.removeEventListener("abort", onAbort); if (error) { reject(error); return; } resolve(); }; const onAbort = () => finish(createAbortError("agents_wait aborted.")); const timer = setTimeout(finish, Math.min(500, Math.max(0, deadline - performance.now()))); const unsubscribe = onSubagentRegistryPersisted(() => finish()); params.signal?.addEventListener("abort", onAbort, { once: true }); if (params.signal?.aborted) onAbort(); }); } } function createAgentsWaitTool(opts) { const swarm = resolveSwarmConfig(opts.config, opts.agentId); return markCollectorReaderTool({ label: "Wait for Agents", name: "agents_wait", displaySummary: "Wait for collector children.", description: describeAgentsWaitTool(false), parameters: AgentsWaitToolSchema, execute: async (_toolCallId, args, signal) => { const params = args; if (params.ids.length > MAX_WAIT_IDS) throw new ToolInputError(`agents_wait supports at most ${MAX_WAIT_IDS} ids.`); const ids = [...new Set(params.ids.map((id) => id.trim()).filter(Boolean))]; if (ids.length === 0) throw new ToolInputError("agents_wait requires at least one non-empty run id."); const currentSessionKeys = new Set([opts.runSessionKey, opts.agentSessionKey].filter((key) => Boolean(key?.trim()))); const requestedTimeout = typeof params.timeoutSeconds === "number" && Number.isFinite(params.timeoutSeconds) ? params.timeoutSeconds : 30; const timeoutSeconds = Math.min(Math.max(0, requestedTimeout), swarm.waitTimeoutSecondsMax); const result = await waitForCollector({ ids, currentSessionKeys, currentAgentId: opts.agentId, config: opts.config, timeoutMs: timeoutSeconds * 1e3, signal }); const noAuthorizedTargets = result.completed.length === 0 && result.pending.length === 0 && Boolean(result.errors?.length); return jsonResult(noAuthorizedTargets ? { ...result, success: false } : result); } }); } //#endregion export { waitForCollectorCompletion as n, createAgentsWaitTool as t };