UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

181 lines (180 loc) • 10.3 kB
import { screenGateOutcome, SETTLEMENT_IS_REFUSAL } from "../gate-outcome.js"; import { classifyAsk, headlessApprovalRemedy } from "../ask-origin.js"; import { isProtectedWrite } from "../tool-registry.js"; import { askUnresolvableCauseOf, classifierDenyCauseOf, classifierRoundOf, decisionText, engineSettlementOf } from "../tool-policy.js"; import { askUnresolvableRemedySeat } from "../ask-unresolvable-notice.js"; import { inlineUntrusted } from "../untrusted-text.js"; import { formatHookFeedback } from "../reminder-mint.js"; export function mintGateOutcome(facts) { const settlement = facts.settled !== undefined ? Object.freeze({ ...facts.settled.settlement, who: Object.freeze({ ...facts.settled.settlement.who }) }) : undefined; const attribution = facts.classifier !== undefined ? Object.freeze({ ...facts.classifier, ...(facts.classifier.fallback !== undefined ? { fallback: Object.freeze({ ...facts.classifier.fallback }) } : {}) }) : undefined; const outcome = Object.freeze({ disposition: Object.freeze(facts.deniedBy !== undefined ? { kind: "denied", deniedBy: facts.deniedBy, ...(facts.cause !== undefined ? { cause: facts.cause } : {}), ...(attribution !== undefined ? { classifier: attribution } : {}), ...(facts.modeRefusal !== undefined ? { mode: facts.modeRefusal.mode, askClass: facts.modeRefusal.askClass, classes: Object.freeze([...facts.modeRefusal.classes]) } : {}), } : { kind: "allowed", ...(attribution !== undefined ? { classifier: attribution } : {}), ...(facts.released !== undefined ? { released: Object.freeze({ ...facts.released, classes: Object.freeze([...facts.released.classes]) }) } : {}) }), ...(facts.settled !== undefined ? { settlement: settlement, origin: facts.settled.origin } : {}), }); const defects = screenGateOutcome(outcome); if (defects.length > 0) throw new Error(`the tool gate composed an outcome that violates its own record: ${defects.join("; ")}`); return outcome; } function releaseRecordOf(a) { const { kind: _kind, ...record } = a; return record; } export function directDeny(deniedBy) { return mintGateOutcome({ deniedBy }); } export function createSettlementLedger(call) { let settled; return { get settled() { return settled; }, set(next) { settled = next; }, fromResolved(resolved, asked, toolName) { if (asked.action !== "ask" || asked.origin === undefined) { if (resolved.settlement === undefined) return; throw new Error(`an ask for "${toolName}" was resolved without an origin word — the origin stamp precedes every resolution`); } const attested = engineSettlementOf(resolved, call); if (attested !== undefined) { settled = { settlement: attested.settlement, origin: asked.origin }; return; } if (resolved.settlement === undefined) return; settled = { settlement: resolved.settlement, origin: asked.origin }; }, fromDecision(decision) { if (decision.action === "ask") return undefined; const attested = engineSettlementOf(decision, call); if (attested !== undefined) settled = { settlement: attested.settlement, origin: attested.origin }; if (decision.action !== "deny") return undefined; return attested !== undefined && SETTLEMENT_IS_REFUSAL[attested.settlement.kind] ? "ask_resolution" : "policy"; }, causeOf(decision) { return decision.action === "deny" ? classifierDenyCauseOf(decision, call) : undefined; }, classifierRoundOf(decision) { return classifierRoundOf(decision, call); }, supersede() { settled = undefined; }, }; } export function cloneObserverInput(input) { try { return structuredClone(input); } catch { if (Array.isArray(input)) return [...input]; if (input !== null && typeof input === "object") return { ...input }; return input; } } const PARK_FAILURE_CAUSE_MAX = 600; function withParkFailureCause(reason, parkFailed) { if (parkFailed === undefined) return reason; return (`${reason} — note: a durable approval park was attempted for this call FIRST and could not be minted ` + `(${inlineUntrusted(parkFailed, PARK_FAILURE_CAUSE_MAX)}), so the refusal above is what the fallback had ` + `left to say, not the reason the call stopped.`); } export async function engineFailClosedExit(pass, reason) { const { input, toolName, toolCallId, notifyPermissionDenied } = pass; const gate = directDeny("hook"); await notifyPermissionDenied({ toolName, input: cloneObserverInput(pass.currentInput), toolCallId, reason, gate, ...(input.identity !== undefined ? { identity: input.identity } : {}) }); return { block: true, reason: formatHookFeedback(reason, input.reminderMark), gate, preToolContext: pass.preToolContext }; } export function hookDenyExit(pass, deniedReason) { const { input } = pass; return { block: true, reason: formatHookFeedback(deniedReason, input.reminderMark), gate: directDeny("hook"), preToolContext: pass.preToolContext, }; } const ANCESTOR_UNRESOLVED_CLASSIFICATION = classifyAsk({ action: "ask", message: "" }, { contentQuestion: false, markedUnresolvable: true, org: undefined, tightened: undefined, mandateMarks: {} }); function unresolvableCauseOfSettlement(gate) { if (gate.disposition.kind !== "denied" || gate.disposition.deniedBy !== "ask_resolution" || gate.settlement === undefined) return undefined; if (gate.settlement.kind === "approver_unavailable") return "approver_unavailable"; if (gate.settlement.kind === "no_approver") return "no_approver_wired"; return undefined; } export async function exitGate(pass) { const { input, event, toolName, toolCallId, ledger, notifyPermissionDenied } = pass; if (pass.decision.action === "deny") { if (pass.decision.updatedInput !== undefined) { pass.currentInput = pass.decision.updatedInput; } if (pass.deniedBy === undefined) throw new Error(`the tool gate refused "${toolName}" without a refusing layer — every deny site attributes itself`); const ancestorArbitration = pass.arbitration === undefined && pass.deniedBy === "policy" ? input.ancestorArbitrationOf?.(toolCallId) : undefined; const modeRefusal = pass.arbitration?.kind === "denied_by_mode" ? pass.arbitration : ancestorArbitration?.kind === "denied_by_mode" ? ancestorArbitration : undefined; if (modeRefusal !== undefined) { pass.deniedBy = "mode"; ledger.supersede(); } const gate = mintGateOutcome({ deniedBy: pass.deniedBy, ...(pass.denyCause !== undefined ? { cause: pass.denyCause } : {}), ...(ledger.settled !== undefined ? { settled: ledger.settled } : {}), ...(pass.classifierRound !== undefined ? { classifier: pass.classifierRound } : {}), ...(modeRefusal !== undefined ? { modeRefusal: { mode: modeRefusal.mode, askClass: modeRefusal.askClass, classes: modeRefusal.classes } } : {}) }); const foldCause = askUnresolvableCauseOf(pass.decision, { toolCallId, toolName }); const unresolvable = foldCause ?? unresolvableCauseOfSettlement(gate); const remedySeat = unresolvable !== undefined ? askUnresolvableRemedySeat(unresolvable) : undefined; const remedy = remedySeat !== undefined ? headlessApprovalRemedy({ mode: input.permissionMode?.(), classification: foldCause !== undefined ? ANCESTOR_UNRESOLVED_CLASSIFICATION : pass.askClassification, approverSeat: remedySeat, coveredWrite: isProtectedWrite(pass.req), ...(pass.shellUnplacedOptions !== undefined ? { unplacedOptions: pass.shellUnplacedOptions } : {}) }) : undefined; const denyReason = withParkFailureCause(`${decisionText(pass.decision) ?? `tool "${toolName}" denied by policy`}${remedy !== undefined ? ` — ${remedy}` : ""}`, pass.parkFailed); if (unresolvable !== undefined) { const cause = unresolvable; pass.notifier.notify(() => input.onAskUnresolvable?.({ toolName, toolCallId, cause, parkLaneExisted: pass.suspendAsk !== undefined, ...(remedy !== undefined ? { remedy } : {}) }), "toolGate.onAskUnresolvable"); } await notifyPermissionDenied({ toolName, input: cloneObserverInput(pass.currentInput), toolCallId, reason: denyReason, gate, ...(input.identity !== undefined ? { identity: input.identity } : {}), }); return { block: true, reason: formatHookFeedback(denyReason, input.reminderMark), gate, ...(() => { const details = pass.denyDetails ?? (modeRefusal !== undefined ? undefined : pass.shellReadDetails); return details !== undefined ? { details } : {}; })(), preToolContext: pass.preToolContext, }; } if (pass.decision.action === "allow") { const rw = pass.decision.updatedInput !== undefined ? pass.decision.updatedInput : pass.policyRewrite; if (rw !== undefined) pass.currentInput = rw; } const release = pass.arbitration?.kind === "released" ? pass.arbitration : (() => { if (ledger.settled !== undefined) return undefined; const a = pass.arbitration === undefined ? input.ancestorArbitrationOf?.(toolCallId) : undefined; return a?.kind === "released" ? a : undefined; })(); return { updatedInput: pass.currentInput === event.input ? undefined : pass.currentInput, gate: mintGateOutcome({ ...(ledger.settled !== undefined ? { settled: ledger.settled } : {}), ...(pass.classifierRound !== undefined ? { classifier: pass.classifierRound } : {}), ...(release !== undefined ? { released: releaseRecordOf(release) } : {}) }), preToolContext: pass.preToolContext, }; }