import type { ExecApprovalDecision, PendingExecApproval } from "@/features/agents/approvals/types"; import { parseExecApprovalRequested, parseExecApprovalResolved, resolveExecApprovalAgentId, } from "@/features/agents/approvals/execApprovalEvents"; import type { AgentState } from "@/features/agents/state/store"; import type { EventFrame } from "@/lib/gateway/GatewayClient"; import { GatewayResponseError } from "@/lib/gateway/errors"; export type ExecApprovalEventEffects = { scopedUpserts: Array<{ agentId: string; approval: PendingExecApproval }>; unscopedUpserts: PendingExecApproval[]; removals: string[]; markActivityAgentIds: string[]; }; export type ExecApprovalFollowUpIntent = { shouldSend: boolean; agentId: string | null; sessionKey: string | null; message: string | null; }; const EMPTY_EVENT_EFFECTS: ExecApprovalEventEffects = { scopedUpserts: [], unscopedUpserts: [], removals: [], markActivityAgentIds: [], }; const NO_FOLLOW_UP_INTENT: ExecApprovalFollowUpIntent = { shouldSend: false, agentId: null, sessionKey: null, message: null, }; export const resolveExecApprovalEventEffects = (params: { event: EventFrame; agents: AgentState[]; }): ExecApprovalEventEffects | null => { const requested = parseExecApprovalRequested(params.event); if (requested) { const resolvedAgentId = resolveExecApprovalAgentId({ requested, agents: params.agents, }); const approval: PendingExecApproval = { id: requested.id, agentId: resolvedAgentId, sessionKey: requested.request.sessionKey, command: requested.request.command, cwd: requested.request.cwd, host: requested.request.host, security: requested.request.security, ask: requested.request.ask, resolvedPath: requested.request.resolvedPath, createdAtMs: requested.createdAtMs, expiresAtMs: requested.expiresAtMs, resolving: false, error: null, }; if (!resolvedAgentId) { return { ...EMPTY_EVENT_EFFECTS, unscopedUpserts: [approval], }; } return { ...EMPTY_EVENT_EFFECTS, scopedUpserts: [{ agentId: resolvedAgentId, approval }], markActivityAgentIds: [resolvedAgentId], }; } const resolved = parseExecApprovalResolved(params.event); if (!resolved) { return null; } return { ...EMPTY_EVENT_EFFECTS, removals: [resolved.id], }; }; export const resolveExecApprovalFollowUpIntent = (params: { decision: ExecApprovalDecision; approval: PendingExecApproval | null; agents: AgentState[]; followUpMessage: string; }): ExecApprovalFollowUpIntent => { if (params.decision !== "allow-once" && params.decision !== "allow-always") { return NO_FOLLOW_UP_INTENT; } if (!params.approval) { return NO_FOLLOW_UP_INTENT; } const scopedAgentId = params.approval.agentId?.trim() ?? ""; const sessionAgentId = params.approval.sessionKey?.trim() ? (params.agents.find( (agent) => agent.sessionKey.trim() === params.approval?.sessionKey?.trim() )?.agentId ?? "") : ""; const targetAgentId = scopedAgentId || sessionAgentId; if (!targetAgentId) { return NO_FOLLOW_UP_INTENT; } const targetSessionKey = params.approval.sessionKey?.trim() || params.agents.find((agent) => agent.agentId === targetAgentId)?.sessionKey?.trim() || ""; const followUpMessage = params.followUpMessage.trim(); if (!targetSessionKey || !followUpMessage) { return NO_FOLLOW_UP_INTENT; } return { shouldSend: true, agentId: targetAgentId, sessionKey: targetSessionKey, message: followUpMessage, }; }; export const shouldTreatExecApprovalResolveErrorAsUnknownId = (error: unknown): boolean => error instanceof GatewayResponseError && /unknown approval id/i.test(error.message);