UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

281 lines (280 loc) • 16.7 kB
import { screenGateOutcome, SETTLEMENT_IS_REFUSAL } from "../gate-outcome.js"; import { askFaceOf, classifyAsk, headlessApprovalRemedy } from "../ask-origin.js"; import { wireOf } from "../tool-name-readings.js"; import { isProtectedWrite } from "../tool-registry.js"; import { attestationOf, decisionText } from "../tool-policy.js"; import { pathDenialCardOf } from "../../tools/fs/safety.js"; import { boundInputHashOf } from "../canonical-json.js"; import { argsAsTheToolReads, argsReadFrom } from "../effective-path-target.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, { reading: "producer" }); 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 }); } const SETTLEMENT_IS_HUMAN = { human_allowed: true, human_refused: true, approval_window_expired: false, denial_limit_window_expired: false, park_sla_expired: false, no_approver: false, approver_unavailable: false, approver_error: false, approver_contract: false, presentation_failed: false, blanket_allow_refused: false, policy_allowed: false, policy_refused: false, task_aborted: false, }; const licensedOf = (r) => (r.inputDigest !== undefined ? { licensed: r.inputDigest } : {}); const sameClasses = (a, b) => a === undefined || b === undefined ? a === b : a.length === b.length && a.every((c, i) => c === b[i]); export function createSettlementLedger(call) { let settled; const contractRefusal = (origin) => ({ settlement: { kind: "approver_contract", who: { party: "none" }, when: Date.now() }, origin }); return { get settled() { return settled; }, set(next) { settled = next; }, fromResolved(resolved, asked, req) { const record = attestationOf(resolved, call); const retired = typeof resolved === "object" && resolved !== null && Object.hasOwn(resolved, "settlement"); if (asked.action !== "ask" || asked.origin === undefined) { if (resolved.action === "allow" && record?.settlement === undefined && !retired) return resolved; throw new Error(`an ask for "${call.toolName}" was resolved without an origin word — the origin stamp precedes every resolution`); } const origin = asked.origin; const refused = (message, by) => { settled = by; return { action: "deny", message, decisionReason: "mode" }; }; if (retired) { return refused(`the resolution for "${call.toolName}" carries the retired "settlement" member — what ended a wait is an engine-minted record ` + `(\`attestation\`), never a resolver's member; denied fail-closed rather than recording a settlement nobody can attribute`, contractRefusal(origin)); } const edited = resolved.action === "allow" && resolved.updatedInput !== undefined; const licensed = () => { if (edited) return record.inputDigest === boundInputHashOf(resolved.updatedInput); const askedDigest = boundInputHashOf(req.args); return record.inputDigest === askedDigest && (resolved.presentedInput === undefined || boundInputHashOf(resolved.presentedInput) === askedDigest); }; const bound = record?.settlement !== undefined && record.origin === origin && sameClasses(record.classes, asked.classes) ? record.settlement : undefined; const refusal = bound !== undefined && SETTLEMENT_IS_REFUSAL[bound.kind] ? bound : undefined; if (resolved.action === "allow") { if (refusal !== undefined) return refused(`the resolution for "${call.toolName}" answered allow but its engine record names a refusal ("${refusal.kind}") — denied fail-closed`, { settlement: refusal, origin }); const answer = bound !== undefined && licensed() ? bound : undefined; if (answer !== undefined) { if (asked.requiresRealApproval === true && !SETTLEMENT_IS_HUMAN[answer.kind]) { return refused(`approval required for "${call.toolName}" — this ask requires real approval; the resolution's engine record names an answer that is not a person's ("${answer.kind}") — denied fail-closed: ${asked.message ?? ""}`, contractRefusal(origin)); } settled = { settlement: answer, origin, ...licensedOf(record) }; return resolved; } if (asked.requiresRealApproval === true) { return refused(`approval required for "${call.toolName}" — this ask requires real approval and the resolution's allow carries no engine ` + `settlement record for it (an answer the engine cannot attribute to anyone); denied fail-closed: ${asked.message ?? ""}`, contractRefusal(origin)); } return resolved; } if (resolved.action !== "deny") return resolved; settled = refusal !== undefined ? { settlement: refusal, origin } : contractRefusal(origin); return resolved; }, fromDecision(decision) { if (decision.action === "ask") return { decision }; const record = attestationOf(decision, call); const answer = record?.settlement !== undefined && record.origin !== undefined ? { settlement: record.settlement, origin: record.origin, ...licensedOf(record) } : undefined; if (answer !== undefined) settled = answer; const refusal = answer !== undefined && SETTLEMENT_IS_REFUSAL[answer.settlement.kind]; if (decision.action === "allow") { if (!refusal) return { decision }; return { decision: { action: "deny", message: `the decision for "${call.toolName}" says allow but its engine record names a refusal ("${answer.settlement.kind}") — denied fail-closed`, decisionReason: "rule" }, deniedBy: "ask_resolution" }; } return { decision, deniedBy: refusal ? "ask_resolution" : "policy" }; }, keepForBytes(runs) { if (settled !== undefined && settled.licensed !== boundInputHashOf(runs) && settled.licensed !== boundInputHashOf(argsReadFrom(runs))) settled = undefined; }, causeOf(decision) { return decision.action === "deny" ? attestationOf(decision, call)?.classifierCause : undefined; }, classifierRoundOf(decision) { return attestationOf(decision, call)?.classifierRound; }, 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; } } export function frozenInputSnapshot(input) { try { return deepFreeze(structuredClone(input)); } catch { return Object.freeze(Array.isArray(input) ? [...input] : input !== null && typeof input === "object" ? { ...input } : input); } } function deepFreeze(value) { if (value === null || typeof value !== "object" || Object.isFrozen(value)) return value; Object.freeze(value); for (const key of Reflect.ownKeys(value)) { const d = Object.getOwnPropertyDescriptor(value, key); if (d !== undefined && "value" in d) deepFreeze(d.value); } return value; } 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 function passAskFace(pass) { return askFaceOf(wireOf("gate.askFace", pass.req)); } export async function engineFailClosedExit(pass, reason) { const { input, toolName, toolCallId, notifyPermissionDenied } = pass; const gate = directDeny("hook"); const finalInput = frozenInputSnapshot(pass.currentInput); await notifyPermissionDenied({ toolName, input: cloneObserverInput(finalInput), toolCallId, reason, gate, ...(input.identity !== undefined ? { identity: input.identity } : {}) }); return { block: true, reason: formatHookFeedback(reason, input.reminderMark), gate, finalInput, preToolContext: pass.preToolContext }; } export const unownedArgsMessage = (subject, reason) => `${subject} could not be taken into the gate's own copy (${reason}) — denied fail-closed`; export async function unownedArgsExit(pass, reason) { pass.decision = { action: "deny", message: unownedArgsMessage(`the arguments of "${pass.toolName}"`, reason) }; pass.deniedBy = "policy"; return await exitGate(pass); } export function hookDenyExit(pass, deniedReason) { const { input } = pass; return { block: true, reason: formatHookFeedback(deniedReason, input.reminderMark), gate: directDeny("hook"), finalInput: frozenInputSnapshot(pass.currentInput), 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, toolName, toolCallId, ledger, notifyPermissionDenied } = pass; if (pass.decision.action === "deny") { if (pass.decision.updatedInput !== undefined) { pass.currentInput = argsAsTheToolReads(pass.req, 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 = attestationOf(pass.decision, { toolCallId, toolName })?.unresolvableCause; const unresolvable = foldCause ?? unresolvableCauseOfSettlement(gate); const tableSeat = unresolvable !== undefined ? askUnresolvableRemedySeat(unresolvable) : undefined; const remedySeat = tableSeat === "absent" && input.askSeatForm === "declared_none" && pass.askClassification?.classes.has("content_question") !== true ? "declared_none" : tableSeat; 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"); } const finalInput = frozenInputSnapshot(pass.currentInput); await notifyPermissionDenied({ toolName, input: cloneObserverInput(finalInput), toolCallId, reason: denyReason, gate, ...(input.identity !== undefined ? { identity: input.identity } : {}), }); return { block: true, reason: formatHookFeedback(denyReason, input.reminderMark), gate, finalInput, ...(() => { const details = pathDenialCardOf(attestationOf(pass.decision, { toolCallId, toolName })?.pathDenial) ?? 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 = argsAsTheToolReads(pass.req, rw); } ledger.keepForBytes(pass.currentInput); 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, gate: mintGateOutcome({ ...(ledger.settled !== undefined ? { settled: ledger.settled } : {}), ...(pass.classifierRound !== undefined ? { classifier: pass.classifierRound } : {}), ...(release !== undefined ? { released: releaseRecordOf(release) } : {}) }), finalInput: frozenInputSnapshot(pass.currentInput), preToolContext: pass.preToolContext, }; }