@sema-agent/core 7.50.2 → 7.51.1

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.
Files changed (200) hide show
  1. package/CHANGELOG.md +313 -0
  2. package/dist/agents/child-failure.d.ts +5 -16
  3. package/dist/agents/child-failure.js +4 -24
  4. package/dist/agents/subagent-steps.js +2 -2
  5. package/dist/agents/subagent.d.ts +4 -13
  6. package/dist/agents/subagent.js +94 -204
  7. package/dist/agents/tool-filter.d.ts +2 -2
  8. package/dist/brain/anthropic.d.ts +1 -1
  9. package/dist/brain/anthropic.js +7 -5
  10. package/dist/brain/open-responses.d.ts +1 -1
  11. package/dist/brain/open-responses.js +3 -2
  12. package/dist/brain/openai.d.ts +1 -1
  13. package/dist/brain/openai.js +3 -2
  14. package/dist/brain/request-route.d.ts +2 -2
  15. package/dist/brain/route-adjudicator.d.ts +16 -7
  16. package/dist/brain/route-adjudicator.js +23 -7
  17. package/dist/brain/route-conformance.js +23 -5
  18. package/dist/brain/stream-engine.js +9 -3
  19. package/dist/core/a2a.js +6 -8
  20. package/dist/core/allow-entry.d.ts +45 -26
  21. package/dist/core/allow-entry.js +48 -24
  22. package/dist/core/arg-summary.d.ts +8 -85
  23. package/dist/core/arg-summary.js +4 -386
  24. package/dist/core/ask-origin.d.ts +35 -33
  25. package/dist/core/ask-origin.js +9 -11
  26. package/dist/core/checkpoint-requirements.d.ts +6 -0
  27. package/dist/core/checkpoint-requirements.js +1 -0
  28. package/dist/core/checkpoint-store.d.ts +15 -34
  29. package/dist/core/checkpoint-store.js +5 -4
  30. package/dist/core/classifier-unavailable-arm.d.ts +12 -8
  31. package/dist/core/classifier-unavailable-arm.js +7 -2
  32. package/dist/core/credential-vocabulary.d.ts +3 -2
  33. package/dist/core/credential-vocabulary.js +27 -18
  34. package/dist/core/delegation-frames.d.ts +40 -15
  35. package/dist/core/delegation-frames.js +34 -0
  36. package/dist/core/engine-capabilities.d.ts +15 -0
  37. package/dist/core/engine-capabilities.js +1 -0
  38. package/dist/core/engine-notice.d.ts +41 -14
  39. package/dist/core/gate-fold.js +9 -7
  40. package/dist/core/gate-lanes.js +6 -4
  41. package/dist/core/gate-outcome.d.ts +81 -15
  42. package/dist/core/gate-outcome.js +168 -67
  43. package/dist/core/governance-codes.d.ts +7 -1
  44. package/dist/core/governance-codes.js +9 -0
  45. package/dist/core/hooks.d.ts +4 -4
  46. package/dist/core/hooks.js +4 -3
  47. package/dist/core/mcp-connection-pool.d.ts +2 -0
  48. package/dist/core/mcp-connection-pool.js +2 -1
  49. package/dist/core/mcp-failure.js +2 -1
  50. package/dist/core/mcp-server-spec.d.ts +27 -5
  51. package/dist/core/mcp.d.ts +13 -38
  52. package/dist/core/mcp.js +28 -23
  53. package/dist/core/media-byte-cap.d.ts +25 -0
  54. package/dist/core/media-byte-cap.js +18 -2
  55. package/dist/core/memory-engine/consolidation-driver.js +1 -1
  56. package/dist/core/mode-policy.d.ts +25 -1
  57. package/dist/core/mode-policy.js +28 -13
  58. package/dist/core/own-property-receipt.d.ts +18 -0
  59. package/dist/core/own-property-receipt.js +39 -0
  60. package/dist/core/permission-rule-model.d.ts +32 -11
  61. package/dist/core/permission-rule-model.js +12 -7
  62. package/dist/core/persisted-rule-arms.d.ts +3 -3
  63. package/dist/core/prompt-section-seat.d.ts +54 -0
  64. package/dist/core/prompt-section-seat.js +49 -0
  65. package/dist/core/protocol-http-auth.d.ts +44 -0
  66. package/dist/core/protocol-http-auth.js +45 -0
  67. package/dist/core/read-face-seats.d.ts +30 -0
  68. package/dist/core/read-face-seats.js +38 -0
  69. package/dist/core/redaction-passes.d.ts +82 -0
  70. package/dist/core/redaction-passes.js +378 -0
  71. package/dist/core/remote-env.d.ts +4 -3
  72. package/dist/core/resume-startup-cause.d.ts +56 -0
  73. package/dist/core/resume-startup-cause.js +43 -0
  74. package/dist/core/rule-denial-sentence.d.ts +2 -2
  75. package/dist/core/runner/announce-once-ledger.js +2 -2
  76. package/dist/core/runner/classifier-ladder.d.ts +17 -13
  77. package/dist/core/runner/classifier-ladder.js +12 -10
  78. package/dist/core/runner/construction-doors.js +9 -20
  79. package/dist/core/runner/contracts.d.ts +4 -4
  80. package/dist/core/runner/gate-exit.js +2 -2
  81. package/dist/core/runner/memory-consolidation-write-judge.d.ts +53 -9
  82. package/dist/core/runner/memory-consolidation-write-judge.js +39 -1
  83. package/dist/core/runner/permission-rule-lanes.d.ts +8 -13
  84. package/dist/core/runner/person-path-rows.d.ts +4 -3
  85. package/dist/core/runner/policy-throw.js +4 -4
  86. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
  87. package/dist/core/runner/prepare-caps-and-workflow.js +8 -6
  88. package/dist/core/runner/prepare-gate-stations.js +4 -4
  89. package/dist/core/runner/prepare-hands-readface.js +1 -19
  90. package/dist/core/runner/prepare-inherited-gate.js +10 -9
  91. package/dist/core/runner/prepare-policy-chain.d.ts +3 -5
  92. package/dist/core/runner/prepare-policy-chain.js +10 -13
  93. package/dist/core/runner/prepare-project-context.d.ts +4 -4
  94. package/dist/core/runner/prepare-project-context.js +2 -1
  95. package/dist/core/runner/prepare-prompt-assembly.js +1 -0
  96. package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -1
  97. package/dist/core/runner/prepare-prompt-inputs.js +4 -4
  98. package/dist/core/runner/prepare-protocol-tools.d.ts +4 -3
  99. package/dist/core/runner/prepare-protocol-tools.js +3 -2
  100. package/dist/core/runner/prepare-task.js +6 -4
  101. package/dist/core/runner/prepare-turn-wiring.d.ts +16 -13
  102. package/dist/core/runner/prepare-turn-wiring.js +8 -7
  103. package/dist/core/runner/run-harness-handlers.js +2 -2
  104. package/dist/core/runner/run-leg.js +10 -9
  105. package/dist/core/runner/run-settle-and-teardown.js +5 -5
  106. package/dist/core/runner/run-turn-boundary.js +3 -2
  107. package/dist/core/runner/runtask.d.ts +1 -2
  108. package/dist/core/runner/runtask.js +8 -12
  109. package/dist/core/runner/session-rule-policy.d.ts +15 -11
  110. package/dist/core/runner/session-rule-policy.js +43 -33
  111. package/dist/core/runner/stream-lifecycle-verbs.js +2 -2
  112. package/dist/core/runner/stream-settle-backstop.js +2 -1
  113. package/dist/core/runner/sub-agent-deny-lists.d.ts +12 -8
  114. package/dist/core/runner/sub-agent-deny-lists.js +23 -12
  115. package/dist/core/runner/terminal-account.js +3 -3
  116. package/dist/core/runner/tool-output-projection.d.ts +5 -0
  117. package/dist/core/runner/tool-output-projection.js +6 -11
  118. package/dist/core/runner/tool-result-media-seat.d.ts +19 -0
  119. package/dist/core/runner/tool-result-media-seat.js +13 -0
  120. package/dist/core/runner/turn-attachments.d.ts +13 -12
  121. package/dist/core/runner/turn-attachments.js +11 -7
  122. package/dist/core/runner-deps.d.ts +4 -3
  123. package/dist/core/safety-merge-corpus.js +21 -21
  124. package/dist/core/secret-env.d.ts +41 -15
  125. package/dist/core/secret-env.js +85 -19
  126. package/dist/core/session-policy-store.d.ts +89 -72
  127. package/dist/core/session-policy-store.js +100 -93
  128. package/dist/core/shell-read-silence.d.ts +38 -4
  129. package/dist/core/shell-read-silence.js +102 -0
  130. package/dist/core/side-query.d.ts +7 -8
  131. package/dist/core/skill-grant-binding.d.ts +32 -11
  132. package/dist/core/skill-grant-binding.js +11 -3
  133. package/dist/core/skill-grant.d.ts +4 -3
  134. package/dist/core/skill-grant.js +5 -4
  135. package/dist/core/skills-directory.d.ts +12 -1
  136. package/dist/core/skills-directory.js +17 -8
  137. package/dist/core/spec-contract.d.ts +5 -3
  138. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
  139. package/dist/core/store-contracts/checkpoint-store-contract.js +3 -0
  140. package/dist/core/swappable-deps.d.ts +15 -1
  141. package/dist/core/swappable-deps.js +25 -1
  142. package/dist/core/synthetic-read-verdict.d.ts +16 -14
  143. package/dist/core/synthetic-read-verdict.js +28 -8
  144. package/dist/core/task-notification.d.ts +13 -3
  145. package/dist/core/task-notification.js +12 -1
  146. package/dist/core/task-spec.d.ts +14 -15
  147. package/dist/core/tool-name-readings.d.ts +0 -4
  148. package/dist/core/tool-name-readings.js +0 -1
  149. package/dist/core/tool-policy.d.ts +40 -9
  150. package/dist/core/tool-policy.js +25 -7
  151. package/dist/core/types.d.ts +3 -3
  152. package/dist/core/types.js +1 -1
  153. package/dist/core/untrusted-egress.d.ts +60 -11
  154. package/dist/core/untrusted-egress.js +120 -9
  155. package/dist/core/untrusted-text.js +16 -0
  156. package/dist/core/wiring-manifest.d.ts +22 -8
  157. package/dist/core/wiring-manifest.js +12 -4
  158. package/dist/engine/execution-env/kill-tree.js +4 -4
  159. package/dist/engine/execution-env/node-execution-env.d.ts +11 -4
  160. package/dist/engine/execution-env/node-execution-env.js +9 -4
  161. package/dist/engine/harness/agent-harness.d.ts +2 -1
  162. package/dist/engine/harness/agent-harness.js +4 -1
  163. package/dist/engine/harness/types.d.ts +3 -0
  164. package/dist/engine/llm/types.d.ts +7 -9
  165. package/dist/engine/loop/agent-loop.d.ts +8 -3
  166. package/dist/engine/loop/agent-loop.js +26 -9
  167. package/dist/engine/loop/types.d.ts +10 -0
  168. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -1
  169. package/dist/engine/lsp/node-lsp-manager.js +2 -2
  170. package/dist/index.d.ts +22 -5
  171. package/dist/index.js +18 -5
  172. package/dist/orchestration/run-workflow-tool.js +8 -8
  173. package/dist/orchestration/workflow.js +2 -2
  174. package/dist/prompt-assembly/assemble.d.ts +3 -1
  175. package/dist/prompt-assembly/assemble.js +6 -0
  176. package/dist/prompts/cowork.d.ts +42 -0
  177. package/dist/prompts/cowork.js +529 -0
  178. package/dist/prompts/default.d.ts +14 -1
  179. package/dist/prompts/default.js +3 -0
  180. package/dist/stores/file/fs-atomic.js +2 -2
  181. package/dist/stores/file/session-policy-store.d.ts +6 -3
  182. package/dist/stores/file/session-policy-store.js +5 -16
  183. package/dist/tools/artifact/artifact-tool.js +2 -0
  184. package/dist/tools/fs/image-admission.d.ts +57 -4
  185. package/dist/tools/fs/image-admission.js +85 -0
  186. package/dist/tools/fs/path-containment.d.ts +2 -0
  187. package/dist/tools/fs/path-containment.js +5 -0
  188. package/dist/tools/fs/shell-read-check.d.ts +31 -6
  189. package/dist/tools/fs/shell-read-check.js +2 -4
  190. package/dist/tools/fs/shell-read-environment.d.ts +10 -25
  191. package/dist/tools/fs/shell-read-environment.js +6 -18
  192. package/dist/tools/fs/shell-read-establish.d.ts +3 -2
  193. package/dist/tools/fs/shell-read-establish.js +5 -5
  194. package/dist/tools/fs/shell-read-judge.d.ts +38 -8
  195. package/dist/tools/fs/shell-read-judge.js +12 -10
  196. package/dist/tools/web.js +2 -2
  197. package/package.json +1 -1
  198. package/test/export-surface.snapshot.json +112 -10
  199. package/wire-changes/core@7.51.0.json +2421 -0
  200. package/wire-changes/core@7.51.1.json +259 -0
@@ -1,29 +1,18 @@
1
1
  import { checkedAgentConfigDir } from "../agent-config-dir.js";
2
2
  import { checkedMediaByteCap } from "../media-byte-cap.js";
3
+ import { receiveOwnProperties, refuseProxy } from "../own-property-receipt.js";
3
4
  import { assertNoRetiredRunnerDeps } from "./retired-runner-deps.js";
5
+ import { copyReadDenySeat, READ_DENY_DEPLOYMENT_SEATS } from "../read-face-seats.js";
4
6
  export function retainRunnerDeps(deps) {
7
+ const words = { subject: "RunnerDeps", unit: "seat", moment: "at new Runner" };
8
+ refuseProxy(deps, words);
5
9
  assertNoRetiredRunnerDeps(deps);
6
- const refusal = (message) => {
7
- const e = new Error(message);
8
- e.code = "config.invalid_argument";
9
- return e;
10
- };
11
- const proto = Object.getPrototypeOf(deps);
12
- if (proto !== Object.prototype && proto !== null) {
13
- throw refusal("RunnerDeps must be a plain object (its prototype is neither Object.prototype nor null): every seat is read once as an own property, and a seat its prototype would supply is not read — refused rather than wired without it");
14
- }
15
- const retained = Object.create(Object.prototype);
16
- for (const name of Object.getOwnPropertyNames(deps)) {
17
- let value;
18
- try {
19
- value = Reflect.get(deps, name);
20
- }
21
- catch {
22
- throw refusal(`RunnerDeps.${name} threw while it was read at new Runner — a seat is read once, here; refused rather than wired without it`);
23
- }
24
- Object.defineProperty(retained, name, { value, enumerable: true, writable: true, configurable: true });
25
- }
10
+ const retained = receiveOwnProperties(deps, words);
26
11
  checkedAgentConfigDir(retained.agentConfigDir, "RunnerDeps.agentConfigDir");
27
12
  checkedMediaByteCap(retained.mediaByteCapBytes, "RunnerDeps.mediaByteCapBytes");
13
+ for (const seat of READ_DENY_DEPLOYMENT_SEATS) {
14
+ if (Object.hasOwn(retained, seat))
15
+ Object.defineProperty(retained, seat, { value: copyReadDenySeat(Reflect.get(retained, seat)), enumerable: true, writable: true, configurable: true });
16
+ }
28
17
  return retained;
29
18
  }
@@ -53,7 +53,7 @@ import type { OnAsk, PermissionResult, ResolvedAsk, ToolCallRequest, ToolPolicy
53
53
  import type { RemoteExecutionEnv, SnapshotId, WorkspaceHandle } from "../remote-env.js";
54
54
  import type { ToolDisclosureManifest } from "../trace.js";
55
55
  import type { AgentDefinition, Brain, CompactOutcome, ModelRef, ModelRoles, NestedUsageAccum, PermissionMode, RunnerDeps, ShellGateDoctrine, TaskEvent, TaskEventIdentity, TaskResult, TaskSpec, TaskStream, ToolActivity, ToolEffect } from "../types.js";
56
- import type { PermissionModeController } from "../mode-policy.js";
56
+ import type { InheritedAutoModeFace, PermissionModeController } from "../mode-policy.js";
57
57
  import type { WiringManifest } from "../wiring-manifest.js";
58
58
  import type { SkillGateSeats, SkillGrantLane, SkillGrantRecord, SkillGrantSource } from "../skill-grant.js";
59
59
  import type { BudgetAxis } from "./assemble-result.js";
@@ -450,8 +450,8 @@ export interface Prepared {
450
450
  };
451
451
  /** The deployment's caller policy, re-checked on a durable resume when the bytes to execute are not the bytes the gate saw. */
452
452
  basePolicyForResumeEdit?: ToolPolicy;
453
- /** The WRITE face of the run's synthetic judge (the settle tail's background consolidation asks it): an `ask` is an objection. */
454
- syntheticWriteVerdict: import("../synthetic-read-verdict.js").SyntheticWriteJudge;
453
+ /** The leg's consolidation write judge (`createConsolidationWriteJudge`, the settle tail's background consolidation asks it). */
454
+ consolidationWriteJudge: import("../memory-engine/consolidation-driver.js").ConsolidationWriteJudge;
455
455
  approvedCallScreen?: (call: {
456
456
  toolCallId: string;
457
457
  toolName: string;
@@ -1390,7 +1390,7 @@ export interface PrepareResume {
1390
1390
  * hook, deduped by full installation so a deps-level hook is consulted once per call at any depth).
1391
1391
  * Depth-N delegation is therefore a linear chain, each layer evaluated at most once per tool call.
1392
1392
  */
1393
- export interface InheritedGate {
1393
+ export interface InheritedGate extends InheritedAutoModeFace {
1394
1394
  /** Serializable ancestor session-rule snapshots (data half), ancestors first. */
1395
1395
  ancestorRules?: ReadonlyArray<{
1396
1396
  sessionId: string;
@@ -1,4 +1,4 @@
1
- import { screenGateOutcome, SETTLEMENT_IS_REFUSAL, releasePolicyDefects } from "../gate-outcome.js";
1
+ import { screenGateOutcome, SETTLEMENT_IS_REFUSAL } from "../gate-outcome.js";
2
2
  import { askFaceOf, classifyAsk, headlessApprovalRemedy } from "../ask-origin.js";
3
3
  import { wireOf } from "../tool-name-readings.js";
4
4
  import { isProtectedWrite } from "../tool-registry.js";
@@ -23,7 +23,7 @@ export function mintGateOutcome(facts) {
23
23
  : { kind: "allowed", ...(attribution !== undefined ? { classifier: attribution } : {}), ...(facts.released !== undefined ? { released: Object.freeze({ ...facts.released, classes: Object.freeze([...facts.released.classes]) }) } : {}) }),
24
24
  ...(facts.settled !== undefined ? { settlement: settlement, origin: facts.settled.origin } : {}),
25
25
  });
26
- const defects = [...screenGateOutcome(outcome), ...releasePolicyDefects(outcome)];
26
+ const defects = screenGateOutcome(outcome, { reading: "producer" });
27
27
  if (defects.length > 0)
28
28
  throw new Error(`the tool gate composed an outcome that violates its own record: ${defects.join("; ")}`);
29
29
  return outcome;
@@ -1,17 +1,61 @@
1
1
  /**
2
- * The Runner's half of the consolidation driver's write judge: the leg's synthetic write judge as the seat
3
- * `runMemoryConsolidationDriver` asks (for the library directory before it claims anything, then for each file a commit
4
- * lands), and the refusal's notice dialect for the auto lane. The judgment itself is `createSyntheticWriteJudge` — the layers
5
- * a model's own `Write` meets; nothing here judges.
2
+ * The consolidation driver's write judge — the seat `runMemoryConsolidationDriver` asks (for the library directory before it
3
+ * claims anything, then for each file a commit lands) — minted by ONE constructor, {@link createConsolidationWriteJudge}, for
4
+ * every caller: the Runner's auto lane (a leg, with every layer a model's own `Write` meets on it) and a host's own
5
+ * consolidation valve (no leg: its rule store, the person, the local-owner declaration, its caller policy). The judgment is
6
+ * `createSyntheticWriteJudge`'s over the person's and the org's rows read through the gate's own lanes; nothing here judges.
7
+ * Beside it, the refusal's notice dialect ({@link deliverWriteRefusal}).
6
8
  */
7
- import type { SyntheticWriteJudge } from "../synthetic-read-verdict.js";
9
+ import { type SyntheticReadLayers, type SyntheticWriteFacts } from "../synthetic-read-verdict.js";
8
10
  import { type ConsolidationWriteJudge } from "../memory-engine/consolidation-driver.js";
9
11
  import { type EngineNotice } from "../engine-notice.js";
12
+ import type { PermissionRuleStoreProvider } from "../permission-rule-provider.js";
13
+ import { type ToolPolicy } from "../tool-policy.js";
14
+ import type { ExecutionEnv } from "../../internal/harness-types.js";
15
+ import { createPermissionRuleLanes } from "./permission-rule-lanes.js";
16
+ /** What every caller states: the layers a host has. */
17
+ export interface ConsolidationWriteJudgeInput {
18
+ /** The deployment's permission-rule store (`RunnerDeps.permissionRuleStore`): the person's standing rows and the org's. */
19
+ ruleStoreProvider?: PermissionRuleStoreProvider;
20
+ /** Whose rows are read: the principal the library belongs to. Absent or empty ⇒ the anonymous floor (no personal row),
21
+ * or the local owner's bucket where `localOwnerDeclared`. */
22
+ principal?: string;
23
+ /** `RunnerDeps.localOwnerRules`: a declaration a store cannot honor is refused at construction, loudly. */
24
+ localOwnerDeclared?: boolean;
25
+ /** The deployment's caller policy (`RunnerDeps.toolPolicy`), asked with a live signal and a request that states nobody can
26
+ * be asked about it (`ToolCallRequest.approvalReach: "none"`) — never an approver. */
27
+ callerPolicy?: ToolPolicy;
28
+ /** The session the person's session-scoped rows are read under. Absent ⇒ the judgment is no session's: no session row applies. */
29
+ sessionId?: string;
30
+ /** Where the person's relative and `~/` rows anchor: the project root (also which project rows apply) and the home. Absent ⇒
31
+ * such a row cannot be anchored, and a deny or ask it holds is an OBJECTION (fail closed) — the judge never guesses where a
32
+ * row would have pointed. */
33
+ anchors?: {
34
+ root?: string;
35
+ home?: string;
36
+ };
37
+ /** The environment the library's paths and the rows' heads resolve in — the one a leg of this deployment judges in (links
38
+ * followed both ways: a row spelled through a link binds the target's real location, a target spelled through one is judged
39
+ * where it leads). Absent ⇒ the engine process's own filesystem, where an in-process file backend writes the library. */
40
+ executionEnv?: ExecutionEnv;
41
+ }
42
+ /** What a leg adds — the Runner's prepare passes it; a host has none of it. */
43
+ export interface ConsolidationWriteJudgeLeg {
44
+ /** The leg's reader of the person's rows, one the run's cut does not end (the judge is asked after the run). */
45
+ rows: Parameters<typeof createPermissionRuleLanes>[0]["rows"];
46
+ /** The leg's facts the lanes judge against: its session, root and anchors, its resolution of rule heads, its sinks. */
47
+ lanes: Omit<Parameters<typeof createPermissionRuleLanes>[0], "provider" | "localOwnerDeclared" | "principal" | "rows">;
48
+ /** The layers only a leg has: its skill rows, its narrowing fold, the live inherited policies, its target stamp. */
49
+ layers: Omit<SyntheticReadLayers, "callerPolicy" | "persistedRuleLane" | "orgRuleLane" | "signal">;
50
+ /** The facts a live write carries on the leg, read at each judgment. */
51
+ facts: () => SyntheticWriteFacts;
52
+ }
10
53
  /**
11
- * The leg's write judge as the consolidation driver's seat: each target the run asks about is put to the leg's synthetic
12
- * Write judge as one `Write` of that path, under a deadline of its own. The answer is the judge's, unchanged — nothing here
13
- * decides, merges or defaults.
54
+ * The consolidation write judge: each target the run asks about is put, as one `Write` of that path under a deadline of its
55
+ * own, to the synthetic Write judge over the caller policy, the person's and the org's rows (the gate's own lanes over a
56
+ * reader of the store) and whatever layers `leg` adds. The answer is the judge's, unchanged. A host passes `input` alone; the
57
+ * engine's leg passes the same `input` and its `leg`, so the same rows give the same answer on both.
14
58
  */
15
- export declare function consolidationWriteJudgeOfLeg(judge: SyntheticWriteJudge): ConsolidationWriteJudge;
59
+ export declare function createConsolidationWriteJudge(input: ConsolidationWriteJudgeInput, leg?: ConsolidationWriteJudgeLeg): ConsolidationWriteJudge;
16
60
  /** Deliver a write-judge refusal as the verb's refusal notice; false (nothing delivered) for any other error. */
17
61
  export declare function deliverWriteRefusal(err: unknown, scope: string, onNotice: ((notice: EngineNotice) => void) | undefined): boolean;
@@ -1,11 +1,49 @@
1
1
  import { ORG_ADJUDICATION_TIMEOUT_MS } from "../permission-rule-org.js";
2
+ import { createSyntheticWriteJudge } from "../synthetic-read-verdict.js";
2
3
  import { CONSOLIDATION_WRITE_REFUSED_CODE } from "../memory-engine/consolidation-driver.js";
3
4
  import { ConsolidationRefusedError } from "../memory-engine/consolidation.js";
4
5
  import { memoryConsolidationRefusedNotice } from "../memory-engine/engine.js";
5
6
  import { deliverEngineNotice } from "../engine-notice.js";
6
- export function consolidationWriteJudgeOfLeg(judge) {
7
+ import { toolPolicyPathRules } from "../tool-policy.js";
8
+ import { RosterBuilder, toolCallFaceOf } from "../tool-roster.js";
9
+ import { createHeadLeads } from "../path-rule-binding.js";
10
+ import { legCallTargetStamp } from "../effective-path-target.js";
11
+ import { createWriteFileTool } from "../../tools/fs/fs-write.js";
12
+ import { whereItsAnsweredPrefixLeads } from "../../tools/fs/read-deny-home.js";
13
+ import { NodeExecutionEnv } from "../../engine/execution-env/node-execution-env.js";
14
+ import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
15
+ import { createPermissionRuleLanes } from "./permission-rule-lanes.js";
16
+ import { createPersonPathRows } from "./person-path-rows.js";
17
+ const noLine = () => undefined;
18
+ let engineWrite;
19
+ function engineWriteFace() {
20
+ if (engineWrite === undefined) {
21
+ const roster = new RosterBuilder(new Map());
22
+ roster.mount(createWriteFileTool(new NodeExecutionEnv({ cwd: "/" }), new Map(), "/"), { source: "builtin", mountedBy: "hands" });
23
+ engineWrite = toolCallFaceOf(roster.snapshot({ effects: new Map(), egress: new Set(), irreversibilityTier: new Map(), reversibilityProbes: new Map(), deferred: new Set(), trustedTools: new Set() }), "Write");
24
+ }
25
+ return engineWrite;
26
+ }
27
+ export function createConsolidationWriteJudge(input, leg) {
28
+ const owner = { provider: input.ruleStoreProvider, localOwnerDeclared: input.localOwnerDeclared === true, principal: input.principal };
29
+ const { rows, lanes, layers, facts } = leg ?? hostLeg(input, owner);
30
+ const { personal, org } = createPermissionRuleLanes({ ...owner, ...lanes, rows });
31
+ const judge = createSyntheticWriteJudge({ ...layers, ...(input.callerPolicy !== undefined ? { callerPolicy: input.callerPolicy } : {}), ...(personal !== undefined ? { persistedRuleLane: personal } : {}), ...(org !== undefined ? { orgRuleLane: org } : {}) }, facts);
7
32
  return (target) => judge({ file_path: target.path }, "memory-consolidation", { deadlineAt: Date.now() + ORG_ADJUDICATION_TIMEOUT_MS });
8
33
  }
34
+ function hostLeg(input, owner) {
35
+ const { root, home } = input.anchors ?? {};
36
+ const env = input.executionEnv ?? new NodeExecutionEnv({ cwd: root ?? "/" });
37
+ const leads = createHeadLeads((part) => whereItsAnsweredPrefixLeads(env, part));
38
+ const chainTop = { ...(root !== undefined ? { root } : {}), ...(home !== undefined ? { home } : {}) };
39
+ const sessionId = input.sessionId;
40
+ return {
41
+ rows: createPersonPathRows({ ...owner, sessionId, root, rootCanonical: undefined, leads, chainTop, onDisclosure: noLine, onUnread: noLine, onRowsDropped: noLine }),
42
+ lanes: { sessionId, root, rootCanonical: undefined, chainTop, leads, liveCwd: () => root, home, questionToolMounted: false, questionToolName: ASK_USER_QUESTION_TOOL_NAME, onRevisionDefect: noLine, onDisclosure: noLine },
43
+ layers: { stampCallTarget: legCallTargetStamp({ env, root: root ?? "/", cwd: () => root, judgesPaths: () => true, leads: () => leads.resolve(toolPolicyPathRules([input.callerPolicy], chainTop)) }) },
44
+ facts: () => ({ face: engineWriteFace(), ...(root !== undefined ? { root, cwd: root } : {}) }),
45
+ };
46
+ }
9
47
  export function deliverWriteRefusal(err, scope, onNotice) {
10
48
  if (!(err instanceof ConsolidationRefusedError) || err.code !== CONSOLIDATION_WRITE_REFUSED_CODE)
11
49
  return false;
@@ -24,7 +24,7 @@
24
24
  * silence the ungated-write warning for deployments that wired no policy at all.
25
25
  */
26
26
  import type { AskRuleEvidence, ToolCallRequest } from "../tool-policy.js";
27
- import { persistedRuleMandateOf, type RuleClearanceFacts } from "../ask-origin.js";
27
+ import { persistedRuleMandateOf, type RuleClearanceFacts, type RuleOffersAbsence } from "../ask-origin.js";
28
28
  import { type OrgGateVerdict, type PersistedRuleAnswer, type PersistedRuleHit, type PersistedRuleUnreadable } from "../hooks.js";
29
29
  import type { PersistedRule, PersistedRuleVerdict, RuleOffer, ScopeRootIdentity, SegmentCoverage } from "../permission-rule-model.js";
30
30
  import { type PathRuleBases } from "../permission-rule-model.js";
@@ -146,10 +146,12 @@ export declare function createPermissionRuleLanes(cfg: {
146
146
  provider: PermissionRuleStoreProvider | undefined;
147
147
  localOwnerDeclared: boolean;
148
148
  principal: string | undefined;
149
- sessionId: string;
149
+ /** The session whose session-scoped rows apply; `undefined` (a judgment no session makes) ⇒ none does. */
150
+ sessionId: string | undefined;
150
151
  /** The isolation-aware task root — the same value the file fence, the prompt cwd and the session rules
151
- * use, so a project-scoped rule agrees with every other place that says "this project" (which project rows apply). */
152
- root: string;
152
+ * use, so a project-scoped rule agrees with every other place that says "this project" (which project rows apply).
153
+ * `undefined` (a judgment outside any task) ⇒ no project row applies and a row anchored at the root cannot be anchored. */
154
+ root: string | undefined;
153
155
  /** The same root's canonical spelling (`undefined`: none could be read): a project row applies when its scope root covers
154
156
  * either spelling of it — the row's root resolved where it leads ({@link projectScopeIdentity}). */
155
157
  rootCanonical: string | undefined;
@@ -180,12 +182,6 @@ export declare function createPermissionRuleLanes(cfg: {
180
182
  /** The allow layer's auto strip over a stored ALLOW row (true ⇒ out of this call's table BEFORE the coverage is chosen). */
181
183
  stripsAllow?: (rule: PersistedRule, req: ToolCallRequest) => boolean;
182
184
  }): PermissionRuleLanes;
183
- /** The closed reason set for the rule-offer factory's empty answer — the same three
184
- * spellings the `AskRequest` and durable-row seats declare, kept at the one factory that fills both.
185
- * Deliberately not exported: this is a wire vocabulary, and its two faces declare it literally (the
186
- * `previewWithheld` precedent) so a consumer reads the closed set on the type it is holding rather
187
- * than through an import. */
188
- type RuleOffersAbsence = "mandated" | "lane_cannot_speak" | "shadowed";
189
185
  /** The surviving-ask facts the rule-offer factory reads (see {@link createRuleOffersOf}): the
190
186
  * synchronous mint sites spread the surviving decision, which carries them; the park leg threads
191
187
  * each as its own parameter. The clearance half is {@link RuleClearanceFacts} — the SAME declaration
@@ -242,9 +238,8 @@ interface RuleOffersAskFacts extends RuleClearanceFacts {
242
238
  * cannot disagree). The doctrine it serves is the loud-bad-value one, applied to an absence: a
243
239
  * surface reading an empty card could not tell "there is nothing this lane could offer" from
244
240
  * "offers were deliberately suppressed", and a person reading it could not tell "write the rule
245
- * yourself" from "no rule can excuse this approval". The seat is CLOSED
246
- * (`mandated | lane_cannot_speak | shadowed`), mutually exclusive with `ruleOffers` by
247
- * construction, and structural-door silent (see the first two guards).
241
+ * yourself" from "no rule can excuse this approval". The seat is CLOSED ({@link RuleOffersAbsence}),
242
+ * mutually exclusive with `ruleOffers` by construction, and structural-door silent (see the first two guards).
248
243
  */
249
244
  export declare function createRuleOffersOf(cfg: {
250
245
  /** `permissionRuleLane !== undefined` at wiring time — the armed-lane structural door. */
@@ -131,9 +131,10 @@ export interface PersonPathRowsConfig {
131
131
  provider: PermissionRuleStoreProvider | undefined;
132
132
  localOwnerDeclared: boolean;
133
133
  principal: string | undefined;
134
- sessionId: string;
135
- /** The leg's own root: which project rows apply (either spelling of it — {@link projectScopeIdentity}). */
136
- root: string;
134
+ /** The session whose session-scoped rows are read; `undefined` ⇒ none. */
135
+ sessionId: string | undefined;
136
+ /** The leg's own root: which project rows apply (either spelling of it — {@link projectScopeIdentity}); `undefined` ⇒ none. */
137
+ root: string | undefined;
137
138
  rootCanonical: string | undefined;
138
139
  /** The leg's resolution of locations (a project row's scope root, where it leads). */
139
140
  leads: HeadLeads;
@@ -1,8 +1,8 @@
1
- import { thrownLabel, thrownText } from "../thrown-value.js";
2
- import { redactSecrets } from "../untrusted-egress.js";
1
+ import { thrownLabel } from "../thrown-value.js";
2
+ import { egressText, egressThrownText } from "../untrusted-egress.js";
3
3
  import { inlineUntrusted, PROBE_REASON_MAX } from "../untrusted-text.js";
4
4
  export function policyThrowLine(err) {
5
- const line = inlineUntrusted(redactSecrets(thrownText(err)), PROBE_REASON_MAX);
5
+ const line = inlineUntrusted(egressThrownText("same-principal", err), PROBE_REASON_MAX);
6
6
  return line === "" ? "(no message)" : line;
7
7
  }
8
8
  export function policyCheckAbortedDeny() {
@@ -18,7 +18,7 @@ export function reportPolicyThrow(report, seat, call, err, signal) {
18
18
  if (signal?.aborted === true)
19
19
  return;
20
20
  try {
21
- report(new Error(`${seat} threw while judging "${call.toolName}" (toolCallId ${call.toolCallId}); the call was denied fail-closed: ${redactSecrets(thrownLabel(err))}`));
21
+ report(new Error(`${seat} threw while judging "${call.toolName}" (toolCallId ${call.toolCallId}); the call was denied fail-closed: ${egressText("same-principal", thrownLabel(err))}`));
22
22
  }
23
23
  catch {
24
24
  }
@@ -24,7 +24,7 @@ import { forkGovernanceDenial } from "../../agents/subagent.js";
24
24
  import type { PromptEpochArtifact } from "../../prompt-assembly/artifact.js";
25
25
  import { type WorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
26
26
  import { type AutoModeArmingRecipe } from "../auto-mode-arming.js";
27
- import type { PermissionModeController } from "../mode-policy.js";
27
+ import { type PermissionModeController } from "../mode-policy.js";
28
28
  import { type AutoModeDecider, type AutoModeDenialTracker } from "../auto-mode.js";
29
29
  import { type CheckpointSeat } from "../checkpoint-seat.js";
30
30
  import { type ComplianceCapability } from "../compliance.js";
@@ -55,8 +55,9 @@ export interface PrepareCapsAndWorkflowInput {
55
55
  * config doors): the plan-pair face reads the store it names, the Workflow mount hands the seat itself to its children. */
56
56
  checkpointSeat: CheckpointSeat | undefined;
57
57
  /** borrowed-readonly — the trusted spawn-side channel; the Workflow mount forwards `rootSessionId`, `placementRoot`,
58
- * `onTaskNotification`, `workflowDepth`, and the capture-floor getter reads `memoryCaptureAncestors`. */
59
- internals: Pick<RunInternals, "rootSessionId" | "placementRoot" | "onTaskNotification" | "workflowDepth" | "memoryCaptureAncestors" | "workflowParkedResume" | "questionFaceStripped"> | undefined;
58
+ * `onTaskNotification`, `workflowDepth`, the capture-floor getter reads `memoryCaptureAncestors`, and the arming reads
59
+ * the chain's auto-mode face (`inheritedGate.autoModeFace`). */
60
+ internals: Pick<RunInternals, "rootSessionId" | "placementRoot" | "onTaskNotification" | "workflowDepth" | "memoryCaptureAncestors" | "workflowParkedResume" | "questionFaceStripped" | "inheritedGate"> | undefined;
60
61
  /** borrowed-readonly — the acquired session; the classifier leg rebuilds its transcript window from `buildContext`. */
61
62
  session: Pick<StoredSession, "buildContext">;
62
63
  /** borrowed-readonly — the acquired session id (operator-line context, the refusal codes' phase record). */
@@ -9,6 +9,7 @@ import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-
9
9
  import { autoModeArmingRecipeOf } from "../auto-mode-arming.js";
10
10
  import { AUTO_MODE_CLASSIFIER_MAX_RETRIES } from "../auto-mode-defaults.js";
11
11
  import { buildAutoModePrompt, readDenyClassifierRules, renderAutoModeAction, renderAutoModeWindow } from "../auto-mode-prompt.js";
12
+ import { autoModeFaceOf } from "../mode-policy.js";
12
13
  import { autoModeClassifierFallbackOf, createAutoModeDecider, createAutoModeDenialTracker, screenAutoModeKnobs } from "../auto-mode.js";
13
14
  import { checkpointStoreOfSeat } from "../checkpoint-seat.js";
14
15
  import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, resolveComplianceDenies } from "../compliance.js";
@@ -210,15 +211,16 @@ export async function prepareCapsAndWorkflow(input) {
210
211
  const autoModeDeciderRef = { current: undefined };
211
212
  const autoModeDenialTrackingRef = { current: undefined };
212
213
  const autoModeArmingRef = { current: undefined };
213
- const autoModeArmReason = () => autoModeArmReasonOf(permissionMode.declared === "auto", deps.autoMode !== undefined, runtimeCaps, runtimeCapsFaulted);
214
- if (deps.autoMode !== undefined)
215
- screenAutoModeKnobs(deps.autoMode);
214
+ const autoModeFace = autoModeFaceOf(deps, internals?.inheritedGate);
215
+ const autoModeArmReason = () => autoModeArmReasonOf(permissionMode.declared === "auto", autoModeFace !== undefined, runtimeCaps, runtimeCapsFaulted);
216
+ if (autoModeFace !== undefined)
217
+ screenAutoModeKnobs(autoModeFace);
216
218
  const armAutoMode = async () => {
217
219
  if (autoModeDeciderRef.current !== undefined)
218
220
  return;
219
- if (permissionMode.declared !== "auto" || deps.autoMode === undefined || runtimeCaps?.autoMode === false)
221
+ if (permissionMode.declared !== "auto" || autoModeFace === undefined || runtimeCaps?.autoMode === false)
220
222
  return;
221
- const am = deps.autoMode;
223
+ const am = autoModeFace;
222
224
  const classifierSeat = classifierSeatModel({ deps, main: model });
223
225
  let classifierModel = classifierSeat.model;
224
226
  if (await derivedRouteFallsBack({ seat: "auto-mode-classifier", derived: classifierModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId, runId, onBrokenPairing: autoModeClassifierFallbackOf(am.classifierFallback) === "none" ? "refuse" : "fall_back" })) {
@@ -277,7 +279,7 @@ export async function prepareCapsAndWorkflow(input) {
277
279
  autoModeDenialTrackingRef.current = denialTracking;
278
280
  autoModeArmingRef.current = arming;
279
281
  };
280
- permissionMode.bindArming({ caps: runtimeCaps, autoFacePresent: deps.autoMode !== undefined, armAuto: armAutoMode, onArmFault: (e) => deps.onError?.(asThrownError(e), { phase: "config", sessionId }) });
282
+ permissionMode.bindArming({ caps: runtimeCaps, autoFacePresent: autoModeFace !== undefined, armAuto: armAutoMode, onArmFault: (e) => deps.onError?.(asThrownError(e), { phase: "config", sessionId }) });
281
283
  const deploymentWorkflowReady = runnerSelf !== undefined && isSelfOrchestrationActive(spec, deps);
282
284
  const selfOrchestrationActive = deploymentWorkflowReady && runtimeCaps?.allowWorkflows !== false;
283
285
  if (deploymentWorkflowReady && runtimeCaps?.allowWorkflows === false) {
@@ -2,12 +2,12 @@ import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
2
2
  import { mountedToolRequiresUserInteraction } from "../mode-policy.js";
3
3
  import { complianceCallDenial } from "../compliance.js";
4
4
  import { cloneObserverInput, formatHookFeedback, runToolGate } from "../hooks.js";
5
- import { screenGateOutcome, releasePolicyDefects } from "../gate-outcome.js";
5
+ import { screenGateOutcome } from "../gate-outcome.js";
6
6
  import { directDeny, frozenInputSnapshot } from "./gate-exit.js";
7
7
  import { emitTrace } from "../trace.js";
8
8
  import { deliverEngineNotice } from "../engine-notice.js";
9
9
  import { askUnresolvableNotice } from "../ask-unresolvable-notice.js";
10
- import { askReachesPerson } from "../wiring-manifest.js";
10
+ import { approvalRouteOf } from "../wiring-manifest.js";
11
11
  import { classifierUnavailableSeat } from "./classifier-ladder.js";
12
12
  import { PATH_CONFINABLE_WRITE_TOOLS } from "./session-rule-policy.js";
13
13
  import { callFaceSeat, rosterToolName, survivorsByName, toolCallFaceOf } from "../tool-roster.js";
@@ -20,7 +20,7 @@ function screenGateResult(result) {
20
20
  const gate = result.gate;
21
21
  if (gate === undefined)
22
22
  return { defects: ["a tool-gate result carried no outcome record on a terminal exit — every terminal exit mints one; the frame carries none"] };
23
- const defects = [...screenGateOutcome(gate), ...releasePolicyDefects(gate)];
23
+ const defects = screenGateOutcome(gate, { reading: "producer" });
24
24
  const blocked = result.block === true;
25
25
  if ((gate.disposition.kind === "denied") !== blocked) {
26
26
  defects.push(`a tool-gate result's outcome reads "${gate.disposition.kind}" on ${blocked ? "a blocked" : "an executing"} call — the two faces of one verdict disagree; the frame carries no outcome`);
@@ -140,7 +140,7 @@ export function prepareGateStations(input) {
140
140
  ...(notifyPermissionDenied ? { permissionDenied: (p) => notifyPermissionDenied({ ...p, permissionMode: permissionMode.observed }) } : {}),
141
141
  onHookError: notifyHookError,
142
142
  shellGated: tierSeat?.shellGated(shellGatedTools, toolMarkSources) === true,
143
- ...(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 }) } } : {}),
143
+ ...(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 } : {}), route: approvalRouteOf(askRoute), onNotice: deps.onNotice, sessionId, runId }) } } : {}),
144
144
  permissionMode: () => permissionMode.current, askSeatForm: askRoute.form,
145
145
  ...(markSources.size > 0 ? { toolMarkSources: markSources } : {}),
146
146
  ...seatInputsFor(e.toolName, wireOf("gate.skillActivationSeat", call)),
@@ -19,6 +19,7 @@ import { sessionSeatAnchorHolds } from "./session-read-file-states.js";
19
19
  import { rebaseWorkspacePath } from "./workspace-path.js";
20
20
  import { RosterBuilder } from "../tool-roster.js";
21
21
  import { unionAddOnly } from "../child-clamp-seats.js";
22
+ import { assertStringArraySeat } from "../read-face-seats.js";
22
23
  import { modePolicyOf } from "../mode-policy.js";
23
24
  const faceClampAnnouncedSinks = { read: new WeakSet(), write: new WeakSet() };
24
25
  const faceClampConsoleAnnounced = { read: false, write: false };
@@ -39,25 +40,6 @@ export function __resetReadFaceClampAnnouncement() {
39
40
  faceClampConsoleAnnounced.read = false;
40
41
  faceClampConsoleAnnounced.write = false;
41
42
  }
42
- function assertStringArraySeat(value, seat, entryLaw) {
43
- if (value === undefined)
44
- return;
45
- if (!Array.isArray(value)) {
46
- const e = new Error(`${seat} must be an array or absent (got ${value === null ? "null" : typeof value}) — a non-array here would be iterated character by character, so it refuses loudly instead.`);
47
- e.code = "config.read_face_seat";
48
- throw e;
49
- }
50
- if (entryLaw !== "strings")
51
- return;
52
- const entries = value;
53
- for (const entry of entries) {
54
- if (typeof entry !== "string") {
55
- const e = new Error(`${seat} entries must be strings (got ${entry === null ? "null" : typeof entry}).`);
56
- e.code = "config.read_face_seat";
57
- throw e;
58
- }
59
- }
60
- }
61
43
  function writeFaceReader(input, onDeploymentClamp) {
62
44
  const { spec, deps, resume, fullShellReachable, permissionMode } = input;
63
45
  const seats = { kind: "write", readOnlyMount: spec.handsReadOnly === true, orgGoverned: deps.permissionRuleStore?.partitions.org === true, fullShellReachable };
@@ -8,7 +8,7 @@ import { StubExecutionEnv } from "../stub-env.js";
8
8
  import { askApproverIdentity, durableMandateSourceOf } from "../tool-policy.js";
9
9
  import { chainAnchorRecords } from "./child-tree.js";
10
10
  import { deliverEngineNotice } from "../types.js";
11
- import { PermissionModeController, modeRequestAfterResume } from "../mode-policy.js";
11
+ import { PermissionModeController, autoModeFaceOf, modeRequestAfterResume } from "../mode-policy.js";
12
12
  import { SessionModeSeats } from "./session-mode-seats.js";
13
13
  import { recordedModeSeed } from "../checkpoint-store.js";
14
14
  import { frozenSkillGrants } from "../skill-grant.js";
@@ -42,22 +42,22 @@ export function prepareInheritedGate(input) {
42
42
  const policy = pathRuleAnchors.onLeg(pc.policy, position);
43
43
  return policy === pc.policy ? pc : { ...pc, policy };
44
44
  });
45
- const inheritedParentConstraints = attachRebuiltDenialTrackers(chainView, deps.autoMode?.denialLimit);
45
+ const autoModeFace = autoModeFaceOf(deps, liveInheritedGate);
46
+ const inheritedParentConstraints = attachRebuiltDenialTrackers(chainView, autoModeFace?.denialLimit);
46
47
  const parentSeat = liveInheritedGate?.permissionModeCeiling;
47
48
  const ceiling = parentSeat ?? liveInheritedGate?.permissionMode;
48
- const announceModeFold = (receipt) => {
49
+ const announceModeFold = (receipt, folded) => {
49
50
  if (receipt.foldedBy === undefined)
50
51
  return;
51
- const requested = receipt.requested ?? "default";
52
52
  const because = receipt.foldedBy === "operator_deny" ? "the deployment denies it for this principal" : receipt.foldedBy === "face_absent" ? "no auto-mode classifier face is wired on this deployment" : `the parent leg's mode is the ceiling a child's declaration is clamped under`;
53
- const message = `permission mode "${requested}" is not available on this leg (${because}) — it runs under "${receipt.effective}"`;
54
- deliverEngineNotice(deps.onNotice, { code: "config.permission_mode_unavailable", message, detail: { requested, effective: receipt.effective, by: receipt.foldedBy, sessionId, runId } });
55
- if (receipt.foldedBy === "inherited_ceiling" && parentSeat !== undefined) {
56
- deliverEngineNotice(deps.onNotice, { code: "config.permission_mode_unavailable", message: `${message} (a child leg, ${sessionId})`, detail: { requested, effective: receipt.effective, by: receipt.foldedBy, sessionId: parentSeat.sessionId, childSessionId: sessionId, runId } });
53
+ const message = `permission mode "${folded}"${receipt.requested === undefined ? " (inherited)" : ""} is not available on this leg (${because}) — it runs under "${receipt.effective}"`;
54
+ deliverEngineNotice(deps.onNotice, { code: "config.permission_mode_unavailable", message, detail: { requested: folded, effective: receipt.effective, by: receipt.foldedBy, sessionId, runId } });
55
+ if (parentSeat !== undefined) {
56
+ deliverEngineNotice(deps.onNotice, { code: "config.permission_mode_unavailable", message: `${message} (a child leg, ${sessionId})`, detail: { requested: folded, effective: receipt.effective, by: receipt.foldedBy, sessionId: parentSeat.sessionId, childSessionId: sessionId, runId } });
57
57
  }
58
58
  };
59
59
  const permissionMode = new PermissionModeController(modeRequestAfterResume(permissionModeSeat, modeSeed, resume?.outcome), ceiling, { onFold: announceModeFold, ...(modeSeed.prePlanMode !== undefined ? { prePlanMode: modeSeed.prePlanMode } : {}) });
60
- announceModeFold(permissionMode.receipt());
60
+ permissionMode.announceStart();
61
61
  const { ceiling: legModeCeiling, commit: commitLegModeCeiling, end: endModeCeiling } = (internals?.sessionModeSeats ?? new SessionModeSeats()).legCeiling(sessionId, permissionMode);
62
62
  const commitModeSeats = (legSignal) => {
63
63
  commitLegModeCeiling();
@@ -186,6 +186,7 @@ export function prepareInheritedGate(input) {
186
186
  ...(effectiveShellGate !== undefined ? { shellGate: effectiveShellGate } : {}),
187
187
  permissionMode: permissionMode.current,
188
188
  permissionModeCeiling: legModeCeiling,
189
+ ...(autoModeFace !== undefined ? { autoModeFace } : {}),
189
190
  ...(parentConstraints.length > 0 ? { parentConstraints } : {}),
190
191
  ...chainAnchorRecords(pathRuleAnchors.chainTop, parentConstraints),
191
192
  admittedOrgScopes: memoryAdmittedOrgScopesRef.current,
@@ -107,8 +107,6 @@ export interface PreparePolicyChainInput {
107
107
  /** borrowed-readonly — the run's one reader of the person's rules (`person-path-rows.ts`): the lanes take each call's
108
108
  * pinned read from it. */
109
109
  personRows: import("./person-path-rows.js").PersonPathRows;
110
- /** borrowed-readonly — a reader of the same rows the run's cut does not end (the same notices), for the lane asked after the run. */
111
- personRowsBeyondRun: import("./person-path-rows.js").PersonPathRows;
112
110
  /** borrowed-readonly — whether the question tool mounted (the lanes' question-tool exclusion). */
113
111
  questionToolMounted: boolean;
114
112
  /** borrowed-readonly — the resolved per-principal entitlements; only `forceDurableGate` (the mandate-park facility and
@@ -241,9 +239,9 @@ export interface PreparePolicyChainResult {
241
239
  grantBindingOf: GrantBindingOf;
242
240
  /** owned — the personal permission-rule lane, or undefined. */
243
241
  permissionRuleLane: PermissionRuleLanes["personal"];
244
- /** owned — the same two lanes (personal and org) over the reader the run's cut does not end, for the one judgment asked after the
245
- * run has ended (the synthetic write face). */
246
- ruleLanesBeyondRun: PermissionRuleLanes;
242
+ /** owned — the leg's facts the two lanes judge against (session, root and anchors, head leads, sinks), for the consolidation
243
+ * write judge, which builds its own two lanes over the reader the run's cut does not end (it is asked after the run). */
244
+ ruleLaneFacts: import("./memory-consolidation-write-judge.js").ConsolidationWriteJudgeLeg["lanes"];
247
245
  /** owned — the org permission-rule lane (`Prepared.permissionRuleOrg`), or undefined. */
248
246
  permissionRuleOrgLane: PermissionRuleLanes["org"];
249
247
  /** owned — whether the sandbox-admission mode armed (declared isolation with its park prerequisites). */
@@ -20,13 +20,14 @@ import { createModeGatePolicy, mountedToolDeclaresReadOnly } from "../mode-polic
20
20
  const ancestorFacts = (ask) => ({ ...(ask ?? {}), ancestorResolved: true });
21
21
  import { headlessDenyAtFold, headlessDenyAtRecheck, INHERITED_STATION_FACTS, inheritedAskCarry, inheritedAskFallbackMessage, judgeInheritedClassifier, lateAskSettlementObserver, resolveInheritedAsk, settleDenialLimitFallback } from "./denial-limit-arms.js";
22
22
  import { settleReadRootGrant } from "./read-root-grant.js";
23
+ import { frozenApproverRoute } from "../wiring-manifest.js";
23
24
  import { askGrantShapeOf } from "./inherited-ask-grants.js";
24
25
  import { createPermissionRuleLanes, inheritedAskRuleEvidence, createRuleOffersOf } from "./permission-rule-lanes.js";
25
26
  import { createLiveSessionRules, createSessionRulePolicy } from "./session-rule-policy.js";
26
27
  import { policyThrowLine, reportPolicyThrow } from "./policy-throw.js";
27
28
  import { heldBy, heldValues, seatOf, wireOf } from "../tool-name-readings.js";
28
29
  export async function preparePolicyChain(input) {
29
- const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, pathRuleAnchors, headLeads, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, personRows, personRowsBeyondRun, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, readFace, permissionMode, scratchpadDir, toolRosterDeltas, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
30
+ const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, pathRuleAnchors, headLeads, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, personRows, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, readFace, permissionMode, scratchpadDir, toolRosterDeltas, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
30
31
  const ancestorArbitrations = new Map();
31
32
  const arbitrateInherited = (ask, toolCallId, judged) => {
32
33
  const verdict = arbitrateByMode(permissionMode.current, judged.classification, { origin: judged.origin, ...askMandateMember(ancestorFacts(judged.ask.action === "ask" ? judged.ask : undefined)) });
@@ -124,7 +125,7 @@ export async function preparePolicyChain(input) {
124
125
  rules: row,
125
126
  };
126
127
  };
127
- sessionRulePolicy = createSessionRulePolicy({ seed, read: createLiveSessionRules(store, { ...sessionRuleKey, seed }, { onUnreadable: announceSessionRulesUnreadable, onEnforced: publishOwnRules }) }, { env: executionEnv, rootPath: taskRootFinal, toolEffects });
128
+ sessionRulePolicy = createSessionRulePolicy({ seed, read: createLiveSessionRules(store, { ...sessionRuleKey, seed }, { onUnreadable: announceSessionRulesUnreadable, onEnforced: publishOwnRules }) }, { env: executionEnv, rootPath: taskRootFinal, toolEffects, anchors: pathRuleAnchors.leg });
128
129
  if (rules)
129
130
  publishOwnRules(rules);
130
131
  }
@@ -145,7 +146,7 @@ export async function preparePolicyChain(input) {
145
146
  ...(deps.sessionPolicyStore
146
147
  ? { read: createLiveSessionRules(deps.sessionPolicyStore, { sessionId: snap.sessionId, ...(snap.principal !== undefined ? { principal: snap.principal } : {}), seed }, { onUnreadable: announceSessionRulesUnreadable, onEnforced: publishAncestorRules }) }
147
148
  : {}),
148
- }, { env: executionEnv, rootPath: taskRootFinal, toolEffects }));
149
+ }, { env: executionEnv, rootPath: taskRootFinal, toolEffects, anchors: pathRuleAnchors.leg }));
149
150
  }
150
151
  const frozenConstraintLayers = (seedInheritedGate?.constraintChain ?? [])
151
152
  .filter((e) => !("opaque" in e))
@@ -321,10 +322,7 @@ export async function preparePolicyChain(input) {
321
322
  const listFacts = { root: taskRootFinal, sessionId, liveCwd: () => handsCwdRef?.current, anchors: pathRuleAnchors.leg };
322
323
  const skillGrantLane = skillGrants.lane(listFacts);
323
324
  const allowEntryLanes = { request: allowedToolTables.request.lane(listFacts), deployment: allowedToolTables.deployment.lane(listFacts) };
324
- const ruleLaneConfig = {
325
- provider: deps.permissionRuleStore,
326
- localOwnerDeclared: deps.localOwnerRules === true,
327
- principal: spec.principal,
325
+ const ruleLaneFacts = {
328
326
  sessionId,
329
327
  root: taskRootFinal,
330
328
  rootCanonical: pathRuleAnchors.leg.root,
@@ -338,8 +336,7 @@ export async function preparePolicyChain(input) {
338
336
  onDisclosure: (message) => emitTrace(deps.tracer, () => ({ kind: "permission.rule_store_unreadable", version: 1, taskId: hostTaskId, message, ts: Date.now() })),
339
337
  stripsAllow: (rule, req) => permissionMode.current === "auto" && autoStrippedRule(rule, req),
340
338
  };
341
- const { personal: permissionRuleLane, org: permissionRuleOrgLane } = createPermissionRuleLanes({ ...ruleLaneConfig, rows: personRows });
342
- const ruleLanesBeyondRun = createPermissionRuleLanes({ ...ruleLaneConfig, rows: personRowsBeyondRun });
339
+ const { personal: permissionRuleLane, org: permissionRuleOrgLane } = createPermissionRuleLanes({ provider: deps.permissionRuleStore, localOwnerDeclared: deps.localOwnerRules === true, principal: spec.principal, ...ruleLaneFacts, rows: personRows });
343
340
  const ruleOffersOf = createRuleOffersOf({
344
341
  laneArmed: permissionRuleLane !== undefined,
345
342
  principal: spec.principal,
@@ -386,7 +383,7 @@ export async function preparePolicyChain(input) {
386
383
  }
387
384
  if (re.updatedInput !== undefined)
388
385
  editArgs = re.updatedInput;
389
- const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: ancestorAutoMode, ask: re, req: { ...creq, args: editArgs }, signal: csignal ?? abortController.signal, subject: "the approved edit", allowRewrite: editArgs, facts: inheritedStationFacts(creq), unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(ancestorDecider?.timeoutMs !== undefined ? { budgetMs: ancestorDecider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(onAskOf), onNotice: deps.onNotice, sessionId, runId }) });
386
+ 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 }) });
390
387
  if (judged.kind === "allow") {
391
388
  return onApproval(judged.result);
392
389
  }
@@ -461,7 +458,7 @@ export async function preparePolicyChain(input) {
461
458
  return first;
462
459
  return mandateUnreconstructibleDeny(creq);
463
460
  }
464
- const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: pc.autoMode, ask: first, req: creq, signal: csignal ?? abortController.signal, subject: "this call", facts: inheritedStationFacts(creq), unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(pc.autoMode?.decider.timeoutMs !== undefined ? { budgetMs: pc.autoMode.decider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(pc.onAsk), onNotice: deps.onNotice, sessionId, runId }) });
461
+ 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 }) });
465
462
  if (judged.kind === "allow")
466
463
  return judged.result;
467
464
  if (judged.kind === "deny")
@@ -537,7 +534,7 @@ export async function preparePolicyChain(input) {
537
534
  return mandateUnreconstructibleDeny(creq);
538
535
  }
539
536
  const presentedArgs = decision.updatedInput !== undefined ? decision.updatedInput : creq.args;
540
- const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: pc.autoMode, ask: decision, req: { ...creq, args: presentedArgs }, signal: csignal ?? abortController.signal, subject: "this call", allowRewrite: decision.updatedInput, facts: inheritedStationFacts(creq), unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(pc.autoMode?.decider.timeoutMs !== undefined ? { budgetMs: pc.autoMode.decider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(pc.onAsk), onNotice: deps.onNotice, sessionId, runId }) });
537
+ 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 }) });
541
538
  if (judged.kind === "allow")
542
539
  return judged.result;
543
540
  if (judged.kind === "deny")
@@ -679,7 +676,7 @@ export async function preparePolicyChain(input) {
679
676
  return decision;
680
677
  })
681
678
  : foldedPolicy;
682
- return { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, allowEntryLanes, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, grantBindingOf, permissionRuleLane, ruleLanesBeyondRun, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations };
679
+ return { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, allowEntryLanes, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, grantBindingOf, permissionRuleLane, ruleLaneFacts, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations };
683
680
  }
684
681
  const advisedPolicyNames = new WeakMap();
685
682
  const ADVISED_KEYS_CAP = 64;