@sema-agent/core
Version:
Stateless, task-oriented AI agent core
674 lines (673 loc) • 44.8 kB
JavaScript
import { policyAskClassOf } from "../ask-class.js";
import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
import { classifierUnavailableSeat } from "./classifier-ladder.js";
import { resolveCheckpointStore } from "../checkpoint-store.js";
import { cloneObserverInput, resolveHookTimeoutMs } from "../hooks.js";
import { assertHookSeatsCallable } from "../hook-seats.js";
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "../permission-rules.js";
import { isIsolated } from "../remote-env.js";
import { askApproverIdentity, carryEngineAttestations, carryPrecedingSettlement, checkToolPolicyProjection, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, durableParkOptInDeclared, isLiveApproverSeat, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, wrapToolPolicy, attestAskUnresolvableCause } from "../tool-policy.js";
import { judgeRuleToolName } from "../tool-name-aliases.js";
import { NAMESPACED_NAME_SHAPES } from "../protocol-table.js";
import { emitTrace } from "../trace.js";
import { autoStrippedRule, cardDisclosureOf } from "../allow-entry.js";
import { arbitrateByMode, askClassMembersOf, askMandateMember } from "../ask-origin.js";
import { createModeGatePolicy, mountedToolDeclaresReadOnly } from "../mode-policy.js";
const ancestorFacts = (ask) => ({ ...(ask ?? {}), ancestorResolved: true });
import { headlessDenyAtFold, headlessDenyAtRecheck, INHERITED_STATION_FACTS, inheritedAskCarry, inheritedAskFallbackMessage, judgeInheritedClassifier, lateAskSettlementObserver, settleDenialLimitFallback } from "./denial-limit-arms.js";
import { settleReadRootGrant } from "./read-root-grant.js";
import { askGrantShapeOf } from "./inherited-ask-grants.js";
import { createPermissionRuleLanes, inheritedAskRuleEvidence, createRuleOffersOf } from "./permission-rule-lanes.js";
import { createLiveSessionRules, createSessionRulePolicy } from "./session-rule-policy.js";
import { thrownText } from "../thrown-value.js";
import { heldBy, heldValues, seatOf, wireOf } from "../tool-name-readings.js";
export async function preparePolicyChain(input) {
const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, permissionMode, scratchpadDir, toolRosterDeltas, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
const ancestorArbitrations = new Map();
const arbitrateInherited = (ask, toolCallId, judged) => {
const verdict = arbitrateByMode(permissionMode.current, judged.classification, { origin: judged.origin, ...askMandateMember(ancestorFacts(judged.ask.action === "ask" ? judged.ask : undefined)) });
if (verdict.kind === "ask" || verdict.kind === "accepted")
return undefined;
ancestorArbitrations.set(toolCallId, verdict);
if (verdict.kind === "released")
return { action: "allow", message: `released by the ${verdict.mode} permission mode at an inherited ancestor layer (the ${verdict.askClass} ask was not put to anyone)`, decisionReason: "mode", ...(ask.updatedInput !== undefined ? { updatedInput: ask.updatedInput } : {}) };
return { action: "deny", message: `refused by the ${verdict.mode} permission mode at an inherited ancestor layer (${verdict.askClass})`, decisionReason: "mode" };
};
const policy = lockedPreflight.toolPolicy;
const auditPolicyNames = (auditee) => {
const nameGroups = toolPolicyNameSets(auditee);
if (nameGroups.length > 0) {
const known = new Set(tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
for (const t of harnessTools)
known.add(t.name);
const unmatched = new Set();
for (const g of nameGroups) {
for (const [kind, list] of [["deny", g.deny], ["ask", g.ask], ["allow", g.allow]]) {
for (const n of list ?? []) {
const verdict = judgeRuleToolName(n, known);
if (verdict.kind !== "current") {
const err = new Error(verdict.kind === "retired"
? `tool policy ${kind}-list entry "${n}" is a RETIRED tool name (${verdict.guidance}) and matches nothing in this run's roster — legacy-name normalization was removed (RB-476-A), so this entry would silently guard nothing. Update the deployment's rule to the current name.`
: `tool policy ${kind}-list entry "${n}" carries the "__" namespace separator but no protocol prefix, and matches nothing in this run's roster — protocol tools are named ${NAMESPACED_NAME_SHAPES}, and legacy-name normalization was removed (RB-476-A), so this entry would silently guard nothing. Prefix the entry with the owning protocol's marker.`);
err.code = "config.legacy_tool_name";
throw err;
}
if (known.has(n))
continue;
if (isNamespacedCoveringRuleName(n) && [...known].some((k) => namespacedRuleNameCovers(n, k)))
continue;
unmatched.add(`"${n}" (${kind})`);
}
}
}
const dedupeKey = [...unmatched].sort().join("|");
const advised = advisedPolicyNames.get(deps) ?? new Set();
if (unmatched.size > 0 && !advised.has(dedupeKey)) {
advised.add(dedupeKey);
if (advised.size > ADVISED_KEYS_CAP) {
const oldest = advised.values().next().value;
if (oldest !== undefined)
advised.delete(oldest);
}
advisedPolicyNames.set(deps, advised);
try {
const e = new Error(`toolPolicy name audit: ${[...unmatched].join(", ")} match(es) no tool mounted this run (primary or alias) — the rule cannot fire here. Often a typo or a self-report/wire name skew (e.g. TodoWrite vs the mounted TaskCreate family; "*" has no wildcard meaning in a name-keyed policy). Advisory only: enforcement is unchanged, and a name can be legitimate for another scenario or a per-spawn tool.`);
e.code = "config.toolpolicy.unmatched_names";
deps.onError?.(e, { phase: "config", sessionId });
}
catch {
}
}
}
};
auditPolicyNames(policy);
const announceSessionRulesUnreadable = (reason) => {
try {
const e = new Error(`session permission rules: ${reason} — the snapshot this leg already enforces stays in force (a failed read never loosens it)`);
e.code = "config.session_rules_unreadable";
deps.onError?.(e, { phase: "config", sessionId });
}
catch {
}
};
let sessionRulePolicy;
if (deps.sessionPolicyStore && spec.sessionId) {
const store = deps.sessionPolicyStore;
const sessionRuleKey = { sessionId: spec.sessionId, ...(spec.principal !== undefined ? { principal: spec.principal } : {}) };
const rules = await store.getRules(spec.sessionId, spec.principal);
const seed = rules ?? { rev: 0 };
const publishOwnRules = (row) => {
ownSessionRulesRef.current = {
sessionId: sessionRuleKey.sessionId,
...(sessionRuleKey.principal !== undefined ? { principal: sessionRuleKey.principal } : {}),
rev: row.rev,
...(row.gen !== undefined ? { gen: row.gen } : {}),
rules: row,
};
};
sessionRulePolicy = createSessionRulePolicy({ seed, read: createLiveSessionRules(store, { ...sessionRuleKey, seed }, { onUnreadable: announceSessionRulesUnreadable, onEnforced: publishOwnRules }) }, { env: executionEnv, rootPath: taskRootFinal, toolEffects });
if (rules)
publishOwnRules(rules);
}
const ancestorRulePolicies = [];
for (const [position, snap] of (inheritedAncestorRules ?? []).entries()) {
const seed = { ...snap.rules, rev: snap.rev, ...(snap.gen !== undefined ? { gen: snap.gen } : {}) };
const publishAncestorRules = (row) => {
ancestorSessionRulesRef.current.set(position, {
sessionId: snap.sessionId,
...(snap.principal !== undefined ? { principal: snap.principal } : {}),
rev: row.rev,
...(row.gen !== undefined ? { gen: row.gen } : {}),
rules: row,
});
};
ancestorRulePolicies.push(createSessionRulePolicy({
seed,
...(deps.sessionPolicyStore
? { read: createLiveSessionRules(deps.sessionPolicyStore, { sessionId: snap.sessionId, ...(snap.principal !== undefined ? { principal: snap.principal } : {}), seed }, { onUnreadable: announceSessionRulesUnreadable, onEnforced: publishAncestorRules }) }
: {}),
}, { env: executionEnv, rootPath: taskRootFinal, toolEffects }));
}
const frozenConstraintLayers = (seedInheritedGate?.constraintChain ?? [])
.filter((e) => !("opaque" in e))
.map((entry) => ({
check: (creq) => checkToolPolicyProjection(entry, creq) ?? { action: "allow" },
}));
const narrowingLayers = [sessionRulePolicy, ...ancestorRulePolicies, ...frozenConstraintLayers].filter((p) => p !== undefined);
for (const layer of narrowingLayers)
auditPolicyNames(layer);
const denyNarrowingPolicy = narrowingLayers.length === 0 ? undefined : narrowingLayers.length === 1 ? narrowingLayers[0] : combinePolicies(...narrowingLayers);
const basePolicyForResumeEdit = lockedPreflight.basePolicyForResumeEdit ?? lockedPreflight.toolPolicy;
const hooks = spec.hooks ?? deps.hooks;
assertHookSeatsCallable(hooks, spec.hooks !== undefined ? "spec" : "deps");
const hookTimeoutMs = resolveHookTimeoutMs(hooks?.timeoutMs, notifyOwnHookCrash, hooks);
const preToolUseObservational = hooks?.preToolUse !== undefined && hooks.preToolUseObservational === true;
const ownGatePreToolUse = !preToolUseObservational
? hooks?.preToolUse
: async (toolName, input, ctx) => {
const r = await hooks.preToolUse(toolName, cloneObserverInput(input), ctx);
if (r === undefined)
return undefined;
try {
const action = typeof r.action === "string" ? r.action : "(non-string action)";
deps.onError?.(new Error(`a PreToolUse hook declared observational (Hooks.preToolUseObservational) returned a decision ("${action}") while screening ` +
`"${toolName}" — the decision was NOT adopted and the call proceeded as if the hook had no opinion. ` +
`A declared-observational face must return undefined; drop the declaration if its verdicts are meant to count ` +
`(they then also travel to delegated children as an inherited screening constraint).`), { phase: "hook", sessionId, classification: "observational-hook-verdict-ignored" });
}
catch {
}
return undefined;
};
const sameInstanceAncestorCount = policy === undefined ? 0 : (inheritedParentConstraints ?? []).reduce((n, pc) => (pc.policy === policy ? n + 1 : n), 0);
const sharedFirstDecision = new Map();
const SHARED_FIRST_DECISION_CAP = 256;
const inheritedUnavailableAsks = new Set();
const INHERITED_UNAVAILABLE_CAP = 256;
const markInheritedUnavailable = (toolCallId) => {
if (inheritedUnavailableAsks.size >= INHERITED_UNAVAILABLE_CAP && !inheritedUnavailableAsks.has(toolCallId))
return false;
inheritedUnavailableAsks.add(toolCallId);
return true;
};
const mandatedCallFloats = (toolCallId) => resolveCheckpointStore(spec, deps) !== undefined && durableParkOptInDeclared({ forceDurableGate: runtimeCaps?.forceDurableGate === true, durableApprovalDeclared: spec.durableApproval !== undefined }) && markInheritedUnavailable(toolCallId);
const mandateUnreconstructibleDeny = (creq) => attestAskUnresolvableCause({
action: "deny",
message: `inherited parent policy requires durable approval for "${creq.toolName}" — the parent's durable ask cannot be reconstructed in a delegated child; denied fail-closed (tighten-only)`,
}, "mandate_unreconstructible", creq);
const markerChannelFullDeny = (creq) => attestAskUnresolvableCause({ action: "deny", message: `no approver is reachable for the inherited approval of "${creq.toolName}" and the marker channel is at capacity — denied fail-closed` }, "approver_unavailable", creq);
const inheritedAskGrants = new Map();
const recordInheritedAskGrant = (toolCallId, approver, presented, waitMs, settled, classes) => {
if (typeof approver !== "function")
return;
if (presented === undefined)
return;
if (inheritedAskGrants.size >= INHERITED_UNAVAILABLE_CAP && !inheritedAskGrants.has(toolCallId))
return;
const argsJson = askGrantShapeOf(presented);
if (argsJson === undefined)
return;
inheritedAskGrants.set(toolCallId, { approver, argsJson, presented, waitMs, settled, classes });
};
const sandboxDeclared = isIsolated(executionEnv);
const sandboxAdmissionArmed = (() => {
if (!sandboxDeclared)
return false;
const store = resolveCheckpointStore(spec, deps);
const missing = [];
if (store === undefined)
missing.push("a wired CheckpointStore");
else if (store.redecision?.reopen !== true)
missing.push("a CheckpointStore that DECLARES redecision.reopen (declaration, never duck-typing)");
if (spec.durableApproval === undefined && runtimeCaps?.forceDurableGate !== true)
missing.push("a durable park (spec.durableApproval, or the forceDurableGate entitlement)");
if (missing.length === 0)
return true;
try {
const e = new Error(`sandbox admission mode NOT armed: the execution env declares an isolated sandbox (capabilities.isolation), but the mode's ` +
`park prerequisites are missing — ${missing.join("; ")}. Asks keep the conservative default chain (no auto-admission, no boundary park).`);
e.code = "config.sandbox_admission_unavailable";
deps.onError?.(e, { phase: "config", sessionId, classification: "sandbox-admission-unavailable" });
}
catch {
}
return false;
})();
const foldAskClasses = new Map();
const FOLD_ASK_CLASSES_CAP = 256;
const recordFoldAskClass = (toolCallId, cls, layer) => {
if (!foldAskClasses.has(toolCallId) && foldAskClasses.size >= FOLD_ASK_CLASSES_CAP)
return;
const list = foldAskClasses.get(toolCallId) ?? [];
list.push({ cls, layer });
foldAskClasses.set(toolCallId, list);
};
const isDelegatedQuestion = (creq) => wireOf("inherited.questionFloat", creq) === ASK_USER_QUESTION_TOOL_NAME;
const sandboxBoundaryCapable = (call) => heldBy("gate.sandboxBoundary.egress", egressTools, call) || !envHandToolNames.has(wireOf("gate.sandboxBoundary.hand", call));
const emitSandboxAdmitted = (info) => {
emitTrace(deps.tracer, () => ({
kind: "permission.sandbox_admitted",
version: 1,
taskId: hostTaskId,
toolCallId: info.toolCallId,
toolName: info.toolName,
askClasses: info.askClasses.map((a) => a.cls),
sourceLayers: info.askClasses.map((a) => a.layer),
boundary: info.boundary,
ts: Date.now(),
}));
};
const ancestorSandboxAdmissions = new Map();
const recordAncestorSandboxAdmission = (toolCallId, toolName) => {
const list = ancestorSandboxAdmissions.get(toolCallId);
if (list !== undefined)
list.push({ cls: "sandbox_local", layer: "ancestor_constraint" });
else
ancestorSandboxAdmissions.set(toolCallId, [{ cls: "sandbox_local", layer: "ancestor_constraint" }]);
void toolName;
};
const callerPolicyLayer = policy !== undefined && sameInstanceAncestorCount > 0
? wrapToolPolicy(policy, async (creq, csignal) => {
const decision = refuseOutOfContractDecision(await policy.check(creq, csignal ?? abortController.signal));
sharedFirstDecision.set(creq.toolCallId, decision);
if (sharedFirstDecision.size > SHARED_FIRST_DECISION_CAP) {
const oldest = sharedFirstDecision.keys().next().value;
if (oldest !== undefined)
sharedFirstDecision.delete(oldest);
}
return decision;
})
: policy;
const askSourceIdentity = () => ({
principal: spec.principal,
sourceTaskId: sessionId,
...(internals?.parentTaskId !== undefined ? { fromSubagent: true } : {}),
...((internals?.explicitAgentName ?? internals?.agentName) !== undefined
? { sourceAgentName: internals?.explicitAgentName ?? internals?.agentName }
: {}),
...(delegation.isDelegatedChild ? { isDelegatedChild: true } : {}),
});
const seatMarksOf = (call) => {
const tierSeat = seatOf("report.riskAxes", irreversibilityTier, call);
return {
tier: tierSeat?.value,
egress: heldBy("report.riskAxes.egress", egressTools, call),
shellGated: tierSeat?.shellGated(shellGatedTools, toolMarkSources) === true,
sources: new Set(heldValues("report.markSources", toolMarkSources, call).flatMap((s) => [...s])),
};
};
const riskAxesOf = (call) => {
const seat = seatMarksOf(call);
const negatives = seatOf("report.riskAxes.negatives", axisExplicitNegatives, call)?.value;
const irreversible = seat.tier === "always" || seat.tier === "maybe" ? true : seat.tier === "never" ? false : negatives?.irreversible;
const egress = seat.egress ? true : negatives?.egress;
if (irreversible === undefined && egress === undefined)
return {};
return { riskAxes: { ...(irreversible !== undefined ? { irreversible } : {}), ...(egress !== undefined ? { egress } : {}) } };
};
const inheritedStationFacts = (call) => {
const seat = seatMarksOf(call);
return {
...INHERITED_STATION_FACTS,
mandateMarks: { egress: seat.egress, shellGated: false, ...(seat.tier === "always" && !seat.shellGated ? { irreversibility: "always" } : {}) },
...(seat.sources.size > 0 ? { toolMarkSources: seat.sources } : {}),
...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}),
};
};
const inheritedAskEvidence = inheritedAskRuleEvidence(deps.permissionRuleStore);
const listFacts = { root: taskRootFinal, sessionId, liveCwd: () => handsCwdRef?.current, home: executionEnv.homeDir };
const skillGrantLane = skillGrants.lane(listFacts);
const allowEntryLanes = { request: allowedToolTables.request.lane(listFacts), deployment: allowedToolTables.deployment.lane(listFacts) };
const { personal: permissionRuleLane, org: permissionRuleOrgLane } = createPermissionRuleLanes({
provider: deps.permissionRuleStore,
localOwnerDeclared: deps.localOwnerRules === true,
principal: spec.principal,
sessionId,
root: taskRootFinal,
liveCwd: () => handsCwdRef?.current,
home: executionEnv.homeDir,
questionToolMounted,
questionToolName: ASK_USER_QUESTION_TOOL_NAME,
onRevisionDefect: (message) => deps.onError?.(new Error(message), { phase: "config", sessionId }),
onDisclosure: (message) => emitTrace(deps.tracer, () => ({ kind: "permission.rule_store_unreadable", version: 1, taskId: hostTaskId, message, ts: Date.now() })),
onStoreUnreadable: announceRuleStoreSkipped,
signal: abortController.signal,
stripsAllow: (rule, req) => permissionMode.current === "auto" && autoStrippedRule(rule, req),
});
const ruleOffersOf = createRuleOffersOf({
laneArmed: permissionRuleLane !== undefined,
principal: spec.principal,
localOwnerDeclared: deps.localOwnerRules === true,
egressTools,
irreversibilityTier,
shellGatedTools,
toolMarkSources,
taskRoot: taskRootFinal,
cwdRef: handsCwdRef,
deniesDirectoryRead: readDenyMatcher !== undefined ? (d) => readDenyMatcher.matchPath(d) !== null : undefined,
});
const recheckApprovedEdit = async (pol, onAskOf, creq, edit, approved, csignal, ancestorAutoMode) => {
const ancestorDecider = ancestorAutoMode?.decider;
const ancestorTracker = ancestorAutoMode?.denialTracking;
let editArgs = edit;
let settledBy = approved;
const vetoed = (d) => {
const out = { ...d };
carryEngineAttestations(d, out);
carryPrecedingSettlement(settledBy, out);
return out;
};
for (let round = 0;; round++) {
if (round >= 3) {
return vetoed({
action: "deny",
message: `the approval-edit chain for "${creq.toolName}" exceeded the re-approval cap (inherited parent policy) — denied fail-closed; re-submit the edited action directly`,
});
}
let re;
try {
re = refuseOutOfContractDecision(await pol.check({ ...creq, args: editArgs }, csignal ?? abortController.signal));
}
catch (err) {
return vetoed({
action: "deny",
message: `inherited parent policy re-check of the approved edit errored for "${creq.toolName}": ${thrownText(err)}`,
});
}
if (re.action === "deny")
return vetoed(re);
if (re.action === "allow") {
const stated = re.updatedInput === undefined ? "mode" : re.decisionReason;
const out = { action: "allow", updatedInput: re.updatedInput !== undefined ? re.updatedInput : editArgs, ...(stated !== undefined ? { decisionReason: stated } : {}) };
carryEngineAttestations(re, out);
carryPrecedingSettlement(settledBy, out);
return out;
}
if (re.updatedInput !== undefined)
editArgs = re.updatedInput;
const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: ancestorAutoMode, ask: re, req: { ...creq, args: editArgs }, signal: csignal ?? abortController.signal, subject: "the approved edit", allowRewrite: editArgs, facts: inheritedStationFacts(creq), unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(ancestorDecider?.timeoutMs !== undefined ? { budgetMs: ancestorDecider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(onAskOf), onNotice: deps.onNotice, sessionId, runId }) });
if (judged.kind === "allow") {
const cleared = { ...judged.result };
carryEngineAttestations(judged.result, cleared);
carryPrecedingSettlement(settledBy, cleared);
return cleared;
}
if (judged.kind === "deny")
return vetoed(judged.result);
const { ask: editAsk, fallback: editFallback, mintedHere: editMintedHere } = judged;
re = editAsk;
const editArbitrated = arbitrateInherited(re, creq.toolCallId, judged);
if (editArbitrated !== undefined) {
const out = editArbitrated.action === "allow" ? { action: "allow", updatedInput: editArgs, decisionReason: "mode" } : editArbitrated;
carryPrecedingSettlement(settledBy, out);
return out;
}
const rr = await resolveAsk({
toolName: creq.toolName,
toolCallId: creq.toolCallId,
args: editArgs,
...ruleOffersOf(creq, editArgs, ancestorFacts(re)),
...cardDisclosureOf(re, inheritedAskFallbackMessage(creq.toolName)),
...askSourceIdentity(),
...riskAxesOf(creq),
...(re.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
...askMandateMember(ancestorFacts(re)),
...inheritedAskCarry(judged, isLiveApproverSeat(onAskOf), ancestorTracker),
ruleEvidence: inheritedAskEvidence,
}, onAskOf, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
void settleReadRootGrant(rr.readRootGrant, undefined, { toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice });
settleDenialLimitFallback({ fallback: editFallback, mintedHere: editMintedHere, tracker: ancestorTracker, resolved: rr, headless: headlessDenyAtRecheck, stop: stopForDenialLimit, toolName: creq.toolName, toolCallId: creq.toolCallId });
if (rr.action !== "allow")
return rr;
settledBy = rr;
if (rr.updatedInput === undefined) {
const out = { action: "allow", updatedInput: editArgs, decisionReason: "mode" };
carryPrecedingSettlement(rr, out);
return out;
}
editArgs = rr.updatedInput;
}
};
const parentConstraintWrappers = (inheritedParentConstraints ?? []).map((pc) => pc.preToolUse !== undefined &&
pc.preToolUse === hooks?.preToolUse &&
!preToolUseObservational &&
pc.durableMandate !== true &&
pc.contentMandate !== true &&
askApproverIdentity(pc.onAsk) === askApproverIdentity(frozenOnAsk) &&
pc.hookEnv === hookEnvSource &&
pc.autoMode?.decider === autoModeDecider()
? { check: () => ({ action: "allow" }) }
: policy !== undefined && pc.policy === policy
? {
check: async (creq, csignal) => {
const first = sharedFirstDecision.get(creq.toolCallId);
if (first === undefined) {
return {
action: "deny",
message: `inherited parent policy could not be arbitrated for "${creq.toolName}" ` +
`(shared-instance first decision unavailable); denied fail-closed`,
};
}
if (first.action === "deny")
return first;
if (first.action === "allow")
return { action: "allow" };
if (isDelegatedQuestion(creq)) {
if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
return first;
return {
action: "deny",
message: `inherited parent policy requires durable handling of the question raised for "${creq.toolName}" and the marker channel is at capacity — ` +
`denied fail-closed (tighten-only)`,
};
}
if (pc.durableMandate === true) {
if (mandatedCallFloats(creq.toolCallId))
return first;
return mandateUnreconstructibleDeny(creq);
}
const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: pc.autoMode, ask: first, req: creq, signal: csignal ?? abortController.signal, subject: "this call", facts: inheritedStationFacts(creq), unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(pc.autoMode?.decider.timeoutMs !== undefined ? { budgetMs: pc.autoMode.decider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(pc.onAsk), onNotice: deps.onNotice, sessionId, runId }) });
if (judged.kind === "allow")
return judged.result;
if (judged.kind === "deny")
return judged.result;
const { ask: inheritedAsk, fallback, mintedHere } = judged;
if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq) && judged.classification.row.sandboxAdmission) {
recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
return { action: "allow", decisionReason: "sandbox" };
}
const arbitrated = arbitrateInherited(inheritedAsk, creq.toolCallId, judged);
if (arbitrated !== undefined)
return arbitrated;
const presentedArgs = creq.args;
const askT0 = now();
const resolved = await resolveAsk({
toolName: creq.toolName,
toolCallId: creq.toolCallId,
args: presentedArgs,
...ruleOffersOf(creq, presentedArgs, ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
...cardDisclosureOf(inheritedAsk, inheritedAskFallbackMessage(creq.toolName)),
...askSourceIdentity(),
...riskAxesOf(creq),
...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
...askMandateMember(ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
...inheritedAskCarry(judged, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
ruleEvidence: inheritedAskEvidence,
}, pc.onAsk, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
void settleReadRootGrant(resolved.readRootGrant, undefined, { toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice });
const askWaitMs = Math.max(0, now() - askT0);
if (resolved.action === "deny" && resolved.approverUnavailable === true) {
if (markInheritedUnavailable(creq.toolCallId))
return inheritedAsk;
return markerChannelFullDeny(creq);
}
settleDenialLimitFallback({ fallback, mintedHere, tracker: pc.autoMode?.denialTracking, resolved, headless: headlessDenyAtFold, stop: stopForDenialLimit, toolName: creq.toolName, toolCallId: creq.toolCallId });
if (resolved.action !== "allow")
return resolved;
if (resolved.updatedInput !== undefined) {
return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, resolved, csignal, pc.autoMode);
}
recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs, resolved, askClassMembersOf(judged.classification).classes);
return { action: "allow", decisionReason: "mode" };
},
}
: wrapToolPolicy(pc.policy, async (creq, csignal) => {
let decision;
try {
decision = refuseOutOfContractDecision(await pc.policy.check(creq, csignal ?? abortController.signal));
}
catch (err) {
return {
action: "deny",
message: `inherited parent policy errored for "${creq.toolName}": ${thrownText(err)}`,
};
}
if (decision.action !== "ask")
return decision;
if (isDelegatedQuestion(creq)) {
if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
return decision;
return {
action: "deny",
message: `inherited parent policy requires durable handling of the question raised for "${creq.toolName}" and the marker channel is at capacity — ` +
`denied fail-closed (tighten-only)`,
};
}
if (pc.durableMandate === true) {
if (mandatedCallFloats(creq.toolCallId))
return decision;
return mandateUnreconstructibleDeny(creq);
}
const presentedArgs = decision.updatedInput !== undefined ? decision.updatedInput : creq.args;
const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: pc.autoMode, ask: decision, req: { ...creq, args: presentedArgs }, signal: csignal ?? abortController.signal, subject: "this call", allowRewrite: decision.updatedInput, facts: inheritedStationFacts(creq), unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(pc.autoMode?.decider.timeoutMs !== undefined ? { budgetMs: pc.autoMode.decider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(pc.onAsk), onNotice: deps.onNotice, sessionId, runId }) });
if (judged.kind === "allow")
return judged.result;
if (judged.kind === "deny")
return judged.result;
const { ask: inheritedAsk, fallback, mintedHere } = judged;
if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq) && judged.classification.row.sandboxAdmission) {
recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
return decision.updatedInput !== undefined ? { action: "allow", updatedInput: decision.updatedInput, decisionReason: "sandbox" } : { action: "allow", decisionReason: "sandbox" };
}
const arbitrated = arbitrateInherited(inheritedAsk.action === "ask" ? { ...inheritedAsk, ...(decision.updatedInput !== undefined ? { updatedInput: decision.updatedInput } : {}) } : inheritedAsk, creq.toolCallId, judged);
if (arbitrated !== undefined)
return arbitrated;
const askT0 = now();
const resolved = await resolveAsk({
toolName: creq.toolName,
toolCallId: creq.toolCallId,
args: presentedArgs,
...ruleOffersOf(creq, presentedArgs, ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
...cardDisclosureOf(inheritedAsk, inheritedAskFallbackMessage(creq.toolName)),
...askSourceIdentity(),
...riskAxesOf(creq),
...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
...askMandateMember(ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
...inheritedAskCarry(judged, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
ruleEvidence: inheritedAskEvidence,
}, pc.onAsk, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
void settleReadRootGrant(resolved.readRootGrant, undefined, { toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice });
const askWaitMs = Math.max(0, now() - askT0);
if (resolved.action === "deny" && resolved.approverUnavailable === true) {
if (markInheritedUnavailable(creq.toolCallId))
return inheritedAsk;
return markerChannelFullDeny(creq);
}
settleDenialLimitFallback({ fallback, mintedHere, tracker: pc.autoMode?.denialTracking, resolved, headless: headlessDenyAtFold, stop: stopForDenialLimit, toolName: creq.toolName, toolCallId: creq.toolCallId });
if (resolved.action !== "allow")
return resolved;
if (resolved.updatedInput !== undefined) {
return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, resolved, csignal, pc.autoMode);
}
if (pc.preToolUse === undefined) {
recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs, resolved, askClassMembersOf(judged.classification).classes);
}
if (decision.updatedInput === undefined)
return resolved;
const approvedWithRewrite = { ...resolved, updatedInput: decision.updatedInput };
carryEngineAttestations(resolved, approvedWithRewrite);
return approvedWithRewrite;
}));
const rewriteCapableLayers = policy !== undefined || parentConstraintWrappers.length > 0;
const rewriteEmitterIndex = new Map();
const rewriteCapableChain = [
...(callerPolicyLayer !== undefined ? [callerPolicyLayer] : []),
...parentConstraintWrappers,
];
const callerSlotOffset = callerPolicyLayer !== undefined ? 1 : 0;
const sameInstanceAdapterIndices = new Set();
(inheritedParentConstraints ?? []).forEach((pc, j) => {
if (policy !== undefined && pc.policy === policy)
sameInstanceAdapterIndices.add(j + callerSlotOffset);
});
const instrumentedChain = rewriteCapableChain.map((layer, idx) => wrapToolPolicy(layer, async (creq, csignal) => {
const d = refuseOutOfContractDecision(await layer.check(creq, csignal));
if (d.updatedInput !== undefined)
rewriteEmitterIndex.set(creq.toolCallId, idx);
if (d.action === "ask") {
recordFoldAskClass(creq.toolCallId, idx < callerSlotOffset ? policyAskClassOf(policy) : "mandate", idx < callerSlotOffset ? "caller_policy" : "ancestor_constraint");
}
return d;
}));
const modeGateLayer = createModeGatePolicy(permissionMode, { env: executionEnv, rootPath: taskRootFinal, acceptDirs: [taskRootFinal, ...additionalRootsCanonical], roster: toolRosterDeltas, readOnlyDeclared: (name) => mountedToolDeclaresReadOnly(tools, name), ...(scratchpadDir !== undefined ? { exemptDirs: [scratchpadDir] } : {}) });
const policyLayers = [
...narrowingLayers,
...instrumentedChain,
...(rewriteCapableLayers ? narrowingLayers : []),
];
const integrityGuardLayers = [createTranscriptIntegrityPolicy(), createUnverifiableDeletePolicy()];
const guardedPolicyLayers = rewriteCapableLayers
? [...integrityGuardLayers, ...policyLayers, modeGateLayer, ...integrityGuardLayers]
: [...integrityGuardLayers, ...policyLayers, modeGateLayer];
const foldedPolicy = guardedPolicyLayers.length === 1 ? guardedPolicyLayers[0] : combinePolicies(...guardedPolicyLayers);
const effectivePolicy = foldedPolicy !== undefined && rewriteCapableLayers
? wrapToolPolicy(foldedPolicy, async (req, signal) => {
let decision;
try {
decision = await foldedPolicy.check(req, signal);
}
catch (err) {
rewriteEmitterIndex.delete(req.toolCallId);
throw err;
}
const emitterIdx = rewriteEmitterIndex.get(req.toolCallId);
rewriteEmitterIndex.delete(req.toolCallId);
if (decision.action === "deny" || decision.updatedInput === undefined)
return decision;
const recheckPrefix = rewriteCapableChain.slice(0, emitterIdx ?? rewriteCapableChain.length);
const postEmitterSameInstanceAdapters = [];
if (emitterIdx !== undefined && emitterIdx > 0) {
for (let i = emitterIdx + 1; i < rewriteCapableChain.length; i++) {
if (sameInstanceAdapterIndices.has(i))
postEmitterSameInstanceAdapters.push(rewriteCapableChain[i]);
}
}
const recheckLayers = [...recheckPrefix, ...postEmitterSameInstanceAdapters];
const finalReq = { ...req, args: decision.updatedInput };
let asked;
const revalidated = {};
let restated;
try {
for (const layer of recheckLayers) {
const d = refuseOutOfContractDecision(await layer.check(finalReq, signal));
if (d.action === "deny") {
const carried = { ...d, updatedInput: decision.updatedInput };
carryEngineAttestations(d, carried);
carryPrecedingSettlement(decision, carried);
return carried;
}
if (d.updatedInput !== undefined) {
return {
action: "deny",
message: "policy required a rewrite during final-form re-check — the arguments that would execute were not accepted as-is (fail-closed; rewrite chains are not iterated to a fixpoint)",
updatedInput: decision.updatedInput,
};
}
if (d.action === "ask" && asked === undefined)
asked = d;
if (d.action === "allow" && restated === undefined && d.decisionReason !== undefined)
restated = d.decisionReason;
carryEngineAttestations(d, revalidated);
}
}
finally {
rewriteEmitterIndex.delete(req.toolCallId);
}
if (asked !== undefined && decision.action === "allow") {
const surfaced = { ...asked, updatedInput: decision.updatedInput };
carryEngineAttestations(asked, surfaced);
return surfaced;
}
if (decision.action === "allow") {
const owned = { ...decision, ...(decision.decisionReason === undefined && restated !== undefined ? { decisionReason: restated } : {}) };
carryEngineAttestations(decision, owned);
carryEngineAttestations(revalidated, owned);
return owned;
}
return decision;
})
: foldedPolicy;
return { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, allowEntryLanes, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations };
}
const advisedPolicyNames = new WeakMap();
const ADVISED_KEYS_CAP = 64;