type FrontendActionArgs = Record & { __frontend_action_call_id?: string; reason?: string; }; type FrontendActionBridgeResult = { version: "frontend-action-result@1"; status: "succeeded" | "failed" | "rejected" | "cancelled" | "expired"; output?: unknown; error?: { code?: string; message?: string }; }; const internalBaseUrl = process.env.TJWATER_AGENT_INTERNAL_BASE_URL ?? "http://127.0.0.1:8787"; const internalToken = process.env.TJWATER_AGENT_INTERNAL_TOKEN ?? ""; export const executeFrontendAction = async ( name: "render_scada_analysis" | "clear_scada_analysis", args: FrontendActionArgs, context: { sessionID: string }, ) => { const callId = args.__frontend_action_call_id; if (!callId) { throw new Error("frontend action bridge did not inject the OpenCode call ID"); } const { __frontend_action_call_id: _callId, reason, ...params } = args; let response: Response; try { response = await fetch(`${internalBaseUrl}/internal/frontend-actions/request`, { method: "POST", headers: { "Content-Type": "application/json", "x-agent-internal-token": internalToken, }, body: JSON.stringify({ session_id: context.sessionID, call_id: callId, name, params, fallback_text: reason, }), }); } catch (error) { throw new Error( `frontend action bridge unavailable: ${error instanceof Error ? error.message : String(error)}`, ); } const text = await response.text(); if (!response.ok) { throw new Error( `frontend action bridge rejected request (${response.status}): ${text}`, ); } return unwrapFrontendActionResult(text); }; export const unwrapFrontendActionResult = (text: string) => { let result: FrontendActionBridgeResult; try { result = JSON.parse(text) as FrontendActionBridgeResult; } catch { throw new Error("frontend action bridge returned invalid JSON"); } if (result.version !== "frontend-action-result@1" || typeof result.status !== "string") { throw new Error("frontend action bridge returned an invalid result"); } if (result.status !== "succeeded") { const code = result.error?.code?.trim() || `ACTION_${result.status.toUpperCase()}`; const message = result.error?.message?.trim() || `browser action ${result.status}`; throw new Error(`${code}: ${message}`); } if (result.output === undefined) { throw new Error("frontend action bridge returned no output"); } return JSON.stringify(result.output); };