@sema-agent/core
Version:
Stateless, task-oriented AI agent core
680 lines (679 loc) • 47.5 kB
JavaScript
import { argsAsTheToolReads, withArgsReading } from "../effective-path-target.js";
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 { chainJudges, inheritedToolChecks, screeningInstallationsOf, standsInFor } from "../mods/stations.js";
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "../permission-rules.js";
import { isIsolated } from "../remote-env.js";
import { deliverEngineNotice } from "../engine-notice.js";
import { earlierGrammarNotice } from "../session-policy-store.js";
import { attest, attestationOf, checkToolPolicyProjection, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, durableParkOptInDeclared, isLiveApproverSeat, mergeAttestation, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, withAttestation, wrapToolPolicy } from "../tool-policy.js";
import { judgeRuleToolName, mountedToolNames } from "../tool-name-aliases.js";
import { approvalCardMemberOf } from "../tool-roster.js";
import { NAMESPACED_NAME_SHAPES } from "../protocol-table.js";
import { emitTrace } from "../trace.js";
import { skillGrantBindingMembers } from "../skill-grant.js";
import { SKILL_TOOL_NAME } from "./synthetic-tools.js";
import { delegationPoolReading } from "./sub-agent-deny-lists.js";
import { valveLeaves } from "../../agents/tool-filter.js";
import { autoStrippedRule, cardDisclosureOf, nearMissSpellingOf, rootFacingToolNames } from "../allow-entry.js";
import { arbitrateByMode, askClassMembersOf, askFaceOf, 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, resolveInheritedAsk, settleDenialLimitFallback } from "./denial-limit-arms.js";
import { settleReadRootGrant } from "./read-root-grant.js";
import { frozenApproverRoute } from "../wiring-manifest.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 { policyThrowLine, reportPolicyThrow } from "./policy-throw.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, pathRuleAnchors, headLeads, tools, harnessTools, selector, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, personRows, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, modBus, ownModChain, autoModeDecider, readFace, 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, inherited = false) => {
const nameGroups = toolPolicyNameSets(auditee);
if (nameGroups.length > 0) {
const known = mountedToolNames(tools);
for (const t of harnessTools)
known.add(t.name);
const pool = delegationPoolReading(spec.tools ?? [], valveLeaves(spec, selector));
const callerNames = mountedToolNames(spec.tools ?? []);
const rootFacing = rootFacingToolNames([...known, ...callerNames, ...pool.names]);
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);
const twin = verdict.kind === "current" && !inherited ? nearMissSpellingOf(n, rootFacing) : undefined;
const stale = verdict.kind !== "current" ? verdict : twin !== undefined ? { kind: "case_twin", twin, carrier: known.has(twin) || callerNames.has(twin) ? "run" : pool.names.has(twin) ? "pool" : "catalog" } : undefined;
if (stale !== undefined) {
const entry = `tool policy ${kind}-list entry "${n}"`;
let message;
switch (stale.kind) {
case "retired":
message = `${entry} is a RETIRED tool name (${stale.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.`;
break;
case "unprefixed_namespace":
message = `${entry} 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.`;
break;
case "unmintable_segment":
message = `${entry} can match no tool, ever (${stale.guidance}), so it would silently guard nothing.${stale.currentName !== undefined ? ` Write "${stale.currentName}".` : ""}`;
break;
case "case_twin":
message = `${entry} differs from ${stale.carrier === "run" ? "the mounted tool" : stale.carrier === "pool" ? "the tool a delegation pool carries" : "the engine's catalog tool"} "${stale.twin}" only in case, and a policy compares tool names exactly, so it matches nothing in this run's roster and would silently guard nothing. Write "${stale.twin}".`;
break;
default: {
const exhaustive = stale;
return exhaustive;
}
}
const err = new Error(message);
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 {
}
};
const announceEarlierGrammar = (rulesSessionId) => (entries, field, grammar) => deliverEngineNotice(deps.onNotice, earlierGrammarNotice({ rulesSessionId, sessionId, entries, field, grammar }));
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, anchors: pathRuleAnchors.leg, onEarlierGrammar: announceEarlierGrammar(sessionRuleKey.sessionId) });
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, anchors: pathRuleAnchors.leg, onEarlierGrammar: announceEarlierGrammar(snap.sessionId) }));
}
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);
auditPolicyNames(sessionRulePolicy);
for (const layer of ancestorRulePolicies)
auditPolicyNames(layer, true);
const denyNarrowingPolicy = narrowingLayers.length === 0 ? undefined : narrowingLayers.length === 1 ? narrowingLayers[0] : combinePolicies(...narrowingLayers);
const basePolicyForResumeEdit = lockedPreflight.basePolicyForResumeEdit ?? lockedPreflight.toolPolicy;
const ownChainJudges = chainJudges(modBus);
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) => attest({
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)`,
}, creq, { unresolvableCause: "mandate_unreconstructible" });
const markerChannelFullDeny = (creq) => attest({ action: "deny", message: `no approver is reachable for the inherited approval of "${creq.toolName}" and the marker channel is at capacity — denied fail-closed` }, creq, { unresolvableCause: "approver_unavailable" });
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 grantBindingOf = (call, args) => skillGrantBindingMembers(call, args, skillSpecs, SKILL_TOOL_NAME);
const inheritedStationFacts = (call) => {
const seat = seatMarksOf(call);
const face = readFace();
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 } : {}),
...(face !== undefined ? { readFace: face } : {}),
askFace: askFaceOf(wireOf("inherited.askFace", call)),
};
};
const inheritedAskEvidence = inheritedAskRuleEvidence(deps.permissionRuleStore);
const listFacts = { root: taskRootFinal, sessionId, liveCwd: () => handsCwdRef?.current, anchors: pathRuleAnchors.leg };
const skillGrantLane = skillGrants.lane(listFacts);
const allowEntryLanes = { request: allowedToolTables.request.lane(listFacts), deployment: allowedToolTables.deployment.lane(listFacts) };
const ruleLaneFacts = {
sessionId,
root: taskRootFinal,
rootCanonical: pathRuleAnchors.leg.root,
chainTop: pathRuleAnchors.chainTop,
leads: headLeads,
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() })),
stripsAllow: (rule, req) => permissionMode.current === "auto" && autoStrippedRule(rule, req),
};
const { personal: permissionRuleLane, org: permissionRuleOrgLane } = createPermissionRuleLanes({ provider: deps.permissionRuleStore, localOwnerDeclared: deps.localOwnerRules === true, principal: spec.principal, ...ruleLaneFacts, rows: personRows });
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 = argsAsTheToolReads(creq, edit);
let settledBy = approved;
const onApproval = (d) => withAttestation(d, mergeAttestation(creq, attestationOf(settledBy, creq), attestationOf(d, creq), "answer"));
const vetoed = onApproval;
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(await withArgsReading(creq, editArgs, csignal ?? abortController.signal), csignal ?? abortController.signal));
}
catch (err) {
reportPolicyThrow(notifyOwnHookCrash, "an inherited parent policy's re-check of the approved edit", creq, err, csignal ?? abortController.signal);
return vetoed({
action: "deny",
message: `inherited parent policy re-check of the approved edit errored for "${creq.toolName}": ${policyThrowLine(err)}`,
});
}
if (re.action === "deny")
return vetoed(re);
if (re.action === "allow") {
const stated = re.updatedInput === undefined ? "mode" : re.decisionReason;
return onApproval(withAttestation({ action: "allow", updatedInput: re.updatedInput !== undefined ? re.updatedInput : editArgs, ...(stated !== undefined ? { decisionReason: stated } : {}) }, re.attestation));
}
if (re.updatedInput !== undefined)
editArgs = argsAsTheToolReads(creq, 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 } : {}), route: frozenApproverRoute(onAskOf), onNotice: deps.onNotice, sessionId, runId }) });
if (judged.kind === "allow") {
return onApproval(judged.result);
}
if (judged.kind === "deny")
return vetoed(judged.result);
const { ask: editAsk } = judged;
re = editAsk;
const editArbitrated = arbitrateInherited(re, creq.toolCallId, judged);
if (editArbitrated !== undefined) {
return onApproval(editArbitrated.action === "allow" ? { action: "allow", updatedInput: editArgs, decisionReason: "mode" } : editArbitrated);
}
const { resolved: rr, judged: settled } = await resolveInheritedAsk(judged, (j) => resolveAsk({
toolName: creq.toolName,
toolCallId: creq.toolCallId,
args: editArgs,
...ruleOffersOf(creq, editArgs, ancestorFacts(j.ask)),
...cardDisclosureOf(j.ask, inheritedAskFallbackMessage(creq.toolName)),
...askSourceIdentity(),
...riskAxesOf(creq),
...approvalCardMemberOf(creq),
...grantBindingOf(creq, editArgs),
...(j.ask.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
...askMandateMember(ancestorFacts(j.ask)),
...inheritedAskCarry(j, 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 })), ancestorTracker, creq, inheritedStationFacts(creq));
void settleReadRootGrant(rr.readRootGrant, undefined, { toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice });
settleDenialLimitFallback({ fallback: settled.fallback, mintedHere: settled.mintedHere, 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)
return onApproval({ action: "allow", updatedInput: editArgs, decisionReason: "mode" });
editArgs = argsAsTheToolReads(creq, rr.updatedInput);
}
};
const settleInstallationAsk = async (pc, decision, creq, csignal) => {
const settledAs = (result) => ({ kind: "settled", result });
if (isDelegatedQuestion(creq)) {
if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
return settledAs(decision);
return settledAs({
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 settledAs(decision);
return settledAs(mandateUnreconstructibleDeny(creq));
}
const presentedArgs = decision.updatedInput !== undefined ? argsAsTheToolReads(creq, 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 } : {}), route: frozenApproverRoute(pc.onAsk), onNotice: deps.onNotice, sessionId, runId }) });
if (judged.kind === "allow")
return settledAs(judged.result);
if (judged.kind === "deny")
return settledAs(judged.result);
const { ask: inheritedAsk, fallback } = judged;
if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq) && judged.classification.row.sandboxAdmission) {
recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
return settledAs(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 settledAs(arbitrated);
const askT0 = now();
const { resolved, judged: settled } = await resolveInheritedAsk(judged, (j) => resolveAsk({
toolName: creq.toolName,
toolCallId: creq.toolCallId,
args: presentedArgs,
...ruleOffersOf(creq, presentedArgs, ancestorFacts(j.ask.action === "ask" ? j.ask : undefined)),
...cardDisclosureOf(j.ask, inheritedAskFallbackMessage(creq.toolName)),
...askSourceIdentity(),
...riskAxesOf(creq),
...approvalCardMemberOf(creq),
...grantBindingOf(creq, presentedArgs),
...(j.ask.action === "ask" && j.ask.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
...askMandateMember(ancestorFacts(j.ask.action === "ask" ? j.ask : undefined)),
...inheritedAskCarry(j, 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 })), pc.autoMode?.denialTracking, creq, inheritedStationFacts(creq));
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 settledAs(settled.ask);
return settledAs(markerChannelFullDeny(creq));
}
settleDenialLimitFallback({ fallback: settled.fallback, mintedHere: settled.mintedHere, tracker: pc.autoMode?.denialTracking, resolved, headless: headlessDenyAtFold, stop: stopForDenialLimit, toolName: creq.toolName, toolCallId: creq.toolCallId });
if (resolved.action !== "allow")
return settledAs(resolved);
return { kind: "approved", resolved, askWaitMs, classes: askClassMembersOf(judged.classification).classes };
};
const legStandsIn = (pc) => standsInFor(pc, { chain: ownModChain, onAsk: frozenOnAsk, hookEnv: hookEnvSource, decider: autoModeDecider() }, ownChainJudges);
const parentConstraintWrappers = (inheritedParentConstraints ?? []).map((pc) => legStandsIn(pc)
? { 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 } : {}), route: frozenApproverRoute(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 } = 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, judged: settled } = await resolveInheritedAsk(judged, (j) => resolveAsk({
toolName: creq.toolName,
toolCallId: creq.toolCallId,
args: presentedArgs,
...ruleOffersOf(creq, presentedArgs, ancestorFacts(j.ask.action === "ask" ? j.ask : undefined)),
...cardDisclosureOf(j.ask, inheritedAskFallbackMessage(creq.toolName)),
...askSourceIdentity(),
...riskAxesOf(creq),
...approvalCardMemberOf(creq),
...grantBindingOf(creq, presentedArgs),
...(j.ask.action === "ask" && j.ask.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
...askMandateMember(ancestorFacts(j.ask.action === "ask" ? j.ask : undefined)),
...inheritedAskCarry(j, 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 })), pc.autoMode?.denialTracking, creq, inheritedStationFacts(creq));
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 settled.ask;
return markerChannelFullDeny(creq);
}
settleDenialLimitFallback({ fallback: settled.fallback, mintedHere: settled.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(settled.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) {
reportPolicyThrow(notifyOwnHookCrash, "an inherited parent policy", creq, err, csignal ?? abortController.signal);
return {
action: "deny",
message: `inherited parent policy errored for "${creq.toolName}": ${policyThrowLine(err)}`,
};
}
if (decision.action !== "ask")
return decision;
const settled = await settleInstallationAsk(pc, decision, creq, csignal);
if (settled.kind === "settled")
return settled.result;
const { resolved } = settled;
if (resolved.updatedInput !== undefined) {
return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, resolved, csignal, pc.autoMode);
}
if (screeningInstallationsOf([pc]).length === 0) {
recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, settled.askWaitMs, resolved, settled.classes);
}
if (decision.updatedInput === undefined)
return resolved;
return { ...resolved, updatedInput: decision.updatedInput };
}));
const installationToolChecks = inheritedToolChecks(inheritedParentConstraints ?? [], legStandsIn, (pc) => async (ask, creq, csignal) => {
const settled = await settleInstallationAsk(pc, ask, creq, csignal);
return settled.kind === "settled" ? settled.result : settled.resolved;
});
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({ removalSeat: { env: executionEnv, additionalRoots: () => additionalRootsCanonical } })];
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 = await withArgsReading(req, decision.updatedInput, signal);
let asked;
let revalidated;
let restated;
try {
for (const layer of recheckLayers) {
const d = refuseOutOfContractDecision(await layer.check(finalReq, signal));
if (d.action === "deny") {
return withAttestation({ ...d, updatedInput: decision.updatedInput }, mergeAttestation(req, attestationOf(decision, req), attestationOf(d, req), "answer"));
}
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;
revalidated = mergeAttestation(req, revalidated, d.attestation);
}
}
finally {
rewriteEmitterIndex.delete(req.toolCallId);
}
if (asked !== undefined && decision.action === "allow") {
return { ...asked, updatedInput: decision.updatedInput };
}
if (decision.action === "allow") {
const owned = { ...decision, ...(decision.decisionReason === undefined && restated !== undefined ? { decisionReason: restated } : {}) };
return withAttestation(owned, mergeAttestation(req, attestationOf(decision, req), revalidated));
}
return decision;
})
: foldedPolicy;
return { basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, allowEntryLanes, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, grantBindingOf, permissionRuleLane, ruleLaneFacts, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations, inheritedToolChecks: installationToolChecks };
}
const advisedPolicyNames = new WeakMap();
const ADVISED_KEYS_CAP = 64;