@sema-agent/core 7.44.0 → 7.45.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 +172 -0
- package/dist/agents/subagent.js +33 -14
- package/dist/agents/tool-filter.d.ts +65 -4
- package/dist/agents/tool-filter.js +127 -4
- package/dist/core/agent-definition.d.ts +2 -2
- package/dist/core/allow-entry.d.ts +25 -1
- package/dist/core/allow-entry.js +29 -4
- package/dist/core/ask-origin.d.ts +23 -4
- package/dist/core/ask-origin.js +10 -4
- package/dist/core/checkpoint-store.d.ts +2 -2
- package/dist/core/decision-receipt.js +1 -1
- package/dist/core/effective-path-target.d.ts +7 -6
- package/dist/core/effective-path-target.js +3 -7
- package/dist/core/engine-notice.d.ts +20 -3
- package/dist/core/engine-notice.js +12 -6
- package/dist/core/gate-lanes.js +2 -0
- package/dist/core/gate-outcome.d.ts +5 -0
- package/dist/core/gate-outcome.js +2 -0
- package/dist/core/hooks.d.ts +2 -0
- package/dist/core/memory-engine/content-origin.d.ts +0 -34
- package/dist/core/memory-engine/content-origin.js +9 -4
- package/dist/core/memory-engine/layout.d.ts +5 -1
- package/dist/core/memory-engine/layout.js +1 -0
- package/dist/core/permission-rules.d.ts +4 -0
- package/dist/core/permission-rules.js +5 -2
- package/dist/core/real-approval.d.ts +9 -6
- package/dist/core/real-approval.js +5 -5
- package/dist/core/runner/child-tree.d.ts +13 -0
- package/dist/core/runner/child-tree.js +3 -0
- package/dist/core/runner/contracts.d.ts +8 -2
- package/dist/core/runner/prepare-ask-lane.d.ts +2 -0
- package/dist/core/runner/prepare-ask-lane.js +2 -2
- package/dist/core/runner/prepare-config-doors.js +3 -1
- package/dist/core/runner/prepare-execution-env.js +2 -1
- package/dist/core/runner/prepare-gate-stations.d.ts +3 -0
- package/dist/core/runner/prepare-gate-stations.js +2 -1
- package/dist/core/runner/prepare-listings.d.ts +4 -4
- package/dist/core/runner/prepare-listings.js +2 -22
- package/dist/core/runner/prepare-park-ask.d.ts +2 -0
- package/dist/core/runner/prepare-park-ask.js +2 -2
- package/dist/core/runner/prepare-policy-chain.d.ts +2 -0
- package/dist/core/runner/prepare-policy-chain.js +4 -2
- package/dist/core/runner/prepare-task.js +10 -6
- package/dist/core/runner/resume-policy-outcome.js +2 -1
- package/dist/core/runner/run-leg.js +1 -1
- package/dist/core/runner/run-notification-lane.js +2 -2
- package/dist/core/runner/runtask.d.ts +2 -0
- package/dist/core/runner/runtask.js +2 -0
- package/dist/core/runner/stream-steer-verb.js +2 -2
- package/dist/core/runner/sub-agent-deny-lists.d.ts +32 -0
- package/dist/core/runner/sub-agent-deny-lists.js +121 -0
- package/dist/core/runner/synthetic-tools.d.ts +1 -1
- package/dist/core/runner/synthetic-tools.js +3 -2
- package/dist/core/session-policy-store.d.ts +6 -2
- package/dist/core/session-policy-store.js +2 -1
- package/dist/core/skill-grant.d.ts +10 -2
- package/dist/core/skill-grant.js +20 -4
- package/dist/core/task-notification.d.ts +12 -1
- package/dist/core/task-notification.js +13 -1
- package/dist/core/task-spec.d.ts +5 -3
- package/dist/core/tool-policy.d.ts +15 -4
- package/dist/core/tool-policy.js +9 -13
- package/dist/core/tool-spec.d.ts +20 -2
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.js +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +5 -3
- package/dist/orchestration/workflow.js +2 -1
- package/dist/tools/fs/removal-targets.d.ts +82 -0
- package/dist/tools/fs/removal-targets.js +236 -0
- package/dist/tools/fs/shell-read-write-targets.d.ts +27 -0
- package/dist/tools/fs/shell-read-write-targets.js +1 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +33 -1
- package/wire-changes/core@7.45.0.json +1084 -0
|
@@ -125,6 +125,11 @@ export type SettlementParty = (typeof SETTLEMENT_PARTIES)[number];
|
|
|
125
125
|
export type SettlementPartiesMatchTheRecord = AssertAllKeysHandled<Exclude<Settlement["who"]["party"], SettlementParty> | Exclude<SettlementParty, Settlement["who"]["party"]>>;
|
|
126
126
|
/** Membership test for {@link SettlementParty}. */
|
|
127
127
|
export declare function isSettlementParty(v: unknown): v is SettlementParty;
|
|
128
|
+
/** Who REPORTED a durable decide (`hostDecision.decidedBy`): a person, the host's own SLA sweep at the row's deadline, or the
|
|
129
|
+
* deployment's policy — the closed set; a host's decide door mirrors the words it accepts from this one. */
|
|
130
|
+
export declare const HOST_DECIDERS: readonly ["person", "sla_timeout", "policy"];
|
|
131
|
+
export type HostDecider = (typeof HOST_DECIDERS)[number];
|
|
132
|
+
export declare const isHostDecider: (word: unknown) => word is HostDecider;
|
|
128
133
|
/** The DECIDER arms of {@link Settlement} — a person's and the deployment policy's, the two parties that
|
|
129
134
|
* answer an ask either way. Named so a site that both composes one and quotes its note can read the seat
|
|
130
135
|
* off the record instead of re-deriving it. */
|
|
@@ -25,6 +25,8 @@ const SETTLEMENT_PARTY_SET = new Set(SETTLEMENT_PARTIES);
|
|
|
25
25
|
export function isSettlementParty(v) {
|
|
26
26
|
return SETTLEMENT_PARTY_SET.has(v);
|
|
27
27
|
}
|
|
28
|
+
export const HOST_DECIDERS = Object.freeze(["person", "sla_timeout", "policy"]);
|
|
29
|
+
export const isHostDecider = (word) => HOST_DECIDERS.some((w) => w === word);
|
|
28
30
|
export function decidersNote(text) {
|
|
29
31
|
return text === "" ? undefined : text;
|
|
30
32
|
}
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -1466,6 +1466,8 @@ export interface ToolGateInput extends PermissionSeatInputs, SkillGateSeats, All
|
|
|
1466
1466
|
permissionMode?: () => import("./ask-origin.js").PermissionMode;
|
|
1467
1467
|
/** The leg's approval-seat form (`WiringManifest.ask.form`): the deny exit's way-out clause names a declaration, never a missing approver. */
|
|
1468
1468
|
askSeatForm?: import("./wiring-manifest.js").AskSeamForm;
|
|
1469
|
+
/** The read face the leg resolved, read per call into the class derivation (`roots` withholds the classifier from a `probe` question); absent ⇒ none resolved. */
|
|
1470
|
+
readFace?: () => import("../tools/fs/containment-face.js").ContainmentFace | undefined;
|
|
1469
1471
|
/** WHO put the tool's egress / irreversibility marks on the seat ({@link import("./ask-origin.js").ToolMarkSource});
|
|
1470
1472
|
* absent or empty with a mark present reads as the deployment's own (fail-closed: an operator's mandate). */
|
|
1471
1473
|
toolMarkSources?: ReadonlySet<import("./ask-origin.js").ToolMarkSource>;
|
|
@@ -1,37 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* design/178 §3 — the tool content-origin classification (three tiers by CAPABILITY, fail-closed).
|
|
3
|
-
*
|
|
4
|
-
* The question this face answers per mounted tool: can invoking it bring content from OUTSIDE the
|
|
5
|
-
* deployment's trust boundary into the session — and therefore, via the automatic harvest path,
|
|
6
|
-
* into long-term memory? Three tiers:
|
|
7
|
-
*
|
|
8
|
-
* - "external" — network-retrieval families and external protocol channels. Invocation marks the
|
|
9
|
-
* session's memory EXTERNALLY EXPOSED (one-way, durable; what the mark does at
|
|
10
|
-
* harvest is the provenance mode's question — design/336: tag-admit with an origin
|
|
11
|
-
* marker under "carry", full quarantine under "off").
|
|
12
|
-
* - "execution" — general execution (shell/exec). A NAMED residual channel: it can reach external
|
|
13
|
-
* content indirectly, but the two industry anchors this tier is calibrated against
|
|
14
|
-
* gate on network/protocol families only, and excluding every shell session would
|
|
15
|
-
* disable memory for the main coding scenario. Not polluting by default; the
|
|
16
|
-
* `execIsExternalContent` strict knob upgrades it to external.
|
|
17
|
-
* - "local" — purely local reads/computation.
|
|
18
|
-
*
|
|
19
|
-
* WHO CLASSIFIES (single-source rule): core built-ins declare `contentOrigin` on their own ToolSpec
|
|
20
|
-
* (the classification lives with the tool definition); protocol tools (MCP/A2A) are structurally
|
|
21
|
-
* external (the channel itself crosses the boundary); a HOST tool declares its own — and when it
|
|
22
|
-
* does not, the default is EXTERNAL (fail-closed: unknown = external), overridable only through the
|
|
23
|
-
* explicit `TaskSpec.memory.trustedTools` allowlist. A core-mounted built-in without a declaration
|
|
24
|
-
* defaults local: core owns those definitions, and the fail-closed default exists for tools core
|
|
25
|
-
* CANNOT vouch for, not for its own. A declaration is honored only when it SPELLS a member of the
|
|
26
|
-
* closed vocabulary — an unreadable one is not a declaration.
|
|
27
|
-
*
|
|
28
|
-
* The three tier names are BOUNDARY-RELATIVE, not statements about topology: `"local"` means "does
|
|
29
|
-
* not bring content from outside the deployment's trust boundary", which a unix socket to another
|
|
30
|
-
* process the deployment itself runs satisfies and a loopback HTTP call to somebody else's service
|
|
31
|
-
* does not. Distance is not the axis; whose content it is, is.
|
|
32
|
-
*
|
|
33
|
-
* There is deliberately no "gate inactive" state: every mounted tool gets a class.
|
|
34
|
-
*/
|
|
35
1
|
import type { ToolContentOrigin } from "../types.js";
|
|
36
2
|
export interface ClassifyToolContentOriginInput {
|
|
37
3
|
/** The tool's own `contentOrigin` declaration (ToolSpec/AgentTool carry) — wins when present AND
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { allowedToolNames, readDeniedEntry, readToolNameList } from "../../agents/tool-filter.js";
|
|
1
2
|
const TOOL_CONTENT_ORIGIN_SET = { external: true, execution: true, local: true };
|
|
2
3
|
export const TOOL_CONTENT_ORIGINS = Object.keys(TOOL_CONTENT_ORIGIN_SET);
|
|
3
4
|
export function isToolContentOrigin(value) {
|
|
@@ -27,10 +28,14 @@ export function delegationCallIsExternal(input) {
|
|
|
27
28
|
if (face === undefined)
|
|
28
29
|
return true;
|
|
29
30
|
const selectedFor = (f) => {
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
31
|
+
const deniedList = readToolNameList(f.denyTools);
|
|
32
|
+
const d = deniedList.kind === "entries" ? deniedList.entries : [];
|
|
33
|
+
const a = allowedToolNames(f.allowTools);
|
|
34
|
+
const denies = (t) => d.some((x) => {
|
|
35
|
+
const read = readDeniedEntry(x);
|
|
36
|
+
return read.kind === "name" && selectedBy(read.name, t);
|
|
37
|
+
});
|
|
38
|
+
return input.pool.filter((t) => (a === undefined || [...a].some((x) => selectedBy(x, t))) && !denies(t));
|
|
34
39
|
};
|
|
35
40
|
if (selectedFor(face).some((t) => input.isPolluting(t)))
|
|
36
41
|
return true;
|
|
@@ -259,6 +259,10 @@ export declare function adoptRootScopeLayout(rootPath: string, controlDir: strin
|
|
|
259
259
|
export declare const canonicalize: typeof physicalPathOfExisting;
|
|
260
260
|
/** True iff `child` (canonicalized) is `root` or inside it. */
|
|
261
261
|
export declare function isContainedIn(root: string, child: string): boolean;
|
|
262
|
+
/** The codes the store's physical form refuses an act with — the whole family, one closed set: {@link TARGET_OUTSIDE_STORE} and
|
|
263
|
+
* {@link SCOPE_HOME_NOT_DIRECTORY}. Each is a plain `Error` carrying `code` and a `detail` ({@link StoreFormRefusalDetail}). */
|
|
264
|
+
export declare const STORE_FORM_REFUSAL_CODES: readonly ["memory.target_outside_store", "memory.scope_home_not_directory"];
|
|
265
|
+
export type StoreFormRefusalCode = (typeof STORE_FORM_REFUSAL_CODES)[number];
|
|
262
266
|
/** The code an act on a store path is refused with when it would land outside the store ({@link landsOutsideStore}). */
|
|
263
267
|
export declare const TARGET_OUTSIDE_STORE = "memory.target_outside_store";
|
|
264
268
|
/** The code a scope home that is not a real directory is refused with ({@link assertHomePhysicalForm}). */
|
|
@@ -283,7 +287,7 @@ export type StoreFormRefusalDetail = {
|
|
|
283
287
|
* the engine's own account is intact, the layout it was handed is not the layout it needs) carrying a
|
|
284
288
|
* `code` and a `detail`. */
|
|
285
289
|
export type StoreFormRefusal = Error & {
|
|
286
|
-
code:
|
|
290
|
+
code: StoreFormRefusalCode;
|
|
287
291
|
detail: StoreFormRefusalDetail;
|
|
288
292
|
};
|
|
289
293
|
/** Seat 1 — an act the store's physical form refuses: `act` names what was refused (no path), `path` is the
|
|
@@ -731,6 +731,7 @@ export function isContainedIn(root, child) {
|
|
|
731
731
|
const c = canonicalize(child);
|
|
732
732
|
return c === r || c.startsWith(`${r}${sep}`);
|
|
733
733
|
}
|
|
734
|
+
export const STORE_FORM_REFUSAL_CODES = Object.freeze(["memory.target_outside_store", "memory.scope_home_not_directory"]);
|
|
734
735
|
export const TARGET_OUTSIDE_STORE = "memory.target_outside_store";
|
|
735
736
|
export const SCOPE_HOME_NOT_DIRECTORY = "memory.scope_home_not_directory";
|
|
736
737
|
export const SCOPE_HOME_FORMS = Object.freeze(["symlink", "file", "other"]);
|
|
@@ -149,6 +149,10 @@ export interface PermissionRulePolicyOptions {
|
|
|
149
149
|
* own; #644). Facts of the DEPLOYMENT and the task, never of the rule — a pattern is stored as spelled.
|
|
150
150
|
* A rule whose base is absent (or not an absolute path) here is refused at compile time
|
|
151
151
|
* (`unsupported.path_base`, naming the base — {@link ruleBasesNeeded} is the same classification).
|
|
152
|
+
* A call's own target resolves against these bases as given; when they hold neither `root` nor `cwd`,
|
|
153
|
+
* against the request's (`ToolCallRequest.root` / `.cwd`, stamped by the Runner) — never a mix of the two
|
|
154
|
+
* sources. A target neither supplies the base for is unreadable: every path rule able to reach the call
|
|
155
|
+
* answers it with the fail-closed ask (`ruleUnreadable`).
|
|
152
156
|
*/
|
|
153
157
|
pathBases?: PathRuleBases;
|
|
154
158
|
}
|
|
@@ -366,10 +366,13 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
366
366
|
...(callFace !== undefined ? faceRules[behavior].filter((r) => r.faces.includes(callFace)) : []),
|
|
367
367
|
];
|
|
368
368
|
const commandParam = catalogRuleFaceOf("Bash")?.primaryParams[0] ?? "command";
|
|
369
|
+
const policyNamesTree = pathBases.root !== undefined || pathBases.cwd !== undefined;
|
|
370
|
+
const callBasesOf = (req) => policyNamesTree ? pathBases : { ...pathBases, ...(typeof req.root === "string" ? { root: req.root } : {}), ...(typeof req.cwd === "string" ? { cwd: req.cwd } : {}) };
|
|
369
371
|
const contentHit = (lane, req) => {
|
|
370
372
|
if (lane.length === 0)
|
|
371
373
|
return undefined;
|
|
372
|
-
const
|
|
374
|
+
const bases = callBasesOf(req);
|
|
375
|
+
const target = effectivePathTargetOf(req, protectivePathTargetOf(req), bases);
|
|
373
376
|
const command = isPlainObjectArgs(req.args) ? req.args[commandParam] : undefined;
|
|
374
377
|
let unreadable;
|
|
375
378
|
for (const r of lane) {
|
|
@@ -386,7 +389,7 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
386
389
|
unreadable ??= { rule: r, unreadable: target.reason };
|
|
387
390
|
continue;
|
|
388
391
|
}
|
|
389
|
-
const outcome = pathRuleReachOf(r.parsed, target.identity,
|
|
392
|
+
const outcome = pathRuleReachOf(r.parsed, target.identity, bases);
|
|
390
393
|
if (outcome.reach === "reached")
|
|
391
394
|
return { rule: r };
|
|
392
395
|
if (outcome.reach === "unreadable")
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* may still refuse it.
|
|
7
7
|
*/
|
|
8
8
|
import type { Checkpoint } from "./checkpoint-store.js";
|
|
9
|
+
import type { HostDecider } from "./gate-outcome.js";
|
|
9
10
|
import type { OrgGateVerdict } from "./hooks.js";
|
|
10
11
|
import type { AskRequest, PermissionResult, ResolvedAsk } from "./tool-policy.js";
|
|
11
12
|
/** The synchronous seat's refusal of a configuration's allow: one settlement word for both forms
|
|
@@ -16,10 +17,12 @@ export declare function unjudgedAllowRefused(req: AskRequest, by: "blanket" | "p
|
|
|
16
17
|
/** A durable row demands a person when its gate carries the real-approval bit (the gate's own, which every resume
|
|
17
18
|
* check reads; the pending action's `requiresRealApproval` is its display twin). */
|
|
18
19
|
export declare function rowDemandsAPerson(cp: Checkpoint): boolean;
|
|
19
|
-
/** A question a
|
|
20
|
-
*
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
/** A question a decide is held against, in each form a station holds one: the parked row, a policy's re-check of an
|
|
21
|
+
* approver's edit, the organization's CURRENT verdict about the call, and the request an approver was handed (the card a
|
|
22
|
+
* live door answers). */
|
|
23
|
+
export type HeldQuestion = Checkpoint | PermissionResult | OrgGateVerdict | AskRequest;
|
|
24
|
+
/** Whether a decide made by `decidedBy` cannot answer the held question: a question demanding a person is not
|
|
23
25
|
* the policy's to answer. One predicate for every station that holds a decide against such a question — the resume
|
|
24
|
-
* entry (the row, an edit's re-check, the org's current verdict)
|
|
25
|
-
|
|
26
|
+
* entry (the row, an edit's re-check, the org's current verdict), the execution belt (the last two, re-read) and a live
|
|
27
|
+
* door answering the request an approver was handed. */
|
|
28
|
+
export declare function policyCannotAnswer(question: HeldQuestion, decidedBy: HostDecider): boolean;
|
|
@@ -16,11 +16,11 @@ export function rowDemandsAPerson(cp) {
|
|
|
16
16
|
return cp.gate.kind === "irreversible_ask" && cp.gate.realApproval !== undefined;
|
|
17
17
|
}
|
|
18
18
|
function demandsAPerson(question) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return question
|
|
19
|
+
const gate = Reflect.get(question, "gate");
|
|
20
|
+
const verdict = Reflect.get(question, "verdict");
|
|
21
|
+
const row = typeof gate === "object" && gate !== null && rowDemandsAPerson(question);
|
|
22
|
+
const org = Reflect.get(question, "status") === "available" && typeof verdict === "object" && verdict !== null && Reflect.get(verdict, "behavior") === "ask";
|
|
23
|
+
return row || Reflect.get(question, "requiresRealApproval") === true || org;
|
|
24
24
|
}
|
|
25
25
|
export function policyCannotAnswer(question, decidedBy) {
|
|
26
26
|
return decidedBy === "policy" && demandsAPerson(question);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RunnerDeps } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Whether this deployment can place a child agent in a tree OTHER than the caller's — the one fact behind
|
|
4
|
+
* every request of that shape: an explicit `cwd`, a workflow agent's `isolation: "worktree"`, and the
|
|
5
|
+
* delegation tool's per-call or per-definition `isolation: "worktree"`.
|
|
6
|
+
*
|
|
7
|
+
* Only an `executionEnvFactory` can: it builds one execution env per task, so a child can be rooted
|
|
8
|
+
* wherever the request says. A static `executionEnv` is ONE shared env with ONE cwd — every child that
|
|
9
|
+
* runs on it runs in the caller's root, whatever tree it was asked to work in. A request the deployment
|
|
10
|
+
* cannot honor is refused before the child starts (and, for a worktree, before one is minted); each site
|
|
11
|
+
* keeps its own code and sentence for the refusal, but none decides the condition itself.
|
|
12
|
+
*/
|
|
13
|
+
export declare function deploymentCanRerootChild(deps: Pick<RunnerDeps, "executionEnvFactory">): boolean;
|
|
@@ -31,7 +31,7 @@ import type { MaterializedA2a } from "../a2a.js";
|
|
|
31
31
|
import type { CompactionForkContext, CompactionPhaseDurations, MaybeCompactOptions, RapidRefillState } from "../auto-compaction.js";
|
|
32
32
|
import type { AutoModeArmingRecipe } from "../auto-mode-arming.js";
|
|
33
33
|
import type { AutoModeDecider, AutoModeDenialTracker, DenialLimitFallbackFace } from "../auto-mode.js";
|
|
34
|
-
import type { SettlementKind } from "../gate-outcome.js";
|
|
34
|
+
import type { HostDecider, SettlementKind } from "../gate-outcome.js";
|
|
35
35
|
import type { CacheBreakDetector, ToolFingerprintInput } from "../cache-break-detector.js";
|
|
36
36
|
import type { Checkpoint, CheckpointState, CheckpointStore, CheckpointToken, PendingSteerEntry, PlatformLimitReason, ReopenReason, ResourceLedger, ResourceLimitReason, ResumeOutcome, SerializedCheckpointState } from "../checkpoint-store.js";
|
|
37
37
|
import type { ExecutionOutcomeRecordWord } from "../checkpoint-execution-record.js";
|
|
@@ -391,6 +391,8 @@ export interface Prepared {
|
|
|
391
391
|
/** The per-task logical cwd ref when a real shell is mounted (else undefined). The Runner
|
|
392
392
|
* reads `cwdRef.current` after each tool to detect a `cd` move and emit `workspace_changed`. */
|
|
393
393
|
cwdRef?: CwdRef;
|
|
394
|
+
/** The root the hand tools resolve root-relative spellings against (present with hands; read-only hands included). */
|
|
395
|
+
handsRoot?: string;
|
|
394
396
|
/** The run's TOOL-PROGRESS lane seat, late-bound for the same reason `harnessRef` is (the tool band is
|
|
395
397
|
* built during prepare; the lane needs `pushContent` and `ident`, which exist only after it returns).
|
|
396
398
|
* The driver binds `current` right after `pushContent` exists. Always present on a completed prepare —
|
|
@@ -442,7 +444,7 @@ export interface Prepared {
|
|
|
442
444
|
toolCallId: string;
|
|
443
445
|
toolName: string;
|
|
444
446
|
args: unknown;
|
|
445
|
-
decidedBy:
|
|
447
|
+
decidedBy: HostDecider;
|
|
446
448
|
}) => Promise<{
|
|
447
449
|
readonly refused: string;
|
|
448
450
|
} | {
|
|
@@ -2748,6 +2750,10 @@ export interface RunnerSelfSeat {
|
|
|
2748
2750
|
toolPolicy?: RunnerDeps["toolPolicy"];
|
|
2749
2751
|
hooks?: RunnerDeps["hooks"];
|
|
2750
2752
|
};
|
|
2753
|
+
/** Whether a child task THIS runner runs can be placed in a tree other than the caller's (an explicit cwd, a worktree):
|
|
2754
|
+
* true only when its deps carry an `executionEnvFactory` (live read — `swapDeps` replaces the deps). The delegation tool asks
|
|
2755
|
+
* the runner that will run the child, not the one that ran the parent leg. Absent on a hand-built seat ⇒ read as "cannot say" ⇒ no; a hand-built seat answers for the runner its `runTask` forwards to. */
|
|
2756
|
+
canRerootChild?(): boolean;
|
|
2751
2757
|
/** Fire `RunnerDeps.onTaskOutcome` through the swallow-guarded chokepoint (thin compositions emit at their terminal state). */
|
|
2752
2758
|
emitTaskOutcome(outcome: TaskOutcome): void;
|
|
2753
2759
|
/** Run a task to completion. `internals` is the TRUSTED run-scoped channel (never a TaskSpec field). */
|
|
@@ -28,6 +28,8 @@ export interface PrepareAskLaneInput {
|
|
|
28
28
|
/** borrowed-readonly — the hand's live tracked cwd, or undefined (hands-less). Read per call at RUN time (`current`),
|
|
29
29
|
* never captured: the fs hand tools resolve their relative paths against this very ref. Not written here. */
|
|
30
30
|
handsCwdRef: CwdRef | undefined;
|
|
31
|
+
/** borrowed-readonly — the root the hand tools resolve root-relative spellings against, or undefined (hands-less). */
|
|
32
|
+
handsRoot: string | undefined;
|
|
31
33
|
/** borrowed-readonly — the run's advertised-writable-directory seat, or undefined (hands-less / nothing advertised). `roots` is read at RUN time, never captured: it is the fence's own admitted list and the write gate's exemption domain must be that same list. Not written here. */
|
|
32
34
|
advertisedWritableDirs: Pick<AdvertisedWritableDirs, "roots"> | undefined;
|
|
33
35
|
/** borrowed-readonly — the shared mount roster; read at RUN time by the approval-preview projection (alias-aware
|
|
@@ -59,14 +59,14 @@ function resolveApprovalPreview(tools, toolName, args) {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
export function prepareAskLane(input) {
|
|
62
|
-
const { gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, grantBindingOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot } = input;
|
|
62
|
+
const { gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, handsRoot, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, grantBindingOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot } = input;
|
|
63
63
|
if (!gateMachineryActive)
|
|
64
64
|
return { askLane: undefined };
|
|
65
65
|
const composedCallSignal = (callSignal) => composeCallSignal(abortController.signal, callSignal);
|
|
66
66
|
const adjudicate = effectivePolicy
|
|
67
67
|
? (req, callSignal) => {
|
|
68
68
|
const signal = composedCallSignal(callSignal);
|
|
69
|
-
return raceAbort(Promise.resolve(effectivePolicy.check({ ...req, budget: budgetSnapshot, ...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}), ...advertisedWritableRootsSeat(advertisedWritableDirs) }, signal)), signal, () => ({
|
|
69
|
+
return raceAbort(Promise.resolve(effectivePolicy.check({ ...req, budget: budgetSnapshot, ...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}), ...(handsRoot !== undefined ? { root: handsRoot } : {}), ...advertisedWritableRootsSeat(advertisedWritableDirs) }, signal)), signal, () => ({
|
|
70
70
|
action: "deny",
|
|
71
71
|
message: "policy check aborted (task timed out or cancelled)",
|
|
72
72
|
}));
|
|
@@ -14,6 +14,7 @@ import { applyToolModelGate, assertRestoreGatedToolsValue, modelIdTail } from ".
|
|
|
14
14
|
import { resolveUsageWindows } from "../usage-window-store.js";
|
|
15
15
|
import { deriveAskEffective, resolveAskSeat, resolveQuestionSeam } from "../wiring-manifest.js";
|
|
16
16
|
import { assertPeerPresenceValue } from "../../agents/peer-directory.js";
|
|
17
|
+
import { snapshotDefinitionLists } from "../../agents/tool-filter.js";
|
|
17
18
|
import { TOTAL_TOKENS_REMINDER_MODES } from "./turn-attachments.js";
|
|
18
19
|
const TASK_LIMIT_KEY_DICT = {
|
|
19
20
|
maxTokens: true,
|
|
@@ -359,9 +360,10 @@ export function prepareConfigDoors(input) {
|
|
|
359
360
|
e.code = "config.agents.no_delegation_tool";
|
|
360
361
|
throw e;
|
|
361
362
|
}
|
|
362
|
-
const perTask = spec.agents;
|
|
363
|
+
const perTask = spec.agents.map(snapshotDefinitionLists);
|
|
363
364
|
spec = {
|
|
364
365
|
...spec,
|
|
366
|
+
agents: perTask,
|
|
365
367
|
tools: pool.map((t) => {
|
|
366
368
|
if (typeof t.withAgents !== "function")
|
|
367
369
|
return t;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { StubExecutionEnv } from "../stub-env.js";
|
|
2
2
|
import { hasDestroy } from "../remote-env.js";
|
|
3
3
|
import { announceMalformedCapabilities } from "./env-capability-census.js";
|
|
4
|
+
import { deploymentCanRerootChild } from "./child-tree.js";
|
|
4
5
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
5
6
|
export async function prepareExecutionEnv(input) {
|
|
6
7
|
const { deps, spec, internals, resume, placementRootResolved, sessionId, forgetOnThrow, rollback } = input;
|
|
7
8
|
let ownedEnv;
|
|
8
9
|
let ownedEnvObservedAt;
|
|
9
|
-
if (internals?.requestedCwd !== undefined && deps
|
|
10
|
+
if (internals?.requestedCwd !== undefined && !deploymentCanRerootChild(deps)) {
|
|
10
11
|
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-unsupported leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
11
12
|
const e = new Error(`Agent cwd "${internals.requestedCwd}" cannot take effect: this deployment has no executionEnvFactory (a static execution environment cannot be re-rooted per agent). Drop the cwd parameter or deploy a factory.`);
|
|
12
13
|
e.code = "config.cwd_unsupported";
|
|
@@ -37,6 +37,7 @@ import { type WiringManifest } from "../wiring-manifest.js";
|
|
|
37
37
|
import type { WriteProtectionCheck } from "../write-protect.js";
|
|
38
38
|
import type { ShellReadCheck } from "../../tools/fs/shell-read-check.js";
|
|
39
39
|
import type { CwdRef } from "../../tools/fs/fs-shared.js";
|
|
40
|
+
import type { ContainmentFace } from "../../tools/fs/containment-face.js";
|
|
40
41
|
import type { AskLane, GateSeatInputsFor, IrreversibilityTier, ParkAsk, Prepared, ReversibilityProbes, StopForDenialLimit } from "./contracts.js";
|
|
41
42
|
import type { PermissionRuleLanes } from "./permission-rule-lanes.js";
|
|
42
43
|
import { type ToolRosterDeltaSeat } from "../tool-roster.js";
|
|
@@ -167,6 +168,8 @@ export interface PrepareGateStationsInput {
|
|
|
167
168
|
/** borrowed-readonly — the leg's shell READ STATION (`ToolGateInput.shellReadCheck`), mounted by the hands phase, or
|
|
168
169
|
* undefined with no real shell: no station, nothing judged. */
|
|
169
170
|
shellReadCheck: ShellReadCheck | undefined;
|
|
171
|
+
/** borrowed-readonly — the leg's resolved read face (`undefined` with no hands), read per call into the class derivation. */
|
|
172
|
+
readFace: () => ContainmentFace | undefined;
|
|
170
173
|
/** borrowed-readonly — the per-tool irreversibility tier handed to the gate per call. */
|
|
171
174
|
irreversibilityTier: IrreversibilityTier;
|
|
172
175
|
/** borrowed-readonly — the per-tool reversibility probes handed to the gate per call. */
|
|
@@ -46,7 +46,7 @@ export function maybeHumanRejectionHalt(input) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
export function prepareGateStations(input) {
|
|
49
|
-
const { askLane, parkAsk, tools, toolRosterDeltas, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, askRoute, permissionMode, toolMarkSources, a2aIsolatePeers, ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement, seatInputsFor } = input;
|
|
49
|
+
const { askLane, parkAsk, tools, toolRosterDeltas, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, shellReadCheck, readFace, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, askRoute, permissionMode, toolMarkSources, a2aIsolatePeers, ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement, seatInputsFor } = input;
|
|
50
50
|
const gatePreToolUse = ownGatePreToolUse !== undefined ? (name, args, ctx) => ownGatePreToolUse(name, args, { ...ctx, permissionMode: permissionMode.observed }) : undefined;
|
|
51
51
|
const releasesAnnounced = new Map();
|
|
52
52
|
const onModeReleased = (info) => {
|
|
@@ -130,6 +130,7 @@ export function prepareGateStations(input) {
|
|
|
130
130
|
peerMessage: heldBy("gate.peerMessage", PEER_MESSAGE_NAMES, call),
|
|
131
131
|
...(writeProtectionCheck !== undefined ? { writeProtectionCheck } : {}),
|
|
132
132
|
...(shellReadCheck !== undefined ? { shellReadCheck } : {}),
|
|
133
|
+
readFace,
|
|
133
134
|
irreversibility: tierSeat?.value,
|
|
134
135
|
reversibilityProbe: tierSeat?.read(reversibilityProbes),
|
|
135
136
|
abortSignal: abortController.signal,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PrepareTask's LISTINGS phase: the
|
|
3
|
-
*
|
|
2
|
+
* PrepareTask's LISTINGS phase: the sub-agent tool lists' audit (`sub-agent-deny-lists.ts`),
|
|
3
|
+
* the agent-listing announce face read off the
|
|
4
4
|
* delegation tool's roster (with its deferred / excluded / removal-to-zero arms), the two listing
|
|
5
5
|
* opt-out advisories, the announced-listings mirror seeded from the once-per-session ledger, the
|
|
6
6
|
* deferred roster's ToolSearch ride, and the date_change seam. The body is the driver's stretch moved
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* the disclosure mount before it and the context-lane seats after it (a stretch
|
|
11
11
|
* with no await is extracted as a sync function — an `async` wrapper would open a microtask window).
|
|
12
12
|
*
|
|
13
|
-
* Throws pass through unchanged: `config.agent.unknown_tool` (a
|
|
14
|
-
*
|
|
13
|
+
* Throws pass through unchanged: `config.agent.unknown_tool` (a denied-tools entry that names no
|
|
14
|
+
* tool — one judgment for every list a deployment writes, in `sub-agent-deny-lists.ts`).
|
|
15
15
|
*/
|
|
16
16
|
import type { AgentTool } from "../../internal/harness.js";
|
|
17
17
|
import type { TaskSpec } from "../types.js";
|
|
@@ -1,30 +1,10 @@
|
|
|
1
1
|
import { formatLocalDate } from "../../prompts/default.js";
|
|
2
|
+
import { auditSubAgentToolLists } from "./sub-agent-deny-lists.js";
|
|
2
3
|
import { SKILL_TOOL_NAME } from "./synthetic-tools.js";
|
|
3
4
|
import { renderAgentListingDelta } from "./turn-attachments.js";
|
|
4
5
|
export function prepareListings(input) {
|
|
5
6
|
const { spec, tools, onceLedger, sessionId, toolFaceSnapshot, activeTools, resume, skillsListing, listingBudget, listingRideRef, renderWithDate, envFacts, tzValid, userTz, dateAnchor } = input;
|
|
6
|
-
|
|
7
|
-
const known = new Set(tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
|
|
8
|
-
for (const def of spec.agents) {
|
|
9
|
-
const unknownAllow = (def.allowTools ?? []).filter((n) => n !== "*" && !known.has(n));
|
|
10
|
-
if (unknownAllow.length > 0) {
|
|
11
|
-
try {
|
|
12
|
-
onceLedger.onError(new Error(`TaskSpec.agents: agent "${def.name}" allows tool(s) ${unknownAllow.join(", ")} not present in this task's assembled roster — likely a typo (the entry would be item-filtered at spawn; the agent stays usable). Advisory only: the delegation pool can differ from this roster, so a tool mounted only on the delegation tool or produced by per-spawn extraTools makes this spurious.`), { phase: "config", sessionId });
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
for (const n of def.denyTools ?? []) {
|
|
18
|
-
if (n === "*")
|
|
19
|
-
continue;
|
|
20
|
-
if (!known.has(n)) {
|
|
21
|
-
const e = new Error(`TaskSpec.agents: agent "${def.name}" declares tool "${n}" in its denied tools, but no such tool exists in this deployment — fix the agent's tools list or mount the tool.`);
|
|
22
|
-
e.code = "config.agent.unknown_tool";
|
|
23
|
-
throw e;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
7
|
+
auditSubAgentToolLists({ spec, tools, onceLedger, sessionId });
|
|
28
8
|
const rosterBearingSpec = (spec.tools ?? []).find((t) => t.agentListing !== undefined && t.agentListing.length > 0);
|
|
29
9
|
const rosterFaceDeferred = rosterBearingSpec !== undefined &&
|
|
30
10
|
!(toolFaceSnapshot.exclude?.includes(rosterBearingSpec.name) ?? false) &&
|
|
@@ -94,6 +94,8 @@ export interface PrepareParkAskInput {
|
|
|
94
94
|
/** borrowed-readonly — the hand's live tracked cwd, or undefined; `current` is read at park time for the re-adjudication.
|
|
95
95
|
* Not written here. */
|
|
96
96
|
handsCwdRef: CwdRef | undefined;
|
|
97
|
+
/** borrowed-readonly — the root the live gate stamps beside the cwd ({@link import("./prepare-ask-lane.js").PrepareAskLaneInput.handsRoot}). */
|
|
98
|
+
handsRoot: string | undefined;
|
|
97
99
|
/** borrowed-readonly — the SAME seat the live gate stamps from ({@link import("./prepare-ask-lane.js").PrepareAskLaneInput.advertisedWritableDirs}): a parked call's projection re-adjudication asks one question about one call, so it must ask it against the same admitted list. Read at park time, not written here. */
|
|
98
100
|
advertisedWritableDirs: Pick<AdvertisedWritableDirs, "roots"> | undefined;
|
|
99
101
|
/** borrowed-readonly — the run's offload store, or undefined (a volatile store holding results refuses the park). */
|
|
@@ -27,7 +27,7 @@ const wordlessDemand = (toolName) => {
|
|
|
27
27
|
throw new Error(`a real-approval demand for "${toolName}" reached the park with no origin word — the origin stamp precedes every park`);
|
|
28
28
|
};
|
|
29
29
|
export function prepareParkAsk(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, grantBindingOf, 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, handsRoot, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, grantBindingOf, now, humanReviewRef, abortController, harness, pausedRef } = input;
|
|
31
31
|
if (askLane === undefined || saga === undefined)
|
|
32
32
|
return { parkAsk: undefined };
|
|
33
33
|
const { composedCallSignal, approvalPreviewOf } = askLane;
|
|
@@ -165,7 +165,7 @@ export function prepareParkAsk(input) {
|
|
|
165
165
|
toolCallId: req.toolCallId,
|
|
166
166
|
budget: budgetSnapshot,
|
|
167
167
|
...callFaceSeat(req.face),
|
|
168
|
-
...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}),
|
|
168
|
+
...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}), ...(handsRoot !== undefined ? { root: handsRoot } : {}),
|
|
169
169
|
...advertisedWritableRootsSeat(advertisedWritableDirs),
|
|
170
170
|
}, cutSignal)), cutSignal);
|
|
171
171
|
if (reprojectedRaced.tag === "aborted")
|
|
@@ -140,6 +140,8 @@ export interface PreparePolicyChainInput {
|
|
|
140
140
|
/** borrowed-readonly — LATE-BOUND: this task's own classifier instance (the pass-through test's decider term), read at chain
|
|
141
141
|
* assembly (the arming runs after this phase, and again on a steer into `auto`). */
|
|
142
142
|
autoModeDecider: () => AutoModeDecider | undefined;
|
|
143
|
+
/** borrowed-readonly — the leg's resolved read face (`undefined` with no hands), read per call into the inherited stations' class derivation. */
|
|
144
|
+
readFace: () => import("../../tools/fs/containment-face.js").ContainmentFace | undefined;
|
|
143
145
|
/** borrowed-readonly — the leg's live permission-mode seat: the mode gate policy layer and the inherited stations' arbitration
|
|
144
146
|
* read `current` per call. */
|
|
145
147
|
permissionMode: PermissionModeController;
|
|
@@ -24,7 +24,7 @@ import { createLiveSessionRules, createSessionRulePolicy } from "./session-rule-
|
|
|
24
24
|
import { thrownText } from "../thrown-value.js";
|
|
25
25
|
import { heldBy, heldValues, seatOf, wireOf } from "../tool-name-readings.js";
|
|
26
26
|
export async function preparePolicyChain(input) {
|
|
27
|
-
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, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, permissionMode, scratchpadDir, toolRosterDeltas, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
|
|
27
|
+
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, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, readFace, permissionMode, scratchpadDir, toolRosterDeltas, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
|
|
28
28
|
const ancestorArbitrations = new Map();
|
|
29
29
|
const arbitrateInherited = (ask, toolCallId, judged) => {
|
|
30
30
|
const verdict = arbitrateByMode(permissionMode.current, judged.classification, { origin: judged.origin, ...askMandateMember(ancestorFacts(judged.ask.action === "ask" ? judged.ask : undefined)) });
|
|
@@ -305,11 +305,13 @@ export async function preparePolicyChain(input) {
|
|
|
305
305
|
const grantBindingOf = (call, args) => skillGrantBindingMembers(call, args, skillSpecs, SKILL_TOOL_NAME);
|
|
306
306
|
const inheritedStationFacts = (call) => {
|
|
307
307
|
const seat = seatMarksOf(call);
|
|
308
|
+
const face = readFace();
|
|
308
309
|
return {
|
|
309
310
|
...INHERITED_STATION_FACTS,
|
|
310
311
|
mandateMarks: { egress: seat.egress, shellGated: false, ...(seat.tier === "always" && !seat.shellGated ? { irreversibility: "always" } : {}) },
|
|
311
312
|
...(seat.sources.size > 0 ? { toolMarkSources: seat.sources } : {}),
|
|
312
313
|
...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}),
|
|
314
|
+
...(face !== undefined ? { readFace: face } : {}),
|
|
313
315
|
};
|
|
314
316
|
};
|
|
315
317
|
const inheritedAskEvidence = inheritedAskRuleEvidence(deps.permissionRuleStore);
|
|
@@ -616,7 +618,7 @@ export async function preparePolicyChain(input) {
|
|
|
616
618
|
...instrumentedChain,
|
|
617
619
|
...(rewriteCapableLayers ? narrowingLayers : []),
|
|
618
620
|
];
|
|
619
|
-
const integrityGuardLayers = [createTranscriptIntegrityPolicy(), createUnverifiableDeletePolicy()];
|
|
621
|
+
const integrityGuardLayers = [createTranscriptIntegrityPolicy(), createUnverifiableDeletePolicy({ removalSeat: { env: executionEnv, additionalRoots: () => additionalRootsCanonical } })];
|
|
620
622
|
const guardedPolicyLayers = rewriteCapableLayers
|
|
621
623
|
? [...integrityGuardLayers, ...policyLayers, modeGateLayer, ...integrityGuardLayers]
|
|
622
624
|
: [...integrityGuardLayers, ...policyLayers, modeGateLayer];
|