@sema-agent/core 7.35.2 → 7.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +142 -0
- package/dist/brain/anthropic.js +6 -6
- package/dist/brain/errors.d.ts +1 -14
- package/dist/brain/errors.js +2 -6
- package/dist/brain/open-responses.js +7 -7
- package/dist/brain/openai.js +7 -7
- package/dist/brain/provider-echo-shape.d.ts +26 -0
- package/dist/brain/provider-echo-shape.js +12 -0
- package/dist/brain/tool-call-id.d.ts +27 -15
- package/dist/brain/tool-call-id.js +43 -0
- package/dist/core/ask-origin.d.ts +35 -5
- package/dist/core/ask-origin.js +23 -4
- package/dist/core/ask-question.d.ts +0 -30
- package/dist/core/ask-question.js +3 -2
- package/dist/core/checkpoint-store.d.ts +18 -6
- package/dist/core/checkpoint-store.js +2 -1
- package/dist/core/context-edit.d.ts +4 -0
- package/dist/core/context-edit.js +5 -2
- package/dist/core/decision-receipt.d.ts +21 -0
- package/dist/core/decision-receipt.js +63 -0
- package/dist/core/engine-notice.d.ts +15 -5
- package/dist/core/gate-fold.js +2 -1
- package/dist/core/gate-lanes.js +20 -23
- package/dist/core/gate-outcome.d.ts +7 -5
- package/dist/core/gate-outcome.js +5 -1
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hooks.d.ts +14 -2
- package/dist/core/hooks.js +7 -3
- package/dist/core/mode-policy.js +2 -1
- package/dist/core/park-selfcheck.js +2 -0
- package/dist/core/permission-rule-model.d.ts +2 -2
- package/dist/core/permission-rule-model.js +2 -2
- package/dist/core/permission-rules.js +2 -5
- package/dist/core/runner/active-skill-scope.js +5 -5
- package/dist/core/runner/decide-continuation.js +2 -1
- package/dist/core/runner/denial-limit-arms.d.ts +28 -18
- package/dist/core/runner/denial-limit-arms.js +11 -7
- package/dist/core/runner/inherited-ask-grants.d.ts +32 -31
- package/dist/core/runner/inherited-ask-grants.js +5 -0
- package/dist/core/runner/permission-rule-lanes.d.ts +3 -0
- package/dist/core/runner/permission-rule-lanes.js +7 -6
- package/dist/core/runner/prepare-context-lane.d.ts +4 -0
- package/dist/core/runner/prepare-context-lane.js +7 -5
- package/dist/core/runner/prepare-gate-stations.js +17 -12
- package/dist/core/runner/prepare-inherited-gate.js +1 -1
- package/dist/core/runner/prepare-park-ask.d.ts +4 -2
- package/dist/core/runner/prepare-park-ask.js +8 -11
- package/dist/core/runner/prepare-policy-chain.d.ts +5 -8
- package/dist/core/runner/prepare-policy-chain.js +50 -31
- package/dist/core/runner/prepare-safety-scan.js +9 -2
- package/dist/core/runner/prepare-task.js +3 -3
- package/dist/core/runner/resume-apply.js +2 -1
- package/dist/core/runner/run-attachment-seats.js +3 -1
- package/dist/core/runner/run-harness-handlers.js +6 -1
- package/dist/core/runner/run-leg.js +11 -9
- package/dist/core/runner/run-recovery-lanes.js +2 -0
- package/dist/core/runner/session-rule-policy.js +5 -4
- package/dist/core/runner/tool-call-id-fold-seat.d.ts +13 -0
- package/dist/core/runner/tool-call-id-fold-seat.js +31 -0
- package/dist/core/sensitive-path-policy.js +2 -2
- package/dist/core/side-query.d.ts +5 -1
- package/dist/core/side-query.js +5 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +18 -0
- package/dist/core/tool-name-readings.d.ts +370 -0
- package/dist/core/tool-name-readings.js +137 -0
- package/dist/core/tool-policy.d.ts +79 -89
- package/dist/core/tool-policy.js +86 -49
- package/dist/core/tool-registry.d.ts +1 -31
- package/dist/core/tool-registry.js +2 -22
- package/dist/core/tool-result-budget.d.ts +3 -0
- package/dist/core/tool-result-budget.js +5 -3
- package/dist/core/tool-roster.d.ts +30 -9
- package/dist/core/tool-roster.js +45 -5
- package/dist/engine/llm/types.d.ts +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/tools/fs/file-read-check.d.ts +1 -1
- package/dist/tools/fs/file-read-check.js +4 -3
- package/dist/tools/fs/shell-read-check.d.ts +15 -1
- package/dist/tools/fs/shell-read-check.js +5 -2
- package/package.json +2 -1
- package/test/export-surface.snapshot.json +7 -1
- package/wire-changes/core@7.36.0.json +288 -0
- package/wire-changes/core@7.37.0.json +405 -0
|
@@ -5,7 +5,7 @@ import { attestClassifierDenyCause, attestClassifierRound } from "../tool-policy
|
|
|
5
5
|
import { SETTLEMENT_NOBODY_JUDGED } from "../gate-outcome.js";
|
|
6
6
|
import { deliverEngineNotice } from "../types.js";
|
|
7
7
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
8
|
-
import { askOriginOf, classifyAsk } from "../ask-origin.js";
|
|
8
|
+
import { askClassMembersOf, askOriginOf, classifyAsk } from "../ask-origin.js";
|
|
9
9
|
export function attachRebuiltDenialTrackers(entries, denialLimit) {
|
|
10
10
|
if (entries === undefined)
|
|
11
11
|
return undefined;
|
|
@@ -68,30 +68,34 @@ function fallbackCarry(fallback, liveApprover, tracker) {
|
|
|
68
68
|
export function gateAskCarry(decision, liveApprover, tracker) {
|
|
69
69
|
return {
|
|
70
70
|
...(decision.origin !== undefined ? { origin: decision.origin } : {}),
|
|
71
|
+
...(decision.askClass !== undefined && decision.classes !== undefined ? { askClass: decision.askClass, classes: decision.classes } : {}),
|
|
71
72
|
...fallbackCarry(decision.denialLimitFallback, liveApprover, tracker),
|
|
72
73
|
};
|
|
73
74
|
}
|
|
74
75
|
export function inheritedAskCarry(judged, liveApprover, tracker) {
|
|
75
76
|
return {
|
|
76
77
|
origin: judged.origin,
|
|
78
|
+
...askClassMembersOf(judged.classification),
|
|
77
79
|
...fallbackCarry(judged.fallback, liveApprover, tracker),
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
export async function judgeInheritedClassifier(opts) {
|
|
81
83
|
const { autoMode, ask, req } = opts;
|
|
82
|
-
const
|
|
84
|
+
const facts = opts.facts ?? INHERITED_STATION_FACTS;
|
|
85
|
+
const origin = askOriginOf(ask, facts);
|
|
86
|
+
const classification = classifyAsk(ask, facts);
|
|
83
87
|
const classifierStandsDown = () => {
|
|
84
88
|
const live = opts.liveMode?.();
|
|
85
89
|
return live === "dontAsk" || live === "bypassPermissions" || live === "plan" || (autoMode?.liveMode !== undefined && autoMode.liveMode.current !== "auto");
|
|
86
90
|
};
|
|
87
|
-
if (autoMode === undefined || classifierStandsDown() || !
|
|
88
|
-
return { kind: "resolve", ask, fallback: ask.denialLimitFallback, mintedHere: false, origin };
|
|
91
|
+
if (autoMode === undefined || classifierStandsDown() || !classification.row.classifierEligible) {
|
|
92
|
+
return { kind: "resolve", ask, fallback: ask.denialLimitFallback, mintedHere: false, origin, classification };
|
|
89
93
|
}
|
|
90
94
|
const verdict = await autoMode.decider
|
|
91
95
|
.decide({ req, ...(ask.message !== undefined ? { askMessage: ask.message } : {}) }, opts.signal)
|
|
92
96
|
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
93
97
|
if (verdict.kind === "allow" && classifierStandsDown())
|
|
94
|
-
return { kind: "resolve", ask, fallback: ask.denialLimitFallback, mintedHere: false, origin };
|
|
98
|
+
return { kind: "resolve", ask, fallback: ask.denialLimitFallback, mintedHere: false, origin, classification };
|
|
95
99
|
const attributed = (d) => (verdict.classifier !== undefined ? attestClassifierRound(d, verdict.classifier, req) : d);
|
|
96
100
|
if (verdict.kind === "allow") {
|
|
97
101
|
autoMode.denialTracking?.recordAllow();
|
|
@@ -100,7 +104,7 @@ export async function judgeInheritedClassifier(opts) {
|
|
|
100
104
|
if (verdict.kind === "unavailable") {
|
|
101
105
|
const arm = classifierUnavailableArm({ ask, req, cause: verdict.cause, seat: opts.unavailableSeat, aborted: opts.signal.aborted });
|
|
102
106
|
if (arm.kind === "asked")
|
|
103
|
-
return { kind: "resolve", ask: arm.ask, fallback: ask.denialLimitFallback, mintedHere: false, origin };
|
|
107
|
+
return { kind: "resolve", ask: arm.ask, fallback: ask.denialLimitFallback, mintedHere: false, origin, classification: classifyAsk(arm.ask, facts) };
|
|
104
108
|
return { kind: "deny", result: attributed(arm.deny) };
|
|
105
109
|
}
|
|
106
110
|
const reason = verdict.kind === "block" ? (verdict.reason ? inlineUntrusted(verdict.reason) : "") : classifierParseFailureDenyMessage(verdict.classifier);
|
|
@@ -116,7 +120,7 @@ export async function judgeInheritedClassifier(opts) {
|
|
|
116
120
|
}
|
|
117
121
|
const fallback = tracked.fallback;
|
|
118
122
|
const minted = { ...ask, message: denialLimitFallbackMessage(fallback, reason || category || req.toolName), decisionReason: "classifier", requiresRealApproval: true, denialLimitFallback: fallback };
|
|
119
|
-
return { kind: "resolve", ask: minted, fallback, mintedHere: true, origin: askOriginOf(minted,
|
|
123
|
+
return { kind: "resolve", ask: minted, fallback, mintedHere: true, origin: askOriginOf(minted, facts), classification: classifyAsk(minted, facts) };
|
|
120
124
|
}
|
|
121
125
|
export function headlessDenyAtFold(r) {
|
|
122
126
|
return r.settlement !== undefined && SETTLEMENT_NOBODY_JUDGED[r.settlement.kind];
|
|
@@ -1,41 +1,42 @@
|
|
|
1
|
+
import type { AskClass } from "../ask-origin.js";
|
|
1
2
|
import type { HumanReviewGateRow } from "../task-result.js";
|
|
2
3
|
import { type OnAsk, type PermissionResult, type ResolvedAsk, type ToolCallRequest } from "../tool-policy.js";
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
* gate just re-asks — the safe direction, never a gate failure). */
|
|
7
|
-
export declare function askGrantShapeOf(args: unknown): string | undefined;
|
|
8
|
-
/**
|
|
9
|
-
* Duplicate-frame collapse (single-Runner embedded shape), the CONSUME half: an inherited
|
|
10
|
-
* layer already resolved THIS call as a clean allow at the SAME approver function, on EXACTLY the
|
|
11
|
-
* byte-shape this caller-slot ask now presents. The human's consent covers this consultation
|
|
12
|
-
* verbatim — asking again is the 2-frame UX debt, not a second authority. Guards, all mismatches
|
|
13
|
-
* re-ask (⇒ `undefined`):
|
|
14
|
-
* · SOURCE: only a caller-slot POLICY ask may reuse — a safety tighten
|
|
15
|
-
* (egress/irreversible/shellGate stamp `decisionReason:"safety"`), a hook-promoted ask
|
|
16
|
-
* ("hook"), or a classifier ask carries a DIFFERENT authority/context and keeps its frame.
|
|
17
|
-
* · IDENTITY: the approver FUNCTION reference (never a name) — compared through
|
|
18
|
-
* `askApproverIdentity`, not by raw reference (ruled 2026-08-04): a delegated child's approver
|
|
19
|
-
* is the ancestor's own, wrapped with delegation provenance — a DIFFERENT function object, the
|
|
20
|
-
* SAME operator. Bare `===` therefore stopped collapsing the frame the moment the ask seam
|
|
21
|
-
* started forwarding approvers, and one tool call presented the same person the same card
|
|
22
|
-
* twice. The wrapper adds metadata and delegates every decision, so it grants no authority of
|
|
23
|
-
* its own; identity here only answers "same consultation".
|
|
24
|
-
* · SHAPE: byte-equality against the presented SNAPSHOT; reuse re-emits that snapshot
|
|
25
|
-
* as the executed shape via `presentedInput` — the same schema-revalidated binding
|
|
26
|
-
* as a main-gate approval (shown == executed, by construction).
|
|
27
|
-
* · LEDGER: the ONE real consultation (timed on the wrapper arm) is recorded here —
|
|
28
|
-
* one frame happened, one frame is booked.
|
|
29
|
-
* The grant lives only within one gate evaluation (consumed on reuse, swept at the call site) — a
|
|
30
|
-
* REOPENED checkpoint's re-adjudication never sees a stale grant.
|
|
31
|
-
*/
|
|
32
|
-
export declare function consumeInheritedAskGrant(grants: Map<string, {
|
|
4
|
+
/** One inherited ask grant: a person's clean allow at an ancestor's live approver, on ONE presented shape, to ONE question
|
|
5
|
+
* (`classes`, the request's class set in `ASK_CLASSES` order) — it answers no stricter question. */
|
|
6
|
+
export interface InheritedAskGrant {
|
|
33
7
|
approver: unknown;
|
|
34
8
|
argsJson: string;
|
|
35
9
|
presented: unknown;
|
|
36
10
|
waitMs: number;
|
|
37
11
|
settled?: object;
|
|
38
|
-
|
|
12
|
+
classes: readonly AskClass[];
|
|
13
|
+
}
|
|
14
|
+
/** Does a grant for `granted` cover a question the gate classified as `asked`? `ordinary` — the plain hesitation every other
|
|
15
|
+
* class is a stricter form of — is covered by any card answered; every other class must have been on the card (the gate reads
|
|
16
|
+
* facts the station cannot hold, and a class those add was asked of nobody). Unclassified, or a card with no class ⇒ never. */
|
|
17
|
+
export declare function askGrantCovers(granted: readonly AskClass[], asked: readonly AskClass[] | undefined): boolean;
|
|
18
|
+
/** JSON.stringify throws on a cyclic value (a rewrite object carries no acyclicity guarantee) — `undefined` means "no
|
|
19
|
+
* comparable shape", which every consumer treats as a mismatch (skip the record / reuse ⇒ re-ask, the safe direction). */
|
|
20
|
+
export declare function askGrantShapeOf(args: unknown): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Duplicate-frame collapse, the CONSUME half: an inherited layer already resolved THIS call as a clean allow at the
|
|
23
|
+
* SAME approver function, on EXACTLY the byte-shape this caller-slot ask now presents, for a question that covers this
|
|
24
|
+
* one — asking again is the 2-frame UX debt, not a second authority. Guards, all mismatches re-ask (⇒ `undefined`):
|
|
25
|
+
* · SOURCE: only a caller-slot POLICY ask may reuse — a safety tighten (`decisionReason:"safety"`), a hook-promoted
|
|
26
|
+
* ask ("hook") or a classifier ask carries a DIFFERENT authority and keeps its frame.
|
|
27
|
+
* · IDENTITY: the approver FUNCTION reference (never a name), compared through `askApproverIdentity`: a delegated
|
|
28
|
+
* child's approver is the ancestor's own wrapped with provenance — a different function object, the same operator
|
|
29
|
+
* (the wrapper grants no authority of its own); identity answers only "same consultation".
|
|
30
|
+
* · SHAPE: byte-equality against the presented SNAPSHOT; reuse re-emits that snapshot as the executed shape via
|
|
31
|
+
* `presentedInput` — the same binding as a main-gate approval (shown == executed, by construction).
|
|
32
|
+
* · CLASS: the gate's stamped `classes` is covered by the set the person answered ({@link askGrantCovers}); a class
|
|
33
|
+
* the gate's own judges add (a protected target, an out-of-root read) was not on the card. Not covered ⇒ the gate
|
|
34
|
+
* asks its own question and the grant stays (it answers what it answered; the call-site sweep ends it).
|
|
35
|
+
* · LEDGER: the ONE real consultation (timed on the wrapper arm) is booked here — one frame happened, one is booked.
|
|
36
|
+
* The grant lives within one gate evaluation (consumed on reuse, swept at the call site): a REOPENED checkpoint's
|
|
37
|
+
* re-adjudication never sees a stale grant.
|
|
38
|
+
*/
|
|
39
|
+
export declare function consumeInheritedAskGrant(grants: Map<string, InheritedAskGrant>, onAsk: OnAsk | undefined, humanReviewRef: {
|
|
39
40
|
count: number;
|
|
40
41
|
totalWaitMs: number;
|
|
41
42
|
gates: HumanReviewGateRow[];
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { primaryActivityArg } from "../arg-summary.js";
|
|
2
2
|
import { askApproverIdentity, carryEngineAttestations } from "../tool-policy.js";
|
|
3
|
+
export function askGrantCovers(granted, asked) {
|
|
4
|
+
return asked !== undefined && asked.length > 0 && granted.length > 0 && asked.every((c) => c === "ordinary" || granted.includes(c));
|
|
5
|
+
}
|
|
3
6
|
export function askGrantShapeOf(args) {
|
|
4
7
|
try {
|
|
5
8
|
return JSON.stringify(args ?? null);
|
|
@@ -14,6 +17,8 @@ export function consumeInheritedAskGrant(grants, onAsk, humanReviewRef, decision
|
|
|
14
17
|
const grant = grants.get(req.toolCallId);
|
|
15
18
|
if (grant === undefined || askApproverIdentity(grant.approver) !== askApproverIdentity(onAsk) || grant.argsJson !== askGrantShapeOf(req.args))
|
|
16
19
|
return undefined;
|
|
20
|
+
if (!askGrantCovers(grant.classes, decision.action === "ask" ? decision.classes : undefined))
|
|
21
|
+
return undefined;
|
|
17
22
|
grants.delete(req.toolCallId);
|
|
18
23
|
humanReviewRef.count += 1;
|
|
19
24
|
humanReviewRef.totalWaitMs += grant.waitMs;
|
|
@@ -241,6 +241,9 @@ export declare function createRuleOffersOf(cfg: {
|
|
|
241
241
|
shellGatedTools: {
|
|
242
242
|
has(name: string): boolean;
|
|
243
243
|
};
|
|
244
|
+
toolMarkSources: {
|
|
245
|
+
get(name: string): ReadonlySet<string> | undefined;
|
|
246
|
+
};
|
|
244
247
|
taskRoot: string | undefined;
|
|
245
248
|
/** The live tracked-cwd ref (read per call — the relative-cd resolution base). */
|
|
246
249
|
cwdRef: {
|
|
@@ -3,7 +3,8 @@ import {} from "../hooks.js";
|
|
|
3
3
|
import { adjudicatePersistedPathRules, adjudicatePersistedRules, laneToolNamesOf, ruleToolGrammarOf, segmentCoverageOf, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
4
4
|
import { isAbsolutePathForm } from "../../tools/fs/safety.js";
|
|
5
5
|
import { effectivePathTargetOf } from "../effective-path-target.js";
|
|
6
|
-
import {
|
|
6
|
+
import { declaredPathTargetOf, protectivePathTargetOf } from "../tool-registry.js";
|
|
7
|
+
import { heldBy, seatOf, wireOf } from "../tool-name-readings.js";
|
|
7
8
|
import { orgRuleVerdictFor } from "../permission-rule-org.js";
|
|
8
9
|
import { ownerStoreOf } from "../permission-rule-provider.js";
|
|
9
10
|
import { thrownText } from "../thrown-value.js";
|
|
@@ -92,7 +93,7 @@ export function createPermissionRuleLanes(cfg) {
|
|
|
92
93
|
const org = provider.partitions.org
|
|
93
94
|
? {
|
|
94
95
|
adjudicate: async (req) => {
|
|
95
|
-
if (
|
|
96
|
+
if (wireOf("offer.orgQuestionExemption", req) === cfg.questionToolName && cfg.questionToolMounted)
|
|
96
97
|
return { status: "available" };
|
|
97
98
|
let resolution;
|
|
98
99
|
try {
|
|
@@ -158,16 +159,16 @@ export function createPermissionRuleLanes(cfg) {
|
|
|
158
159
|
}
|
|
159
160
|
export function createRuleOffersOf(cfg) {
|
|
160
161
|
return (call, args, ask) => {
|
|
161
|
-
const toolName = call
|
|
162
|
+
const toolName = wireOf("offer.laneTool", call);
|
|
162
163
|
if (!cfg.laneArmed || toolName !== PERSISTED_RULE_TOOL)
|
|
163
164
|
return {};
|
|
164
165
|
if ((cfg.principal === undefined || cfg.principal === "") && !cfg.localOwnerDeclared)
|
|
165
166
|
return {};
|
|
166
|
-
const tierSeat =
|
|
167
|
+
const tierSeat = seatOf("offer.marks", cfg.irreversibilityTier, call);
|
|
167
168
|
const closedDoor = ruleClearanceOf(ask, {
|
|
168
|
-
egress:
|
|
169
|
+
egress: heldBy("offer.marks.egress", cfg.egressTools, call),
|
|
169
170
|
irreversibility: tierSeat?.value,
|
|
170
|
-
shellGated: tierSeat
|
|
171
|
+
shellGated: tierSeat?.shellGated(cfg.shellGatedTools, cfg.toolMarkSources) === true,
|
|
171
172
|
}).word;
|
|
172
173
|
if (closedDoor !== undefined)
|
|
173
174
|
return { ruleOffersAbsence: closedDoor };
|
|
@@ -24,6 +24,7 @@ import { type RunnerDeps } from "../types.js";
|
|
|
24
24
|
import type { Prepared, PreparedMicroCompact } from "./contracts.js";
|
|
25
25
|
import { type GitStatusLaneRef } from "./git-status-frame.js";
|
|
26
26
|
import type { ReadFileState } from "../../tools/fs/safety.js";
|
|
27
|
+
import { type ToolRosterDeltaSeat } from "../tool-roster.js";
|
|
27
28
|
export interface PrepareContextLaneInput {
|
|
28
29
|
/** borrowed-readonly — the task model: the budgets, the trigger-window geometry and `charsPerToken` are read off it. */
|
|
29
30
|
model: Model;
|
|
@@ -39,6 +40,8 @@ export interface PrepareContextLaneInput {
|
|
|
39
40
|
hostTaskId: string;
|
|
40
41
|
/** borrowed-readonly — the run's offload store, or undefined (the caps' and the machine's persist seat). */
|
|
41
42
|
offloadStore: ToolResultStore | undefined;
|
|
43
|
+
/** borrowed-readonly — the leg's live roster: a recorded tool name is judged as the wire name it resolves to. */
|
|
44
|
+
toolRosterDeltas: ToolRosterDeltaSeat;
|
|
42
45
|
/** borrowed-readonly — the doors' RESOLVED microCompact knob (immutable values, never the deps bag). */
|
|
43
46
|
microCompactKnob: {
|
|
44
47
|
machine: MicroCompactMachine;
|
|
@@ -135,6 +138,7 @@ export interface GuardChainArgs {
|
|
|
135
138
|
offloadStore: import("../tool-result-store.js").ToolResultStore | undefined;
|
|
136
139
|
sessionId: string;
|
|
137
140
|
taskId: string;
|
|
141
|
+
toolNameOf?: (recorded: string) => string;
|
|
138
142
|
deps: RunnerDeps;
|
|
139
143
|
trimPressureRef: {
|
|
140
144
|
droppedMessages: boolean;
|
|
@@ -8,8 +8,10 @@ import { emitTrace } from "../trace.js";
|
|
|
8
8
|
import { deliverEngineNotice } from "../types.js";
|
|
9
9
|
import { applyGitFrameGuard } from "./git-status-frame.js";
|
|
10
10
|
import { applyContextShrinkToReadFileState } from "../../tools/fs/index.js";
|
|
11
|
+
import { rosterToolName } from "../tool-roster.js";
|
|
11
12
|
export function prepareContextLane(input) {
|
|
12
|
-
const { model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, readFileState } = input;
|
|
13
|
+
const { model, deps, sessionId, runId, hostTaskId, offloadStore, toolRosterDeltas, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, readFileState } = input;
|
|
14
|
+
const toolNameOf = (recorded) => rosterToolName(toolRosterDeltas.current, recorded);
|
|
13
15
|
const contextOverlay = input.contextOverlay ?? (() => undefined);
|
|
14
16
|
const requestGeometry = () => {
|
|
15
17
|
const current = harness.getModel();
|
|
@@ -27,7 +29,7 @@ export function prepareContextLane(input) {
|
|
|
27
29
|
const replay = replayClearedProjection(healed, microCompact.ledger);
|
|
28
30
|
const replayed = replay.messages;
|
|
29
31
|
const capped = await capAggregateToolResults(replayed, {
|
|
30
|
-
store: offloadStore,
|
|
32
|
+
store: offloadStore, toolNameOf,
|
|
31
33
|
sessionId,
|
|
32
34
|
onCapped: (info) => pendingProjectionObservations.push(() => emitTrace(deps.tracer, () => ({
|
|
33
35
|
kind: "tool_result.capped",
|
|
@@ -49,7 +51,7 @@ export function prepareContextLane(input) {
|
|
|
49
51
|
});
|
|
50
52
|
let ccPass;
|
|
51
53
|
const edited = microCompact.machine === "off" ? mediaCapped : clearStaleToolResults(mediaCapped, {
|
|
52
|
-
budgetTokens: editAt,
|
|
54
|
+
budgetTokens: editAt, toolNameOf,
|
|
53
55
|
clearSource: replayed,
|
|
54
56
|
onCleared: (pass) => { ccPass = pass; },
|
|
55
57
|
anchoredTotalTokens: estimateContextTokens(mediaCapped, cpt).tokens,
|
|
@@ -67,7 +69,7 @@ export function prepareContextLane(input) {
|
|
|
67
69
|
}
|
|
68
70
|
const chain = await runGuardChain({
|
|
69
71
|
edited, replayed, index: replay.index, microCompact, guardAt, charsPerToken: cpt, offloadStore,
|
|
70
|
-
sessionId, taskId: hostTaskId, deps, trimPressureRef, recheck, signal,
|
|
72
|
+
sessionId, taskId: hostTaskId, deps, trimPressureRef, recheck, signal, toolNameOf,
|
|
71
73
|
pendingProjectionObservations,
|
|
72
74
|
...(readFileState !== undefined ? { readFileState } : {}),
|
|
73
75
|
});
|
|
@@ -221,7 +223,7 @@ export async function runGuardChain(args) {
|
|
|
221
223
|
if (microCompact.machine === "off" && anchoredWorking > guardAt) {
|
|
222
224
|
let blockingPass;
|
|
223
225
|
const blockingEdited = clearStaleToolResults(working, {
|
|
224
|
-
budgetTokens: guardAt,
|
|
226
|
+
budgetTokens: guardAt, toolNameOf: args.toolNameOf,
|
|
225
227
|
clearSource: args.replayed,
|
|
226
228
|
onCleared: (pass) => { blockingPass = pass; },
|
|
227
229
|
anchoredTotalTokens: anchoredWorking,
|
|
@@ -9,8 +9,10 @@ import { askUnresolvableNotice } from "../ask-unresolvable-notice.js";
|
|
|
9
9
|
import { askReachesPerson } from "../wiring-manifest.js";
|
|
10
10
|
import { classifierUnavailableSeat } from "./classifier-ladder.js";
|
|
11
11
|
import { PATH_CONFINABLE_WRITE_TOOLS } from "./session-rule-policy.js";
|
|
12
|
-
import {
|
|
12
|
+
import { callFaceSeat, rosterToolName, toolCallFaceOf } from "../tool-roster.js";
|
|
13
|
+
import { heldBy, heldValues, seatOf, wireOf } from "../tool-name-readings.js";
|
|
13
14
|
import { noteHostRewrite } from "../frame-settlement.js";
|
|
15
|
+
const PEER_MESSAGE_NAMES = new Set([SEND_MESSAGE_TOOL_NAME]);
|
|
14
16
|
function screenGateResult(result) {
|
|
15
17
|
if (result.suspend !== undefined)
|
|
16
18
|
return { defects: [] };
|
|
@@ -88,12 +90,12 @@ export function prepareGateStations(input) {
|
|
|
88
90
|
harness.on("tool_call", async (called) => {
|
|
89
91
|
const e = { ...called, toolName: rosterToolName(toolRosterDeltas.current, called.toolName) };
|
|
90
92
|
const call = { toolName: e.toolName, ...callFaceSeat(toolCallFaceOf(toolRosterDeltas.current, called.toolName)) };
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
+
const tierSeat = seatOf("gate.shellSeat", irreversibilityTier, call);
|
|
94
|
+
const markSources = new Set(heldValues("gate.markSources", toolMarkSources, call).flatMap((s) => [...s]));
|
|
93
95
|
blockedToolCalls.delete(e.toolCallId);
|
|
94
96
|
inheritedAskGrants.delete(e.toolCallId);
|
|
95
97
|
{
|
|
96
|
-
const complianceDeny =
|
|
98
|
+
const complianceDeny = heldValues("gate.compliance", { get: (n) => complianceCallDenial(complianceDenies, n) }, call)[0];
|
|
97
99
|
if (complianceDeny !== undefined) {
|
|
98
100
|
if (blockedTracked)
|
|
99
101
|
blockedToolCalls.add(e.toolCallId);
|
|
@@ -122,19 +124,19 @@ export function prepareGateStations(input) {
|
|
|
122
124
|
resolveAsk: resolveAskBound,
|
|
123
125
|
suspendAsk,
|
|
124
126
|
resolveContentAsk,
|
|
125
|
-
egress:
|
|
126
|
-
peerMessage:
|
|
127
|
+
egress: heldBy("gate.egress", egressTools, call),
|
|
128
|
+
peerMessage: heldBy("gate.peerMessage", PEER_MESSAGE_NAMES, call),
|
|
127
129
|
...(writeProtectionCheck !== undefined ? { writeProtectionCheck } : {}),
|
|
128
130
|
...(shellReadCheck !== undefined ? { shellReadCheck } : {}),
|
|
129
131
|
irreversibility: tierSeat?.value,
|
|
130
|
-
reversibilityProbe: tierSeat
|
|
132
|
+
reversibilityProbe: tierSeat?.read(reversibilityProbes),
|
|
131
133
|
abortSignal: abortController.signal,
|
|
132
134
|
...(notifyPermissionDenied ? { permissionDenied: notifyPermissionDenied } : {}),
|
|
133
135
|
onHookError: notifyHookError,
|
|
134
|
-
shellGated: tierSeat
|
|
136
|
+
shellGated: tierSeat?.shellGated(shellGatedTools, toolMarkSources) === true,
|
|
135
137
|
...(autoModeDecider.current !== undefined ? { autoMode: { decider: autoModeDecider.current, ...(autoModeDenialTracking.current !== undefined ? { denialTracking: autoModeDenialTracking.current } : {}), unavailable: classifierUnavailableSeat({ station: "leg", ...(autoModeDecider.current.timeoutMs !== undefined ? { budgetMs: autoModeDecider.current.timeoutMs } : {}), approvalSeat: askReachesPerson(askRoute), onNotice: deps.onNotice, sessionId, runId }) } } : {}),
|
|
136
138
|
permissionMode: () => permissionMode.current,
|
|
137
|
-
...(
|
|
139
|
+
...(markSources.size > 0 ? { toolMarkSources: markSources } : {}),
|
|
138
140
|
...(a2aIsolatePeers === true ? { a2aIsolatePeers: true } : {}),
|
|
139
141
|
onModeReleased,
|
|
140
142
|
ancestorArbitrationOf: (toolCallId) => ancestorArbitrations.get(toolCallId),
|
|
@@ -243,6 +245,7 @@ export function prepareGateStations(input) {
|
|
|
243
245
|
}
|
|
244
246
|
let content = e.content;
|
|
245
247
|
let changed = false;
|
|
248
|
+
const post = { toolName: rosterToolName(toolRosterDeltas.current, e.toolName) };
|
|
246
249
|
if (e.isError) {
|
|
247
250
|
if (hooks?.postToolUseFailure) {
|
|
248
251
|
let clonedDetails = e.details;
|
|
@@ -251,7 +254,8 @@ export function prepareGateStations(input) {
|
|
|
251
254
|
}
|
|
252
255
|
catch {
|
|
253
256
|
}
|
|
254
|
-
const
|
|
257
|
+
const failedName = wireOf("hook.postToolUseFailure", post);
|
|
258
|
+
const failureSeat = await runHookSeat("postToolUseFailure", { timeoutMs: hookTimeoutMs, signal: abortController.signal }, (sig) => hooks.postToolUseFailure(failedName, e.input, {
|
|
255
259
|
error: e.content
|
|
256
260
|
.filter((c) => c.type === "text")
|
|
257
261
|
.map((c) => c.text)
|
|
@@ -259,7 +263,7 @@ export function prepareGateStations(input) {
|
|
|
259
263
|
isInterrupt: abortController.signal.aborted || spec.signal?.aborted === true,
|
|
260
264
|
content: e.content.map((c) => ({ ...c })),
|
|
261
265
|
details: clonedDetails,
|
|
262
|
-
}, { toolCallId: e.toolCallId, toolName:
|
|
266
|
+
}, { toolCallId: e.toolCallId, toolName: failedName, signal: sig, ...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}), ...(hookEnvFace !== undefined ? { env: hookEnvFace } : {}), identity: hookIdentity }));
|
|
263
267
|
if (failureSeat.expired) {
|
|
264
268
|
notifyOwnHookCrash(hookSeatExpiredError("postToolUseFailure", hookTimeoutMs, failureSeat.cause, "the observation was abandoned and its additionalContext dropped; the failure result itself is unchanged"));
|
|
265
269
|
}
|
|
@@ -271,7 +275,8 @@ export function prepareGateStations(input) {
|
|
|
271
275
|
}
|
|
272
276
|
}
|
|
273
277
|
else if (hooks?.postToolUse) {
|
|
274
|
-
const
|
|
278
|
+
const ranName = wireOf("hook.postToolUse", post);
|
|
279
|
+
const successSeat = await runHookSeat("postToolUse", { timeoutMs: hookTimeoutMs, signal: abortController.signal }, (sig) => hooks.postToolUse(ranName, e.input, { content: e.content, details: e.details, isError: e.isError }, { toolCallId: e.toolCallId, toolName: ranName, signal: sig, ...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}), ...(hookEnvFace !== undefined ? { env: hookEnvFace } : {}), identity: hookIdentity }));
|
|
275
280
|
if (successSeat.expired) {
|
|
276
281
|
notifyOwnHookCrash(hookSeatExpiredError("postToolUse", hookTimeoutMs, successSeat.cause, "the seat was abandoned; the tool result is delivered to the model UNMODIFIED (no updatedOutput, no additionalContext)"));
|
|
277
282
|
}
|
|
@@ -36,7 +36,7 @@ export function prepareInheritedGate(input) {
|
|
|
36
36
|
if (modeSeed.migrated !== undefined) {
|
|
37
37
|
deliverEngineNotice(deps.onNotice, {
|
|
38
38
|
code: "resume.legacy_mode_seed_migrated",
|
|
39
|
-
message: `the resumed checkpoint row carried pre-mode keys (${modeSeed.migrated.droppedKeys.join(", ")}); read once into ${modeSeed.migrated.permissionMode !== undefined ? `permissionMode: "${modeSeed.migrated.permissionMode}"` : "no mode of its own (the chain's ceiling stands)"}${modeSeed.shellGate !== undefined ? ' with the shell floor "always" kept' : ""} — the next suspend writes the one key;
|
|
39
|
+
message: `the resumed checkpoint row carried pre-mode keys (${modeSeed.migrated.droppedKeys.join(", ")}); read once into ${modeSeed.migrated.permissionMode !== undefined ? `permissionMode: "${modeSeed.migrated.permissionMode}"` : "no mode of its own (the chain's ceiling stands)"}${modeSeed.shellGate !== undefined ? ' with the shell floor "always" kept' : ""} — the next suspend writes the one key; this migration is kept through 7.37.x and removed in 7.38.0, which refuses a row still carrying these keys (resume or abandon such rows before upgrading)`,
|
|
40
40
|
detail: { migrated: { ...(modeSeed.migrated.permissionMode !== undefined ? { permissionMode: modeSeed.migrated.permissionMode } : {}), droppedKeys: [...modeSeed.migrated.droppedKeys] }, sessionId, runId },
|
|
41
41
|
});
|
|
42
42
|
}
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* decomposition exists to make explicit.
|
|
10
10
|
*
|
|
11
11
|
* The thirteen-argument park closure is unchanged: twelve positional seats then the structural {@link AskCarry}, the
|
|
12
|
-
* shape the gate's one invocation site (`parkWith`) holds. The ask's origin word and
|
|
13
|
-
* carry onto the row
|
|
12
|
+
* shape the gate's one invocation site (`parkWith`) holds. The ask's origin word, class pair and denial-limit fallback
|
|
13
|
+
* ride the carry onto the row — this phase mints no origin or class, changes no word, adds no arm to the closed set.
|
|
14
14
|
*
|
|
15
15
|
* SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await of its own (both closures run per tool call), so the
|
|
16
16
|
* phase adds no yield between the boundary parks before it and the gate station after it.
|
|
@@ -116,6 +116,8 @@ export interface PrepareParkAskInput {
|
|
|
116
116
|
shellGatedTools: ReadonlySet<string>;
|
|
117
117
|
/** borrowed-readonly — the per-tool tier, read only for the name the call's tier seat is read under (as at the gate). */
|
|
118
118
|
irreversibilityTier: IrreversibilityTier;
|
|
119
|
+
/** borrowed-readonly — who marked each name's seat (the descriptor's `shellGated` reads it as the gate does). */
|
|
120
|
+
toolMarkSources: ReadonlyMap<string, ReadonlySet<import("../ask-origin.js").ToolMarkSource>>;
|
|
119
121
|
/** borrowed-readonly — the operator's shell floor (`"always"`), persisted on a shell-gated descriptor; undefined = no floor. */
|
|
120
122
|
effectiveShellGate: ShellGateDoctrine | undefined;
|
|
121
123
|
/** borrowed-readonly — the EFFECTIVE durable-approval opt-in (the caller's, or the synthesized mandate scope): picks the
|
|
@@ -15,7 +15,8 @@ import { checkpointScopeOf } from "./checkpoint-scope.js";
|
|
|
15
15
|
import { CONTENT_ASK_BINDING_CAP } from "./content-ask-bindings.js";
|
|
16
16
|
import { DEFAULT_UNATTENDED_APPROVAL_TTL_MS, publishCommittedSuspend, sanitizedTtlMs } from "./park-commit.js";
|
|
17
17
|
import { remoteEnvFailureNote } from "./remote-env-retry.js";
|
|
18
|
-
import { callFaceSeat,
|
|
18
|
+
import { callFaceSeat, parkedToolIdentity, parkedToolFace } from "../tool-roster.js";
|
|
19
|
+
import { seatOf, wireOf } from "../tool-name-readings.js";
|
|
19
20
|
class ParkRefusal extends Error {
|
|
20
21
|
constructor(message, options) {
|
|
21
22
|
super(message, options);
|
|
@@ -26,7 +27,7 @@ const wordlessDemand = (toolName) => {
|
|
|
26
27
|
throw new Error(`a real-approval demand for "${toolName}" reached the park with no origin word — the origin stamp precedes every park`);
|
|
27
28
|
};
|
|
28
29
|
export function prepareParkAsk(input) {
|
|
29
|
-
const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef } = input;
|
|
30
|
+
const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef } = input;
|
|
30
31
|
if (askLane === undefined || saga === undefined)
|
|
31
32
|
return { parkAsk: undefined };
|
|
32
33
|
const { composedCallSignal, approvalPreviewOf } = askLane;
|
|
@@ -114,7 +115,7 @@ export function prepareParkAsk(input) {
|
|
|
114
115
|
};
|
|
115
116
|
const suspendAsk = parkLaneArmed && checkpointStore !== undefined
|
|
116
117
|
? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause, segmentCoverage, matchedAskRule, probeMandated, carry) => {
|
|
117
|
-
const syncFirstEligible =
|
|
118
|
+
const syncFirstEligible = wireOf("park.syncFirstRoute", req) === ASK_USER_QUESTION_TOOL_NAME ? contentAskRoutable(req.toolCallId) : isLiveApproverSeat(onAsk);
|
|
118
119
|
if (syncFirstEligible &&
|
|
119
120
|
runtimeCaps?.forceDurableGate !== true &&
|
|
120
121
|
liveFaceUnavailable !== true &&
|
|
@@ -172,11 +173,7 @@ export function prepareParkAsk(input) {
|
|
|
172
173
|
if (reprojectedRaced.tag === "threw")
|
|
173
174
|
throw reprojectedRaced.error;
|
|
174
175
|
const reprojected = refuseOutOfContractDecision(reprojectedRaced.value);
|
|
175
|
-
const
|
|
176
|
-
const rewroteTheFiledValue = reprojected.updatedInput !== undefined &&
|
|
177
|
-
!(demanded?.ok === true &&
|
|
178
|
-
samePlainValue(demanded.value, parkedArgs) &&
|
|
179
|
-
samePlainValue(reprojected.updatedInput, parkedArgs));
|
|
176
|
+
const rewroteTheFiledValue = reprojected.updatedInput !== undefined && !samePlainValue(reprojected.updatedInput, parkedArgs);
|
|
180
177
|
if (reprojected.action === "deny" || rewroteTheFiledValue) {
|
|
181
178
|
throw new ParkRefusal(`the arguments of "${req.toolName}" change shape when stored by this checkpoint backend, and the deployment's tool policy does not accept the stored shape` +
|
|
182
179
|
`${reprojected.action === "deny" && reprojected.message ? `: ${reprojected.message}` : ""} — ` +
|
|
@@ -234,7 +231,7 @@ export function prepareParkAsk(input) {
|
|
|
234
231
|
remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
|
|
235
232
|
}
|
|
236
233
|
token = mintCheckpointToken();
|
|
237
|
-
const tierSeat =
|
|
234
|
+
const tierSeat = seatOf("park.riskDescriptor", irreversibilityTier, req);
|
|
238
235
|
const riskDescriptor = buildRiskDescriptor({
|
|
239
236
|
toolName: req.toolName,
|
|
240
237
|
args: parkedArgs,
|
|
@@ -242,7 +239,7 @@ export function prepareParkAsk(input) {
|
|
|
242
239
|
...(shadowedRule !== undefined ? { shadowedRule } : {}),
|
|
243
240
|
...(probeReason !== undefined ? { probeReason } : {}),
|
|
244
241
|
...(probeCause !== undefined ? { probeCause } : {}),
|
|
245
|
-
shellGated: tierSeat
|
|
242
|
+
shellGated: tierSeat?.shellGated(shellGatedTools, toolMarkSources) === true,
|
|
246
243
|
...(effectiveShellGate !== undefined ? { shellGateDoctrine: effectiveShellGate } : {}),
|
|
247
244
|
});
|
|
248
245
|
gate =
|
|
@@ -303,7 +300,7 @@ export function prepareParkAsk(input) {
|
|
|
303
300
|
kind: "tool_approval",
|
|
304
301
|
toolCallId: req.toolCallId,
|
|
305
302
|
toolName: req.toolName,
|
|
306
|
-
...parkedToolIdentity(toolRosterDeltas.current, req.toolName), ...parkedToolFace(toolRosterDeltas.current, req
|
|
303
|
+
...parkedToolIdentity(toolRosterDeltas.current, req.toolName), ...parkedToolFace(toolRosterDeltas.current, req),
|
|
307
304
|
args: parkedArgs,
|
|
308
305
|
...(() => {
|
|
309
306
|
const p = approvalPreviewOf(req.toolName, parkedArgs);
|
|
@@ -34,6 +34,7 @@ import { type ActiveSkillScope } from "./active-skill-scope.js";
|
|
|
34
34
|
import type { InheritedGate, IrreversibilityTier, PrepareResume, RunInternals, StopForDenialLimit } from "./contracts.js";
|
|
35
35
|
import { type Arbitration } from "../ask-origin.js";
|
|
36
36
|
import { type ModeGateSeats, type PermissionModeController } from "../mode-policy.js";
|
|
37
|
+
import { type InheritedAskGrant } from "./inherited-ask-grants.js";
|
|
37
38
|
import { createRuleOffersOf, type PermissionRuleLanes } from "./permission-rule-lanes.js";
|
|
38
39
|
import type { ToolCallLike } from "../tool-registry.js";
|
|
39
40
|
export interface PreparePolicyChainInput {
|
|
@@ -45,7 +46,7 @@ export interface PreparePolicyChainInput {
|
|
|
45
46
|
* `permissionRuleStore` / `localOwnerRules` (the lanes and the offer factory), `onError` / `onNotice` (the advisories,
|
|
46
47
|
* the refusal disclosures, the late-ask observer), `tracer` (the admission and rule-store frames), `checkpointStore` (the park facility). Receiver preserved;
|
|
47
48
|
* the advisory dedupe is KEYED on this object's identity. */
|
|
48
|
-
deps: Pick<RunnerDeps, "sessionPolicyStore" | "hooks" | "permissionRuleStore" | "localOwnerRules" | "onError" | "onNotice" | "tracer" | "checkpointStore">;
|
|
49
|
+
deps: Pick<RunnerDeps, "sessionPolicyStore" | "hooks" | "permissionRuleStore" | "localOwnerRules" | "onError" | "onNotice" | "tracer" | "checkpointStore" | "a2a">;
|
|
49
50
|
/** borrowed-readonly — the trusted spawn-side channel: `parentTaskId` (the from-subagent bit), `explicitAgentName` /
|
|
50
51
|
* `agentName` (the source agent name) — the ask source identity's local, trusted facts. */
|
|
51
52
|
internals: Pick<RunInternals, "parentTaskId" | "explicitAgentName" | "agentName"> | undefined;
|
|
@@ -85,6 +86,8 @@ export interface PreparePolicyChainInput {
|
|
|
85
86
|
envHandToolNames: ReadonlySet<string>;
|
|
86
87
|
/** borrowed-readonly — the shell-seat provenance set (the offer factory's `shellGated` mandate term, read per call). */
|
|
87
88
|
shellGatedTools: ReadonlySet<string>;
|
|
89
|
+
/** borrowed-readonly — who marked each name's seat (the offer factory's `shellGated` term reads it as the gate does). */
|
|
90
|
+
toolMarkSources: ReadonlyMap<string, ReadonlySet<import("../ask-origin.js").ToolMarkSource>>;
|
|
88
91
|
/** borrowed-readonly — the tracked-cwd seat, read per call by the lanes and the offer factory (never written here). */
|
|
89
92
|
handsCwdRef: CwdRef | undefined;
|
|
90
93
|
/** borrowed-readonly — the compiled read-deny judge, or undefined (the offer factory's directory negative control). */
|
|
@@ -184,13 +187,7 @@ export interface PreparePolicyChainResult {
|
|
|
184
187
|
}>>;
|
|
185
188
|
/** borrowed-mutable — the grant record. Writers: the inherited arms here (record a clean allow at a live approver);
|
|
186
189
|
* the gate station (consume on reuse, sweep at settlement). */
|
|
187
|
-
inheritedAskGrants: Map<string,
|
|
188
|
-
approver: unknown;
|
|
189
|
-
argsJson: string;
|
|
190
|
-
presented: unknown;
|
|
191
|
-
waitMs: number;
|
|
192
|
-
settled?: object;
|
|
193
|
-
}>;
|
|
190
|
+
inheritedAskGrants: Map<string, InheritedAskGrant>;
|
|
194
191
|
/** owned — the SINGLE AskRequest source-identity factory every resolveAsk construction site spreads. */
|
|
195
192
|
askSourceIdentity: () => {
|
|
196
193
|
principal?: string;
|