UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

394 lines (393 loc) • 23.5 kB
export const ASK_ORIGINS = Object.freeze(["content_question", "ancestor_marked", "org_unavailable", "org_rule", "rule_store_unavailable", "hook", "ask_rule", "denial_limit_fallback", "shell_gate_tighten", "safety_tighten", "policy"]); const ASK_ORIGIN_SET = new Set(ASK_ORIGINS); export function isAskOrigin(v) { return ASK_ORIGIN_SET.has(v); } export function askOriginOf(decision, facts) { const origin = originLadder(decision, facts); if (ORIGIN_IMPLIES_REAL_APPROVAL[origin] === "at_derivation" && decision.requiresRealApproval !== true) { throw new Error(`ask origin "${origin}" requires the requiresRealApproval bit on the decision it was derived from, and the decision carries none — an engine mint site dropped the bit`); } return origin; } const TIGHTEN_ONLY_ORIGINS = new Set(["shell_gate_tighten", "safety_tighten"]); export function stampAskOrigin(decision, facts, classification) { const origin = askOriginOf(decision, facts); const classed = askClassMembersOf(classification); const { mandate: _selfDeclared, ...unstamped } = decision; const clearance = ruleClearanceOf(unstamped, facts.mandateMarks); const mandated = { ...unstamped, ...(clearance.mandated ? { mandated: true } : {}), ...(clearance.mandate !== undefined ? { mandate: clearance.mandate } : {}), ...(facts.mandateMarks.requiresUserInteraction === true ? { requiresRealApproval: true } : {}) }; if (mandated.probeReadRootCandidate === undefined || TIGHTEN_ONLY_ORIGINS.has(origin)) return { ...mandated, origin, ...classed }; const { probeReadRootCandidate: _withdrawn, ...kept } = mandated; return { ...kept, origin, ...classed }; } export function ruleClearanceOf(ask, marks) { const marksWord = persistedRuleMandateOf({ ...marks, probeMandated: ask?.probeMandated === true }); const foldedDoor = (ask?.foldedAskClasses ?? []).some((c) => !MODE_ARBITRATION[c].ruleMayClear); const doorOutsideTable = ask?.requiresRealApproval === true || ask?.decisionReason === "hook" || ask?.decisionReason === "mode" || foldedDoor || ask?.authoredBy === "deployment" || ask?.inheritedUnresolved === true || ask?.ancestorResolved === true; const mandated = ask?.mandated === true || doorOutsideTable || marksWord !== undefined; const stampedWord = ask?.mandated === true && isPersistedRuleMandate(ask.mandate) ? ask.mandate : undefined; const mandate = doorOutsideTable || (ask?.mandated === true && stampedWord === undefined) ? undefined : (stampedWord ?? marksWord); const word = (() => { if (ask?.requiresRealApproval === true) return "mandated"; if (Array.isArray(ask?.allowRuleShadowed) && ask.allowRuleShadowed.length > 0) return "shadowed"; if (ask?.decisionReason === "hook") return "mandated"; if (ask?.decisionReason === "mode" || foldedDoor) return "mandated"; if (ask?.authoredBy === "deployment") return "mandated"; if (ask?.matchedAskRule !== undefined) return "shadowed"; if (ask?.inheritedUnresolved === true) return "mandated"; if (ask?.ancestorResolved === true) return "mandated"; return mandated ? "mandated" : undefined; })(); return { ...(word !== undefined ? { word } : {}), mandated, ruleMayClear: !mandated && ask?.matchedAskRule === undefined, ...(mandate !== undefined ? { mandate } : {}) }; } export function askMandateMember(ask) { const { mandated, mandate } = ruleClearanceOf(ask, {}); const authored = ask?.authoredBy === "deployment" ? { authoredBy: "deployment" } : {}; return mandated ? { mandated: true, ...(mandate !== undefined ? { mandate } : {}), ...authored } : authored; } export function ownedClearanceFacts(d) { const { mandated, requiresRealApproval, decisionReason, matchedAskRule, probeMandated, authoredBy, foldedAskClasses } = d; return { ...(mandated === true ? { mandated } : {}), ...(Array.isArray(foldedAskClasses) && foldedAskClasses.length > 0 ? { foldedAskClasses: [...foldedAskClasses] } : {}), ...(authoredBy === "deployment" ? { authoredBy } : {}), ...(requiresRealApproval === true ? { requiresRealApproval } : {}), ...(decisionReason !== undefined ? { decisionReason } : {}), ...(typeof matchedAskRule === "string" ? { matchedAskRule } : {}), ...(probeMandated === true ? { probeMandated } : {}), }; } function originLadder(decision, facts) { if (facts.contentQuestion) return "content_question"; if (facts.markedUnresolvable) return "ancestor_marked"; if (facts.org === "unavailable") return "org_unavailable"; if (facts.org === "rule") return "org_rule"; if (facts.ruleCallUnreadable === true) return "rule_store_unavailable"; if (decision.decisionReason === "hook") return "hook"; if (decision.matchedAskRule !== undefined) return "ask_rule"; if (decision.denialLimitFallback !== undefined) return "denial_limit_fallback"; if (facts.tightened === "shell_gate") return "shell_gate_tighten"; if (facts.tightened === "safety") return "safety_tighten"; return "policy"; } export const ORIGIN_IMPLIES_REAL_APPROVAL = { content_question: false, ancestor_marked: false, org_unavailable: "at_derivation", org_rule: "at_derivation", rule_store_unavailable: "at_derivation", hook: false, ask_rule: false, denial_limit_fallback: "at_mint", shell_gate_tighten: false, safety_tighten: false, policy: false, }; export const PERSISTED_RULE_MANDATES = Object.freeze(["operator_always", "tool_marks", "probe_mandate", "probe_unanswered", "write_protection", "write_protection_unresolved"]); const PERSISTED_RULE_MANDATE_SET = new Set(PERSISTED_RULE_MANDATES); export function isPersistedRuleMandate(v) { return PERSISTED_RULE_MANDATE_SET.has(v); } const MANDATE_DISCLOSURE = { probe_mandate: "the reversibility check declared this call structurally gated (the built-in shell check raises this for a read outside the directories allowed for this session) — it is cleared by confirming this call, never by a standing allow rule", probe_unanswered: "the reversibility check did not answer for this call (it timed out or failed), so whether the call reads outside the directories allowed for this session is unknown — it is cleared by confirming this call, never by a standing allow rule", operator_always: "this deployment mandates per-call confirmation for shell commands (shellGate: always)", tool_marks: "this tool carries egress/irreversibility/interaction marks (a mandated confirmation a rule cannot clear)", write_protection: "this write's target is on the deployment's write-protection table (the agent's own configuration is guarded one look per write) — it is cleared by confirming this call, never by a standing allow rule", write_protection_unresolved: "the write-protection judge could not resolve this write's target, so whether it lands on the protected table is unknown — it is cleared by confirming this call: no standing rule and no automatic decider answers for it, though a wired approver seat can", }; export function mandateDisclosureOf(mandate) { return mandate === undefined ? "an explicit ask rule matched this call (a person's ask-me-each-time outranks a standing allow rule)" : MANDATE_DISCLOSURE[mandate]; } export function persistedRuleMandateOf(marks) { const word = irreversibilityMandateOf(marks); if (word === "probe_mandate" || word === "probe_unanswered") return word; if (marks.writeProtectionUnresolved === true) return "write_protection_unresolved"; if (marks.writeProtected === true) return "write_protection"; return marks.egress === true || marks.requiresUserInteraction === true ? "tool_marks" : word; } function irreversibilityMandateOf(marks) { return marks.probeMandated === true ? "probe_mandate" : marks.probeUnanswered === true ? "probe_unanswered" : marks.shellGated === true ? marks.irreversibility === "always" ? "operator_always" : undefined : marks.irreversibility === "always" || marks.irreversibility === "maybe" ? "tool_marks" : undefined; } export function safetyAxisOf(marks) { const egress = marks.egress === true; const raised = irreversibilityMandateOf(marks) !== undefined || (marks.shellGated === true && marks.irreversibility === "maybe" && marks.tightened === "shell_gate"); const shell = raised && marks.shellGated === true; return egress || raised ? { egress, irreversible: raised && !shell, ...(shell ? { shell: true } : {}) } : undefined; } export const PERMISSION_MODES = Object.freeze(["default", "acceptEdits", "plan", "auto", "bypassPermissions", "dontAsk"]); const PERMISSION_MODE_SET = new Set(PERMISSION_MODES); export function isPermissionMode(v) { return PERMISSION_MODE_SET.has(v); } export const PERMISSION_MODE_RANK = { plan: 0, default: 1, dontAsk: 1, acceptEdits: 2, auto: 3, bypassPermissions: 4 }; export function clampPermissionMode(declared, ceiling) { if (declared === undefined) return { effective: ceiling, clamped: false }; if (declared === ceiling) return { effective: declared, clamped: false }; if (declared === "bypassPermissions") return { effective: ceiling, clamped: true }; if ((declared === "acceptEdits" && ceiling === "auto") || (declared === "auto" && ceiling === "acceptEdits")) return { effective: ceiling, clamped: true }; return PERMISSION_MODE_RANK[declared] <= PERMISSION_MODE_RANK[ceiling] ? { effective: declared, clamped: false } : { effective: ceiling, clamped: true }; } export const ASK_CLASSES = Object.freeze([ "content_question", "hook", "ancestor", "unverifiable_delete", "transcript_write", "org_rule", "governance_unavailable", "rule_unreadable", "ask_rule", "denial_limit_fallback", "requires_user_interaction", "operator_forced", "shell_floor", "write_protection_unresolved", "probe", "write_protection", "tool_marks_mcp", "tool_marks_a2a", "policy_rra", "artifact_shared_read", "mode", "unstated", "ordinary", ]); const ASK_CLASS_SET = new Set(ASK_CLASSES); export function isAskClass(v) { return ASK_CLASS_SET.has(v); } export const MODE_ARBITRATION = { content_question: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true }, hook: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false }, ancestor: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false }, unverifiable_delete: { bypassImmune: true, classifierEligible: true, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true }, transcript_write: { bypassImmune: false, classifierEligible: true, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true }, org_rule: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false }, governance_unavailable: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false }, rule_unreadable: { bypassImmune: false, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false }, ask_rule: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true }, denial_limit_fallback: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false }, requires_user_interaction: { bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true }, operator_forced: { bypassImmune: true, classifierEligible: true, sandboxAdmission: true, ruleMayClear: false, hookAskFloor: false }, shell_floor: { bypassImmune: true, classifierEligible: true, sandboxAdmission: true, ruleMayClear: false, hookAskFloor: false }, write_protection_unresolved: { bypassImmune: true, classifierEligible: false, sandboxAdmission: true, ruleMayClear: false, hookAskFloor: true }, probe: { bypassImmune: false, classifierEligible: true, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false }, write_protection: { bypassImmune: false, classifierEligible: true, sandboxAdmission: true, ruleMayClear: false, hookAskFloor: true }, tool_marks_mcp: { bypassImmune: false, classifierEligible: true, sandboxAdmission: true, ruleMayClear: false, hookAskFloor: false }, tool_marks_a2a: { bypassImmune: false, classifierEligible: true, sandboxAdmission: true, ruleMayClear: false, hookAskFloor: false }, policy_rra: { bypassImmune: true, classifierEligible: true, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false }, artifact_shared_read: { bypassImmune: false, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true }, mode: { bypassImmune: false, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false }, unstated: { bypassImmune: false, classifierEligible: true, sandboxAdmission: false, ruleMayClear: true, hookAskFloor: false }, ordinary: { bypassImmune: false, classifierEligible: true, sandboxAdmission: true, ruleMayClear: true, hookAskFloor: false }, }; export const RRA_EXPLAINERS = new Set(["unverifiable_delete", "transcript_write", "org_rule", "governance_unavailable", "rule_unreadable", "denial_limit_fallback", "ancestor", "artifact_shared_read", "requires_user_interaction"]); export const RELEASABLE_ASK_CLASSES = ASK_CLASSES.filter((c) => !MODE_ARBITRATION[c].bypassImmune); export const MODE_RELEASE_REASONS = Object.freeze(["rule_unreadable"]); const MODE_RELEASE_REASON_SET = new Set(MODE_RELEASE_REASONS); export function isModeReleaseReason(v) { return MODE_RELEASE_REASON_SET.has(v); } export function askClassMembersOf(c) { return { askClass: c.askClass, classes: Object.freeze(ASK_CLASSES.filter((k) => c.classes.has(k))) }; } export function screenAskClassMembers(askClass, classes) { if (!isAskClass(askClass) || !Array.isArray(classes) || classes.length === 0 || !classes.every(isAskClass)) return undefined; const held = new Set(classes); const ordered = ASK_CLASSES.filter((k) => held.has(k)); if (ordered.length !== classes.length || ordered[0] !== askClass) return undefined; return { askClass, classes: Object.freeze(ordered) }; } export function decisionAskClassesOf(d) { const out = new Set(); if (d.guard === "unverifiable_delete") out.add("unverifiable_delete"); if (d.guard === "transcript_write") out.add("transcript_write"); if (d.guard === "artifact_shared_read") out.add("artifact_shared_read"); if (d.ruleUnreadable !== undefined) out.add("rule_unreadable"); if (d.matchedAskRule !== undefined) out.add("ask_rule"); if (d.denialLimitFallback !== undefined) out.add("denial_limit_fallback"); if (d.authoredBy === "deployment") out.add("operator_forced"); if (d.probeMandated === true) out.add("probe"); if (d.decisionReason === "hook") out.add("hook"); if (d.decisionReason === "mode") out.add("mode"); if (d.requiresRealApproval === true && ![...out].some((c) => RRA_EXPLAINERS.has(c))) out.add("policy_rra"); if (out.size === 0 && d.mandated === true) out.add("operator_forced"); for (const c of d.foldedAskClasses ?? []) out.add(c); return out; } export function foldArbitrationRow(classes) { if (classes.size === 0) return MODE_ARBITRATION.ordinary; let bypassImmune = false, classifierEligible = true, sandboxAdmission = true, ruleMayClear = true, hookAskFloor = false; for (const c of classes) { const row = MODE_ARBITRATION[c]; bypassImmune ||= row.bypassImmune; classifierEligible &&= row.classifierEligible; sandboxAdmission &&= row.sandboxAdmission; ruleMayClear &&= row.ruleMayClear; hookAskFloor ||= row.hookAskFloor; } return { bypassImmune, classifierEligible, sandboxAdmission, ruleMayClear, hookAskFloor }; } export function classifyAsk(d, facts) { const classes = decisionAskClassesOf({ ...d, foldedAskClasses: undefined }); classes.delete("policy_rra"); if (facts.contentQuestion) classes.add("content_question"); if (facts.markedUnresolvable) classes.add("ancestor"); if (facts.org === "rule") classes.add("org_rule"); if (facts.org === "unavailable") classes.add("governance_unavailable"); if (facts.ruleCallUnreadable === true) classes.add("rule_unreadable"); const marks = facts.mandateMarks; if (marks.requiresUserInteraction === true) classes.add("requires_user_interaction"); if (marks.shellGated === true && marks.irreversibility === "always") classes.add("shell_floor"); const toolMarked = marks.shellGated !== true && (marks.egress === true || marks.irreversibility === "always" || marks.irreversibility === "maybe"); if (toolMarked) { const sources = facts.toolMarkSources; if (sources === undefined || sources.size === 0 || sources.has("deployment")) classes.add("operator_forced"); if (sources?.has("mcp_annotation")) classes.add("tool_marks_mcp"); if (sources?.has("a2a_default")) classes.add(facts.a2aIsolatePeers === true ? "operator_forced" : "tool_marks_a2a"); } if (marks.probeUnanswered === true) classes.add("probe"); if (marks.writeProtectionUnresolved === true) classes.add("write_protection_unresolved"); if (marks.writeProtected === true) classes.add("write_protection"); if (facts.tightened === "unstated") classes.add("unstated"); for (const c of facts.ancestorClasses ?? []) classes.add(c); if (d.requiresRealApproval === true && ![...classes].some((c) => RRA_EXPLAINERS.has(c))) classes.add("policy_rra"); for (const c of d.foldedAskClasses ?? []) classes.add(c); if (classes.size === 0 && d.mandated === true) classes.add("operator_forced"); if (classes.size === 0) classes.add("ordinary"); const askClass = ASK_CLASSES.find((c) => classes.has(c)); return { classes, askClass, row: foldArbitrationRow(classes) }; } export function headlessDenialMessage(toolName, askMessage) { return `Permission for this tool use was denied: it requires interactive approval, and permission prompts are not available in this session (no approver is wired for "${toolName}" — headless auto-deny). The action was NOT performed. Do not claim it succeeded, and do not retry it in this session — report the limitation to the user, or suggest an alternative. What was requested: ${askMessage}`; } export function headlessApprovalRemedy(input) { const approver = input.approverSeat === "unavailable" ? "make a person reachable through the approver that is wired (it answered `unavailable` for this ask)" : "wire an approver (`onAsk`) to put it to a person"; const c = input.classification; if (input.mode === "acceptEdits" && input.unplacedOptions !== undefined && (c === undefined || (c.classes.size === 1 && c.classes.has("ordinary")))) return `${fileCommandOptionsSentence(input.unplacedOptions.program)} Spell the ${input.unplacedOptions.program} without option words for the mode to answer it, or ${approver}`; if (c?.row.bypassImmune === true) return `no permission mode releases this ask (${c.askClass}): ${approver}, or change the call`; if (input.mode === "plan" && c?.classes.has("mode") === true) return `the plan permission mode asks before every write and every call to a tool the engine does not define that is not read-only — leave plan mode (\`setPermissionMode\` to another mode, or approve the plan) to run it, or ${approver}`; if (c !== undefined && c.classes.size === 1 && c.classes.has("unstated")) return `no rule, policy or hook granted this tool, and it states no permission of its own — grant it with an allow rule (a permission rule \`allow\` for its name, or an allow-list entry), run with \`permissionMode: "bypassPermissions"\`, or ${approver}`; const onlyOrdinary = c === undefined || (c.classes.size === 1 && c.classes.has("ordinary")); if (input.coveredWrite && onlyOrdinary) { if (input.mode === "acceptEdits") return `under \`permissionMode: "acceptEdits"\` the mode asks only for a write outside the working directories — if this target is outside them, add its directory to \`additionalDirectories\`; otherwise (or besides) ${approver}`; if (input.mode === undefined || input.mode === "default" || input.mode === "auto") return `the mode's write gate asks every write under \`default\`: \`permissionMode: "acceptEdits"\` lets writes inside the working directories (the task root and \`additionalDirectories\`) through without asking (a question another policy raised still stands), or ${approver}`; } return `run with \`permissionMode: "bypassPermissions"\` to release this ask (the release is disclosed on the call), or ${approver}`; } export function fileCommandOptionsSentence(program) { return `${program} with flags requires manual approval to ensure path safety: the mode cannot automatically validate ${program} commands that use flags, as some flags like --target-directory=PATH can bypass path validation.`; } export function arbitrateByMode(mode, c, disclosure, call) { const has = (k) => c.classes.has(k); const classes = ASK_CLASSES.filter((k) => c.classes.has(k)); switch (mode) { case "dontAsk": return has("hook") && !c.row.hookAskFloor ? { kind: "ask" } : { kind: "denied_by_mode", mode, askClass: c.askClass, classes }; case "bypassPermissions": if (has("write_protection_unresolved")) return { kind: "denied_by_mode", mode, askClass: c.askClass, classes }; if (c.row.bypassImmune) return { kind: "ask" }; const reason = [...MODE_RELEASE_REASON_SET].find((r) => has(r)); return { kind: "released", mode, askClass: c.askClass, classes, ...(disclosure?.origin !== undefined ? { origin: disclosure.origin } : {}), ...(disclosure?.mandate !== undefined ? { mandate: disclosure.mandate } : {}), ...(reason !== undefined ? { reason } : {}), }; case "acceptEdits": return call?.fileCommand === true && c.classes.size === 1 && c.classes.has("ordinary") ? { kind: "accepted", mode } : { kind: "ask" }; case "plan": case "default": case "auto": return { kind: "ask" }; default: { const _exhaustive = mode; return _exhaustive; } } }