@sema-agent/core
Version:
Stateless, task-oriented AI agent core
181 lines (180 loc) • 10.3 kB
JavaScript
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,
};
}