4fa4f13558
Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com>
128 lines
3.8 KiB
TypeScript
128 lines
3.8 KiB
TypeScript
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);
|