UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

682 lines (681 loc) • 47.4 kB
import { attestClassifierDenyCause, classifierDenyCauseOf, decisionText, refuseOutOfContractDecision } from "./tool-policy.js"; import { arbitrateByMode, classifyAsk, fileCommandOptionsSentence, persistedRuleMandateOf, ruleClearanceOf, safetyAxisOf, stampAskOrigin } from "./ask-origin.js"; import { classifierParseFailureDenyMessage, denialLimitFallbackMessage, unarmedWindow } from "./auto-mode.js"; import { classifierUnavailableArm } from "./classifier-unavailable-arm.js"; import { SETTLEMENT_NOBODY_JUDGED } from "./gate-outcome.js"; import { inlineUntrusted } from "./untrusted-text.js"; import { ASK_USER_QUESTION_TOOL_NAME } from "./ask-question.js"; import { applyPersistedTightening, disclosedRuleSet } from "./persisted-rule-arms.js"; import { skillAllowDecision, skillRowRefusal } from "./skill-grant.js"; import { allowRuleShadowOf } from "./allow-entry.js"; import { CD_GIT_COMPOUND_SENTENCE, changesDirectoryBeforeGit, readOnlyShellVerdict } from "./read-only-shell.js"; import { readShellCommand } from "./shell-lexer.js"; import { COMMAND_RULE_TOOL } from "./permission-rule-model.js"; import { catalogRuleFaceOf } from "./tool-registry.js"; import { callFaceSeat } from "./tool-roster.js"; import { heldBy, wireOf } from "./tool-name-readings.js"; import { asksUnstated, modeAcceptsFileCommand, permissionSeatAt, unstatedAskMessage } from "./mode-policy.js"; import { ORG_RULE_DECISION_REASON, ORG_UNAVAILABLE_DECISION_REASON } from "./permission-rule-org.js"; import { exitGate } from "./runner/gate-exit.js"; import { readOrgRuleLane, readPersistedRuleLane } from "./rule-lane-reads.js"; import { traceHookCrash } from "./gate-crash-trace.js"; import { createGateDeadline, awaitWriteProtectionJudgment, withGateTimeout } from "./gate-bounded-await.js"; import { thrownText } from "./thrown-value.js"; const QUESTION_TOOL_NAMES = new Set([ASK_USER_QUESTION_TOOL_NAME]); function parkWith(suspendAsk, parkArgs, carry) { return suspendAsk(...parkArgs, carry); } export async function runGateLanes(pass) { const { input, toolName, toolCallId, callSignal, ledger, notifier, screening, adjudicate, resolveAsk, suspendAsk } = pass; const boundedAwaitDeadlineMs = createGateDeadline(input, notifier); let orgRealApprovalRequired = false; let markedThisRound; const markedUnresolvable = () => (markedThisRound ??= input.isMarkedUnresolvable?.(input.event.toolCallId) === true); const mandateMarksNow = () => ({ egress: input.egress, shellGated: input.shellGated, irreversibility: input.irreversibility, requiresUserInteraction: input.requiresUserInteraction, probeUnanswered: pass.probeUnanswered === true, writeProtected: pass.writeProtected === true, writeProtectionUnresolved: pass.writeProtectionUnresolved === true, }); const askOriginFacts = (org, ruleCallUnreadable) => ({ contentQuestion: heldBy("lanes.contentQuestionClass", QUESTION_TOOL_NAMES, pass.req), markedUnresolvable: markedUnresolvable(), org, ...(ruleCallUnreadable ? { ruleCallUnreadable } : {}), tightened: pass.tightenedBy, mandateMarks: mandateMarksNow(), ...(input.toolMarkSources !== undefined ? { toolMarkSources: input.toolMarkSources } : {}), ...(input.a2aIsolatePeers === true ? { a2aIsolatePeers: true } : {}), }); const classifyStanding = (unstamped, facts) => (pass.askClassification = classifyAsk(unstamped, facts)); let orgTightenCount = 0; let orgEvidence = (() => { const w = input.orgRules === undefined ? "not_wired" : "not_adjudicated"; return { revisionAbsent: w, ruleAbsent: w }; })(); const mintRuleEvidence = (personal) => ({ ...(orgEvidence.revision !== undefined ? { orgRevision: orgEvidence.revision } : { orgRevisionAbsent: orgEvidence.revisionAbsent ?? "not_reported" }), ...(orgEvidence.rule !== undefined ? { orgRule: orgEvidence.rule } : { orgRuleAbsent: orgEvidence.ruleAbsent ?? "not_reported" }), ...(personal.dots !== undefined ? { personalRuleDots: personal.dots } : { personalRuleDotsAbsent: personal.dotsAbsent ?? "not_reported" }), }); const applyOrgLayer = async (current, args) => { if (input.orgRules === undefined) return { decision: current, org: undefined }; if (wireOf("lanes.orgContentAskExemption", pass.req) === ASK_USER_QUESTION_TOOL_NAME && input.orgRules.contentAskToolMounted === true) return { decision: current, org: undefined }; let decided = current; const answer = await readOrgRuleLane(input.orgRules, { toolName, args, toolCallId, ...callFaceSeat(pass.req.face) }, input.abortSignal); if (answer.status === "unavailable") { orgEvidence = { revisionAbsent: "unavailable", ruleAbsent: "unavailable" }; orgRealApprovalRequired = true; await notifier.notifyAsync(() => input.orgRules?.onUnavailable?.({ toolName, toolCallId, message: answer.disclosures.join("; ") }), "toolGate.orgSnapshotUnavailable"); const orgUnavailableMessage = `tool "${toolName}" needs approval: this deployment is org-governed and cannot currently adjudicate ` + `against an organization policy snapshot — every allow tightens to a real approval until it can`; if (decided.action === "allow") { decided = { action: "ask", message: orgUnavailableMessage, decisionReason: ORG_UNAVAILABLE_DECISION_REASON, requiresRealApproval: true }; orgTightenCount += 1; } else if (decided.action === "ask" && decided.requiresRealApproval !== true) { decided = { ...decided, requiresRealApproval: true, ...(pass.tightenedBy !== undefined ? { message: orgUnavailableMessage } : {}) }; orgTightenCount += 1; } return { decision: decided, org: "unavailable" }; } const revisionCell = answer.revision !== undefined ? { revision: answer.revision } : { revisionAbsent: "not_reported" }; if (answer.verdict === undefined) { orgEvidence = { ...revisionCell, ruleAbsent: "no_match" }; return { decision: decided, org: undefined }; } const { behavior, rule, unreadable } = answer.verdict; orgEvidence = { ...revisionCell, rule }; if (behavior === "deny") { orgTightenCount += 1; pass.deniedBy = "org"; pass.denyCause = undefined; return { decision: { action: "deny", message: `an organization policy rule (${rule}) denies this call`, decisionReason: ORG_RULE_DECISION_REASON }, org: undefined }; } if (decided.action === "deny") return { decision: decided, org: undefined }; orgTightenCount += 1; orgRealApprovalRequired = true; const orgRuleMessage = unreadable !== undefined ? `an organization policy rule (${rule}) may reach this call, which could not be read against it (${unreadable}) — a person must decide` : `an organization policy rule (${rule}) requires approval for this call`; return { decision: decided.action === "ask" ? { ...decided, requiresRealApproval: true, ...(decided.requiresRealApproval !== true && pass.tightenedBy !== undefined ? { message: orgRuleMessage } : {}) } : { action: "ask", message: orgRuleMessage, decisionReason: ORG_RULE_DECISION_REASON, requiresRealApproval: true }, org: "rule", }; }; const orgAnswer = await applyOrgLayer(pass.decision, pass.policyRewrite !== undefined ? pass.policyRewrite : pass.currentInput); pass.decision = orgAnswer.decision; const ruleCallUnreadableOf = (lane) => lane?.unreadable === true && lane.reason !== undefined; const readPersistedRules = (req) => readPersistedRuleLane(input.persistedRules, req, input.abortSignal); const laneReq = { ...pass.req, ...(pass.policyRewrite !== undefined ? { args: pass.policyRewrite } : {}) }; const laneAnswer = input.persistedRules !== undefined && wireOf("lanes.persistedLaneSkip", pass.req) !== ASK_USER_QUESTION_TOOL_NAME ? await readPersistedRules(laneReq) : undefined; { const tightened = applyPersistedTightening(pass.decision, laneAnswer); pass.decision = tightened.decision; if (tightened.tightened === "deny") pass.deniedBy = "persisted_rule"; if (tightened.tightened === "deny") pass.denyCause = undefined; const skillRefusal = pass.decision.action === "deny" ? undefined : skillRowRefusal(input.skillGrants, laneReq); if (skillRefusal !== undefined) { pass.decision = skillRefusal; pass.deniedBy = "policy"; pass.denyCause = undefined; } } if (pass.decision.action === "ask") { pass.decision = { ...pass.decision }; if (pass.policyRewrite !== undefined) { pass.currentInput = pass.policyRewrite; pass.req.args = pass.policyRewrite; } const ruleMayClear = () => ruleClearanceOf(pass.decision.action === "ask" ? pass.decision : undefined, mandateMarksNow()).ruleMayClear; const classMayClear = () => pass.decision.action === "ask" && classifyAsk(pass.decision, askOriginFacts(orgAnswer.org, ruleCallUnreadableOf(laneAnswer))).row.ruleMayClear; let personalEvidence = { dotsAbsent: input.persistedRules === undefined ? "not_wired" : "not_adjudicated" }; if (laneAnswer?.unreadable === true) personalEvidence = { dotsAbsent: "unavailable" }; let laneCoverage; const questionTool = wireOf("lanes.allowLayerQuestionSkip", pass.req) === ASK_USER_QUESTION_TOOL_NAME; const allowLayerMayClear = () => !orgRealApprovalRequired && pass.decision.action === "ask" && pass.decision.requiresRealApproval !== true && pass.decision.decisionReason !== "hook" && !questionTool && !markedUnresolvable(); const unionDotsOf = (rules) => { const seen = new Set(); const union = []; for (const r of rules) { if (r.dots === undefined || r.dots.length === 0) return { dotsAbsent: "not_reported" }; for (const d of r.dots) { const key = JSON.stringify([d.actor, d.counter]); if (!seen.has(key)) (seen.add(key), union.push(d)); } } return { dots: union }; }; if (input.persistedRules && allowLayerMayClear()) { const answer = laneAnswer ?? {}; laneCoverage = answer.coverage; personalEvidence = answer.hit?.behavior === "ask" ? unionDotsOf(answer.hit.rules) : { dotsAbsent: answer.unreadable === true ? "unavailable" : "no_match" }; } const afterRuleClear = () => { if (input.permissionMode === undefined || input.permissionMode() === "auto") input.autoMode?.denialTracking?.recordAllow(); }; const mode = input.permissionMode?.(); const rewrite = pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}; const listAllow = (decisionReason, whose, entries) => ({ action: "allow", message: `${whose} allowedTools (${entries.map((e) => inlineUntrusted(e, 200)).join(", ")}) ${entries.length === 1 ? "covers" : "cover"} this call`, decisionReason, ...rewrite, }); const storedAllows = laneAnswer?.hit?.behavior === "allow" ? laneAnswer.hit.rules : undefined; const members = [ ["hook", () => (pass.hookAllowed === true && pass.policyRewrite === undefined ? { entries: [], clear: () => ({ action: "allow", message: "a PreToolUse hook allowed this call", decisionReason: "hook" }), afterClear: async () => undefined } : undefined)], ["deployment", () => { const hit = input.deploymentAllows?.allowing(pass.req, mode); return hit && { entries: hit.entries, clear: () => listAllow("deployment_rule", "the deployment's", hit.entries), afterClear: () => notifier.notifyAsync(() => input.deploymentAllows?.onAllowed?.({ toolName: pass.req.toolName, toolCallId, entries: hit.entries }), "toolGate.allowedToolsResolved") }; }], ["persisted", () => storedAllows && { entries: storedAllows.map((r) => r.rule), clear: () => ({ action: "allow", message: storedAllows.length === 1 ? `a persisted allow rule (${disclosedRuleSet(storedAllows)}) covers this call` : `persisted allow rules (${disclosedRuleSet(storedAllows)}) cover every segment of this call`, decisionReason: "persisted_rule", ...rewrite }), afterClear: () => notifier.notifyAsync(() => input.persistedRules?.onResolved?.({ toolName: pass.req.toolName, toolCallId, rules: storedAllows.map((r) => r.rule) }), "toolGate.persistedRuleResolved"), shadowed: () => (personalEvidence = unionDotsOf(storedAllows)), }], ["skill", () => { const hit = input.skillGrants?.allowing(pass.req, mode); return hit && { entries: hit.entries, clear: () => skillAllowDecision(hit, pass.policyRewrite), afterClear: () => notifier.notifyAsync(() => input.skillGrants?.onAllowed?.({ toolName: pass.req.toolName, toolCallId, ...hit }), "toolGate.skillGrantResolved") }; }], ["request", () => { const hit = input.requestAllows?.allowing(pass.req, mode); return hit && { entries: hit.entries, clear: () => listAllow("request_rule", "this run's", hit.entries), afterClear: () => notifier.notifyAsync(() => input.requestAllows?.onAllowed?.({ toolName: pass.req.toolName, toolCallId, entries: hit.entries }), "toolGate.allowedToolsResolved") }; }], ]; const shadows = []; if (!questionTool) { for (const [member, match] of members) { if (pass.decision.action !== "ask") break; const hit = match(); if (hit === undefined) continue; if (allowLayerMayClear() && ruleMayClear() && classMayClear()) { pass.decision = hit.clear(); await hit.afterClear(); if (member !== "hook") afterRuleClear(); break; } shadows.push(allowRuleShadowOf(member, hit.entries)); hit.shadowed?.(); } } if (pass.decision.action === "ask") { const { allowRuleShadowed: _claimed, ...standing } = pass.decision; pass.decision = shadows.length > 0 ? { ...standing, allowRuleShadowed: shadows } : standing; } const originFacts = askOriginFacts(orgAnswer.org, ruleCallUnreadableOf(laneAnswer)); if (pass.decision.action === "ask") { const stamped = { ...pass.decision, ruleEvidence: mintRuleEvidence(personalEvidence), ...(laneCoverage !== undefined ? { segmentCoverage: laneCoverage } : {}) }; pass.decision = stampAskOrigin(stamped, originFacts, classifyStanding(stamped, originFacts)); } if (allowLayerMayClear() && pass.decision.action === "ask" && ruleMayClear() && classMayClear() && pass.probeBoundaryDeclined !== true && wireOf("lanes.readOnlyShellAllow", pass.req) === COMMAND_RULE_TOOL) { const commandParam = catalogRuleFaceOf(COMMAND_RULE_TOOL)?.primaryParams[0] ?? "command"; const args = pass.req.args; const command = typeof args === "object" && args !== null && !Array.isArray(args) ? args[commandParam] : undefined; const backgrounded = typeof args === "object" && args !== null && args.run_in_background === true; const shape = typeof command === "string" ? readShellCommand(command) : undefined; if (typeof command === "string" && shape !== undefined && !backgrounded && readOnlyShellVerdict(command, shape, input.trackedCwd?.()).readOnly) { pass.decision = { action: "allow", message: "the command is read-only (upstream's read-only command tables) — allowed without asking", decisionReason: "read_only", ...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}), }; await notifier.notifyAsync(() => input.onReadOnlyAllowed?.({ toolName: pass.req.toolName, toolCallId, command }), "toolGate.readOnlyAllowed"); afterRuleClear(); } else if (shape !== undefined && pass.tightenedBy === "shell_gate" && changesDirectoryBeforeGit(shape, input.trackedCwd?.())) { pass.decision = { ...pass.decision, message: CD_GIT_COMPOUND_SENTENCE }; } } if (pass.decision.action === "ask") ledger.supersede(); if (input.autoMode && pass.decision.action === "ask" && pass.decision.origin !== undefined && pass.askClassification?.row.classifierEligible === true && (input.permissionMode === undefined || input.permissionMode() === "auto")) { const classifySignal = callSignal ?? input.abortSignal; const heard = await input.autoMode.decider .decide({ req: pass.req, askMessage: decisionText(pass.decision) }, classifySignal) .catch(() => ({ kind: "unavailable", cause: "error" })); const verdict = heard.kind === "allow" && input.permissionMode !== undefined && input.permissionMode() !== "auto" ? undefined : heard; if (verdict !== undefined) pass.classifierRound = verdict.classifier; if (verdict?.kind === "allow") { input.autoMode.denialTracking?.recordAllow(); pass.decision = { action: "allow", message: "auto-mode classifier allowed this call", decisionReason: "classifier", ...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}), }; } else if (verdict?.kind === "unavailable") { const arm = classifierUnavailableArm({ ask: pass.decision, req: pass.req, cause: verdict.cause, attribution: verdict.classifier, seat: input.autoMode.unavailable, aborted: classifySignal?.aborted === true }); if (arm.kind === "asked") { pass.decision = stampAskOrigin(arm.ask, originFacts, classifyStanding(arm.ask, originFacts)); pass.classifierRound = undefined; } else pass.decision = arm.deny; } else if (verdict !== undefined) { const reason = verdict.kind === "block" ? (verdict.reason ? inlineUntrusted(verdict.reason) : "") : classifierParseFailureDenyMessage(verdict.classifier); const category = verdict.kind === "block" && verdict.category ? inlineUntrusted(verdict.category) : ""; const tracked = input.autoMode.denialTracking?.recordBlock(); if (tracked?.limitReached === true) { const fallbackAsk = { ...pass.decision, message: denialLimitFallbackMessage(tracked.fallback, reason || category || toolName), decisionReason: "classifier", requiresRealApproval: true, denialLimitFallback: tracked.fallback, }; pass.decision = stampAskOrigin(fallbackAsk, originFacts, classifyStanding(fallbackAsk, originFacts)); } else { const denied = { action: "deny", message: verdict.kind === "block" ? `auto-mode classifier blocked this call${reason ? `: ${reason}` : category ? `: [${category}]` : ""}` : reason, decisionReason: "classifier", }; pass.decision = verdict.kind === "parse_error" ? attestClassifierDenyCause(denied, "parse_error", pass.req) : denied; } } if (pass.decision.action === "deny") { pass.deniedBy = "classifier"; pass.denyCause = classifierDenyCauseOf(pass.decision, pass.req); } } if (input.sandboxAdmission !== undefined && pass.decision.action === "ask" && wireOf("lanes.sandboxQuestionSkip", pass.req) !== ASK_USER_QUESTION_TOOL_NAME && input.isMarkedUnresolvable?.(input.event.toolCallId) !== true && pass.decision.requiresRealApproval !== true && pass.decision.decisionReason !== "hook" && pass.decision.origin !== "ask_rule" && pass.hookAsk === undefined && input.egress !== true && input.peerMessage !== true && pass.askClassification?.row.sandboxAdmission === true && pass.probeBoundaryDeclined !== true && input.sandboxAdmission.boundaryCapable !== true) { const recorded = input.sandboxAdmission.askClassesOf(toolCallId); const admissible = recorded !== undefined && recorded.length > 0 && recorded.every((r) => r.cls === "sandbox_local"); if (admissible) { const askClasses = recorded; input.sandboxAdmission.onAdmitted({ toolCallId, toolName, askClasses, boundary: "sandbox_internal" }); pass.preToolContext.push(`permission.sandbox_admitted: this call's pending approval was auto-admitted because the deployment declares an isolated sandbox execution env and the request stays inside it. Recorded for audit.`); pass.decision = { action: "allow", message: "admitted inside the deployment-declared sandbox (permission.sandbox_admitted)", decisionReason: "sandbox", ...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}), }; } } if (pass.decision.action === "ask" && input.permissionMode !== undefined && pass.askClassification !== undefined) { const standing = pass.decision; const mode = input.permissionMode(); const fileCommandByWord = modeAcceptsFileCommand(mode, wireOf("lanes.modeFileCommand", pass.req), pass.policyRewrite ?? pass.currentInput); const verdict = arbitrateByMode(mode, pass.askClassification, { ...(standing.origin !== undefined ? { origin: standing.origin } : {}), ...(standing.mandate !== undefined ? { mandate: standing.mandate } : {}) }, { fileCommand: pass.shellWritesPlaced === true && fileCommandByWord }); if (verdict.kind === "ask" && fileCommandByWord && pass.shellWritesPlaced !== true && pass.shellUnplacedOptions !== undefined && pass.askClassification.classes.size === 1 && pass.askClassification.classes.has("ordinary")) { pass.decision = { ...standing, message: fileCommandOptionsSentence(pass.shellUnplacedOptions.program) }; } if (verdict.kind === "accepted") { ledger.supersede(); pass.decision = { action: "allow", message: `accepted by the ${verdict.mode} permission mode (one of its file commands)`, decisionReason: "mode", ...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}) }; } else if (verdict.kind === "released") { pass.arbitration = verdict; ledger.supersede(); await notifier.notifyAsync(() => input.onModeReleased?.({ toolName, toolCallId, release: { mode: verdict.mode, askClass: verdict.askClass, classes: verdict.classes, ...(verdict.origin !== undefined ? { origin: verdict.origin } : {}), ...(verdict.mandate !== undefined ? { mandate: verdict.mandate } : {}), ...(verdict.reason !== undefined ? { reason: verdict.reason } : {}) }, ...(pass.shellReadUnestablished === true ? { readUnestablished: true } : {}) }), "toolGate.onModeReleased"); pass.decision = { action: "allow", message: `released by the ${verdict.mode} permission mode (the ${verdict.askClass} ask was not put to anyone)`, decisionReason: "mode", ...(pass.policyRewrite !== undefined ? { updatedInput: pass.policyRewrite } : {}), }; } else if (verdict.kind === "denied_by_mode") { pass.arbitration = verdict; pass.deniedBy = "mode"; pass.denyCause = undefined; pass.decision = { action: "deny", message: verdict.mode === "dontAsk" ? `refused by the dontAsk permission mode: this call needed approval (${verdict.askClass}) and the mode answers no ask; run under a mode with an approver, or change the call` : `refused by the bypassPermissions permission mode: the write-protection judge could not resolve this write's target, and with nobody to ask the mode refuses rather than releases it`, decisionReason: "mode", }; } } const safety = safetyAxisOf({ egress: input.egress, shellGated: input.shellGated, irreversibility: input.irreversibility, probeMandated: pass.decision.action === "ask" && pass.decision.probeMandated === true, probeUnanswered: pass.probeUnanswered === true, tightened: pass.tightenedBy, }); const realApprovalOf = (d) => { if (d.action !== "ask" || d.requiresRealApproval !== true) return undefined; if (d.origin === undefined) throw new Error(`a real-approval ask for "${toolName}" reached a park site with no origin word — the origin stamp precedes every park`); return { origin: d.origin, ...(originFacts.org !== undefined ? { org: originFacts.org } : {}) }; }; const carryOf = (d) => ({ ...(callSignal !== undefined ? { signal: callSignal } : {}), ...(d.action === "ask" && d.denialLimitFallback !== undefined ? { denialLimitFallback: unarmedWindow(d.denialLimitFallback) } : {}), ...(d.action === "ask" && d.origin !== undefined ? { origin: d.origin } : {}), ...(d.action === "ask" && d.askClass !== undefined && d.classes !== undefined ? { askClass: d.askClass, classes: d.classes } : {}), ...(d.action === "ask" && d.mandated === true ? { mandated: true, ...(d.mandate !== undefined ? { mandate: d.mandate } : {}) } : {}), ...(d.action === "ask" && d.authoredBy === "deployment" ? { authoredBy: "deployment" } : {}), }); if (suspendAsk && pass.decision.action === "ask") { const parkArgs = [pass.req, pass.currentInput, safety, undefined, realApprovalOf(pass.decision), pass.decision.action === "ask" ? pass.decision.allowRuleShadowed : undefined, pass.decision.action === "ask" ? pass.decision.decisionReason : undefined, pass.decision.action === "ask" ? pass.decision.probeReason : undefined, pass.decision.action === "ask" ? pass.decision.probeCause : undefined, pass.decision.action === "ask" ? pass.decision.segmentCoverage : undefined, pass.decision.action === "ask" ? pass.decision.matchedAskRule : undefined, pass.decision.action === "ask" ? pass.decision.probeMandated : undefined]; const suspended = await parkWith(suspendAsk, parkArgs, carryOf(pass.decision)); if (suspended) { if ("parkFailed" in suspended) pass.parkFailed = suspended.parkFailed; else return { suspend: suspended, preToolContext: pass.preToolContext }; } } if (pass.decision.action === "ask" && wireOf("lanes.contentAskRoute", pass.req) === ASK_USER_QUESTION_TOOL_NAME) { const outcome = input.resolveContentAsk !== undefined ? await (callSignal !== undefined ? input.resolveContentAsk(pass.req, callSignal) : input.resolveContentAsk(pass.req)) : { kind: "unavailable", parkDeclined: false }; if (outcome.kind === "answered") { pass.decision = { action: "allow", updatedInput: outcome.presentedInput }; } else if (outcome.kind === "unavailable") { if (outcome.presentedInput !== undefined) { pass.currentInput = outcome.presentedInput; pass.req.args = outcome.presentedInput; } if (suspendAsk && outcome.parkDeclined && pass.parkFailed === undefined) { const parkArgs = [pass.req, pass.currentInput, safety, true, realApprovalOf(pass.decision), pass.decision.action === "ask" ? pass.decision.allowRuleShadowed : undefined, pass.decision.action === "ask" ? pass.decision.decisionReason : undefined, pass.decision.action === "ask" ? pass.decision.probeReason : undefined, pass.decision.action === "ask" ? pass.decision.probeCause : undefined, pass.decision.action === "ask" ? pass.decision.segmentCoverage : undefined, pass.decision.action === "ask" ? pass.decision.matchedAskRule : undefined, pass.decision.action === "ask" ? pass.decision.probeMandated : undefined]; const suspended = await parkWith(suspendAsk, parkArgs, carryOf(pass.decision)); if (suspended) { if ("parkFailed" in suspended) pass.parkFailed = suspended.parkFailed; else return { suspend: suspended, preToolContext: pass.preToolContext }; } } if (pass.decision.origin === undefined) throw new Error(`the content ask for "${toolName}" reached its refusal without an origin word`); ledger.set({ settlement: { kind: input.resolveContentAsk !== undefined ? "approver_unavailable" : "no_approver", who: { party: "none" }, when: Date.now() }, origin: pass.decision.origin }); pass.deniedBy = "ask_resolution"; pass.decision = { action: "deny", message: "AskUserQuestion could not be parked for a durable human answer on this path (no checkpoint " + "facility, or the suspend failed) and a synchronous permission approver cannot carry an answer — " + "the question was NOT shown to anyone. Proceed with your best judgment and state the assumption " + "you made.", }; } else { if (outcome.presentedInput !== undefined) pass.decision = { action: "allow", updatedInput: outcome.presentedInput }; else pass.decision = { action: "allow" }; } } if (pass.decision.action === "ask") { const askBeforeResolve = pass.decision; const resolved = await (callSignal !== undefined ? resolveAsk(pass.decision, pass.req, callSignal) : resolveAsk(pass.decision, pass.req)); ledger.fromResolved(resolved, askBeforeResolve, toolName); if (resolved.action === "deny") pass.deniedBy = "ask_resolution"; pass.decision = resolved; if (resolved.action === "deny" && resolved.approverUnavailable === true && suspendAsk && pass.parkFailed === undefined) { const parkArgs = [pass.req, pass.currentInput, safety, true, realApprovalOf(askBeforeResolve), askBeforeResolve.action === "ask" ? askBeforeResolve.allowRuleShadowed : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.decisionReason : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.probeReason : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.probeCause : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.segmentCoverage : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.matchedAskRule : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.probeMandated : undefined]; const suspended = await parkWith(suspendAsk, parkArgs, carryOf(askBeforeResolve)); if (suspended) { if ("parkFailed" in suspended) pass.parkFailed = suspended.parkFailed; else return { suspend: suspended, preToolContext: pass.preToolContext }; } } if (askBeforeResolve.action === "ask" && askBeforeResolve.denialLimitFallback !== undefined) { if (pass.decision.action === "allow") { input.autoMode?.denialTracking?.recordAllow(); } else if ((resolved.action === "deny" && (SETTLEMENT_NOBODY_JUDGED[resolved.settlement.kind] || resolved.settlement.kind === "approver_unavailable")) || resolved.approverUnavailable === true) { try { input.onHeadlessDenialLimit?.({ toolName, toolCallId, fallback: unarmedWindow(askBeforeResolve.denialLimitFallback) }); } catch { } } } if (pass.decision.action === "allow" && pass.decision.updatedInput !== undefined) { let editArgs = pass.decision.updatedInput; let editDenied; let editRewrittenSinceHuman = false; for (let round = 0;; round++) { if (round >= 3) { editDenied = { action: "deny", message: `the approval-edit chain for "${toolName}" exceeded the re-approval cap — denied fail-closed; re-submit the edited action directly`, }; pass.deniedBy = "policy"; break; } if (screening !== undefined) { let hr; let hrUnanswered; try { const seat = await screening.screen(editArgs); if (seat.expired) hrUnanswered = seat.cause; else hr = screening.screened(seat.value); } catch (err) { traceHookCrash(input, err, notifier); editDenied = { action: "deny", message: screening.crashReason(`the approved edit for "${toolName}"`, err) }; pass.deniedBy = "hook"; break; } if (hrUnanswered !== undefined) { editDenied = { action: "deny", message: await screening.unansweredReason(`the approved edit for "${toolName}"`, hrUnanswered) }; pass.deniedBy = "hook"; break; } if (hr) { if (hr.additionalContext) pass.preToolContext.push(hr.additionalContext); if (hr.action === "deny") { editDenied = { action: "deny", message: decisionText(hr) ?? `the approved edit for "${toolName}" was blocked by a PreToolUse hook` }; pass.deniedBy = "hook"; break; } if (hr.action === "ask") { editDenied = { action: "deny", message: `the approved edit for "${toolName}" raises a new PreToolUse approval requirement the original approval does not cover — denied fail-closed; re-submit the edited action directly`, }; pass.deniedBy = "hook"; break; } if (hr.updatedInput !== undefined) { editArgs = hr.updatedInput; editRewrittenSinceHuman = true; } } } let recheck = { action: "allow" }; if (adjudicate) { try { const editReq = { toolName, args: editArgs, toolCallId, ...(pass.req.face !== undefined ? { face: pass.req.face } : {}) }; recheck = refuseOutOfContractDecision(await (callSignal !== undefined ? adjudicate(editReq, callSignal) : adjudicate(editReq))); } catch (err) { recheck = { action: "deny", message: `policy re-check of the approved edit for "${toolName}" errored: ${thrownText(err)}`, }; } } { const attributed = ledger.classifierRoundOf(recheck); if (attributed !== undefined) pass.classifierRound = attributed; const refusedBy = ledger.fromDecision(recheck); if (recheck.action === "deny" && refusedBy !== undefined) { editDenied = recheck; pass.deniedBy = refusedBy; pass.denyCause = ledger.causeOf(recheck); break; } } if (recheck.updatedInput !== undefined) { editArgs = recheck.updatedInput; editRewrittenSinceHuman = true; } const editOrg = await applyOrgLayer(recheck, editArgs); recheck = editOrg.decision; if (recheck.action === "deny") { editDenied = recheck; break; } let editRuleCallUnreadable = false; if (input.persistedRules !== undefined && wireOf("lanes.persistedEditSkip", pass.req) !== ASK_USER_QUESTION_TOOL_NAME) { const editLane = await readPersistedRules({ toolName, args: editArgs, toolCallId, ...(pass.req.face !== undefined ? { face: pass.req.face } : {}) }); editRuleCallUnreadable = ruleCallUnreadableOf(editLane); const editTightened = applyPersistedTightening(recheck, editLane); recheck = editTightened.decision; if (editTightened.tightened === "deny") pass.deniedBy = "persisted_rule"; if (recheck.action === "deny") { editDenied = recheck; break; } } const editSkillRefusal = skillRowRefusal(input.skillGrants, { toolName, args: editArgs, toolCallId, ...(pass.req.face !== undefined ? { face: pass.req.face } : {}) }); if (editSkillRefusal !== undefined) { editDenied = editSkillRefusal; pass.deniedBy = "policy"; break; } if (recheck.action === "allow") { if (recheck.updatedInput !== undefined) { editArgs = recheck.updatedInput; editRewrittenSinceHuman = true; } if (!editRewrittenSinceHuman || !asksUnstated(recheck, permissionSeatAt(input, editArgs)?.seat, input.permissionMode?.())) break; recheck = { action: "ask", message: permissionSeatAt(input, editArgs)?.question ?? unstatedAskMessage(toolName) }; pass.tightenedBy = "unstated"; } if (recheck.updatedInput !== undefined) { editArgs = recheck.updatedInput; editRewrittenSinceHuman = true; } const editAskSnapshot = { ...recheck, ruleEvidence: mintRuleEvidence({ dotsAbsent: "not_adjudicated" }) }; markedThisRound = undefined; const editFacts = askOriginFacts(editOrg.org, editRuleCallUnreadable); const editAskDecision = stampAskOrigin(editAskSnapshot, editFacts, classifyStanding(editAskSnapshot, editFacts)); if (input.permissionMode !== undefined && pass.askClassification !== undefined) { const editVerdict = arbitrateByMode(input.permissionMode(), pass.askClassification, { ...(editAskDecision.origin !== undefined ? { origin: editAskDecision.origin } : {}), ...(editAskDecision.mandate !== undefined ? { mandate: editAskDecision.mandate } : {}) }); if (editVerdict.kind === "released") { ledger.supersede(); pass.arbitration = editVerdict; await notifier.notifyAsync(() => input.onModeReleased?.({ toolName, toolCallId, release: { mode: editVerdict.mode, askClass: editVerdict.askClass, classes: editVerdict.classes, ...(editVerdict.origin !== undefined ? { origin: editVerdict.origin } : {}), ...(editVerdict.mandate !== undefined ? { mandate: editVerdict.mandate } : {}), ...(editVerdict.reason !== undefined ? { reason: editVerdict.reason } : {}) }, ...(pass.shellReadUnestablished === true ? { readUnestablished: true } : {}) }), "toolGate.onModeReleased"); break; } if (editVerdict.kind === "denied_by_mode") { ledger.supersede(); pass.arbitration = editVerdict; editDenied = { action: "deny", message: `the approved edit for "${toolName}" raised a new approval requirement that the ${editVerdict.mode} permission mode refuses`, decisionReason: "mode" }; pass.deniedBy = "mode"; break; } } const editAskReq = { toolName, args: editArgs, toolCallId, ...(pass.req.face !== undefined ? { face: pass.req.face } : {}) }; const rr = await (callSignal !== undefined ? resolveAsk(editAskDecision, editAskReq, callSignal) : resolveAsk(editAskDecision, editAskReq)); ledger.fromResolved(rr, editAskDecision, toolName); if (rr.action !== "allow") { editDenied = rr; pass.deniedBy = "ask_resolution"; break; } if (rr.updatedInput === undefined) { if (rr.presentedInput !== undefined) editArgs = rr.presentedInput; editRewrittenSinceHuman = false; break; } editArgs = rr.updatedInput; editRewrittenSinceHuman = false; } if (editDenied === undefined && editRewrittenSinceHuman && input.writeProtectionCheck !== undefined) { const judged = input.writeProtectionCheck(toolName, editArgs, pass.req.face, input.trackedCwd?.(), callSignal ?? input.abortSignal); const verdict = judged === null ? undefined : await awaitWriteProtectionJudgment(judged, boundedAwaitDeadlineMs(), callSignal ?? input.abortSignal); if (verdict !== undefined && verdict.kind !== "clear") { editDenied = { action: "deny", message: verdict.kind === "hit" ? `the approved edit for "${toolName}" was rewritten by the restriction chain onto a write-protected path (table entry "${verdict.hit.name}") that no approval covers — denied fail-closed; re-submit the edited action directly` : `the approved edit for "${toolName}" was rewritten by the restriction chain onto a target whose real location could not be resolved (${verdict.cause}) — denied fail-closed; re-submit the edited action directly`, }; pass.deniedBy = "write_protection"; } } if (editDenied === undefined && input.shellReadCheck !== undefined) { const station = input.shellReadCheck; const judged = station.judge(pass.req, editArgs, input.trackedCwd?.(), callSignal ?? input.abortSignal, { seat: "rejudge", toolCallId }); if (judged !== null) { let verdict; try { verdict = await withGateTimeout(judged, boundedAwaitDeadlineMs(), callSignal ?? input.abortSignal, "shellReadStation"); } catch { verdict = station.unanswered(pass.req, "the read station could not answer"); } if (verdict.disposition !== "vouch") { editDenied = { action: "deny", message: verdict.sentence ?? `the approved edit for "${toolName}" reads where the read boundary does not vouch — denied fail-closed; re-submit the command as a fresh call and answer its card`, }; pass.deniedBy = "read_boundary"; if (verdict.details !== undefined) pass.denyDetails = verdict.details; } } } pass.decision = editDenied ?? { ...pass.decision, updatedInput: editArgs }; } else if (pass.decision.action === "allow" && pass.decision.updatedInput === undefined && resolved.presentedInput !== undefined) { pass.decision = { ...pass.decision, updatedInput: resolved.presentedInput }; } } } return await exitGate(pass); }