UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

120 lines 4.57 kB
/** * A2A terminal observer. * * v2 dispatch returns an A2A Task immediately. Executors may not also emit the * executor-contract mission.* event stream, so aiwg serve must follow the task * until it reaches a terminal A2A state and then project that state into the * ExecutorRegistry mission model. * * @issue #1374 */ import { A2AClient } from '../a2a/client.js'; import { isTerminalTaskState, } from '../a2a/types.js'; const DEFAULT_POLL_INTERVAL_MS = 1000; const DEFAULT_MAX_POLLS = 300; export async function observeA2ATerminalState(registry, executor, missionId, a2aInstanceId, initialTask, opts = {}) { try { const clientOpts = { baseUrl: executor.transportEndpoints.rest, bearer: executor.token, instanceId: a2aInstanceId, }; if (opts.fetch) clientOpts.fetch = opts.fetch; const client = new A2AClient(clientOpts); let task = initialTask; emitNonTerminalProgress(registry, executor.executorId, missionId, task); for (let attempt = 0; attempt <= (opts.maxPolls ?? DEFAULT_MAX_POLLS); attempt++) { if (isTerminalTaskState(task.status.state)) { emitTerminalTask(registry, executor.executorId, missionId, task); return; } if (attempt === (opts.maxPolls ?? DEFAULT_MAX_POLLS)) break; await sleep(opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS); task = await client.getTask(task.id); emitNonTerminalProgress(registry, executor.executorId, missionId, task); } registry.failMission(missionId, `A2A task ${initialTask.id} did not reach a terminal state before observer timeout`); } catch (err) { registry.failMission(missionId, `A2A terminal observer failed: ${err.message ?? String(err)}`); opts.onError?.(err); } } function emitNonTerminalProgress(registry, executorId, missionId, task) { const state = task.status.state; if (state === 'working') { registry.handleEvent(makeEnvelope('mission.started', executorId, missionId, task, { state: 'running', a2a_task_id: task.id, summary: taskStatusSummary(task), })); return; } if (state === 'input-required') { registry.handleEvent(makeEnvelope('mission.hitl_required', executorId, missionId, task, { state: 'hitl-required', a2a_task_id: task.id, summary: taskStatusSummary(task), })); } } function emitTerminalTask(registry, executorId, missionId, task) { const state = task.status.state; if (state === 'completed') { registry.handleEvent(makeEnvelope('mission.completed', executorId, missionId, task, { state: 'done', a2a_task_id: task.id, summary: taskStatusSummary(task), ...exitCodeData(task), })); return; } if (state === 'canceled') { registry.handleEvent(makeEnvelope('mission.aborted', executorId, missionId, task, { state: 'aborted', a2a_task_id: task.id, summary: taskStatusSummary(task), error: taskStatusSummary(task) ?? `A2A task ${task.id} was canceled`, ...exitCodeData(task), })); return; } registry.handleEvent(makeEnvelope('mission.failed', executorId, missionId, task, { state: 'failed', a2a_task_id: task.id, summary: taskStatusSummary(task), error: taskStatusSummary(task) ?? `A2A task ${task.id} reached terminal state ${state}`, ...exitCodeData(task), })); } function makeEnvelope(event, executorId, missionId, task, data) { return { event, executor_id: executorId, mission_id: missionId, ts: task.status.timestamp ?? new Date().toISOString(), data, }; } function taskStatusSummary(task) { const status = task.status; return typeof status.summary === 'string' ? status.summary : undefined; } function exitCodeData(task) { const status = task.status; return typeof status.exit_code === 'number' ? { exit_code: status.exit_code } : {}; } function sleep(ms) { return new Promise((resolve) => { const timer = setTimeout(resolve, ms); if (typeof timer === 'object' && typeof timer.unref === 'function') { timer.unref(); } }); } export function isA2ATerminalState(state) { return isTerminalTaskState(state); } //# sourceMappingURL=a2a-terminal-observer.js.map