import type { AdapterExecutionContext, AdapterExecutionResult } from "../types.js"; import { asString, asNumber, parseObject } from "../utils.js"; import { guardedHttpAdapterFetch } from "./remote-fetch.js"; export async function execute(ctx: AdapterExecutionContext): Promise { const { config, runId, agent, context } = ctx; const url = asString(config.url, ""); if (!url) throw new Error("HTTP adapter missing url"); const method = asString(config.method, "POST"); const timeoutMs = asNumber(config.timeoutMs, 0); const headers = parseObject(config.headers) as Record; const payloadTemplate = parseObject(config.payloadTemplate); const body = { ...payloadTemplate, agentId: agent.id, runId, context, connectionInstructions: context.connectionInstructions ?? null, ...(ctx.runtimeTools ? { paperclipRuntimeTools: ctx.runtimeTools } : {}), }; const controller = new AbortController(); const timer = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null; try { // HTTP adapters have no child-process spawn event. Signal immediately // before starting the remote request so dispatch gates can release without // waiting for the endpoint to respond. ctx.onDispatch?.(); const res = await guardedHttpAdapterFetch(url, { method, headers: { "content-type": "application/json", ...headers, }, body: JSON.stringify(body), ...(timer ? { signal: controller.signal } : {}), }); if (!res.ok) { throw new Error(`HTTP invoke failed with status ${res.status}`); } return { exitCode: 0, signal: null, timedOut: false, summary: `HTTP ${method} ${url}`, }; } catch (err) { if (timer && err instanceof Error && err.name === "AbortError") { return { exitCode: null, signal: null, timedOut: true, errorMessage: `HTTP ${method} ${url} timed out after ${timeoutMs}ms`, errorCode: "timeout", }; } throw err; } finally { if (timer) clearTimeout(timer); } }