@sema-agent/core 7.50.2 → 7.51.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.
Files changed (188) hide show
  1. package/CHANGELOG.md +263 -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-notice.d.ts +35 -11
  37. package/dist/core/gate-fold.js +9 -7
  38. package/dist/core/gate-lanes.js +6 -4
  39. package/dist/core/gate-outcome.d.ts +81 -15
  40. package/dist/core/gate-outcome.js +168 -67
  41. package/dist/core/governance-codes.d.ts +7 -1
  42. package/dist/core/governance-codes.js +9 -0
  43. package/dist/core/hooks.d.ts +4 -4
  44. package/dist/core/hooks.js +4 -3
  45. package/dist/core/mcp-connection-pool.d.ts +2 -0
  46. package/dist/core/mcp-connection-pool.js +2 -1
  47. package/dist/core/mcp-failure.js +2 -1
  48. package/dist/core/mcp-server-spec.d.ts +27 -5
  49. package/dist/core/mcp.d.ts +13 -38
  50. package/dist/core/mcp.js +28 -23
  51. package/dist/core/media-byte-cap.d.ts +25 -0
  52. package/dist/core/media-byte-cap.js +18 -2
  53. package/dist/core/memory-engine/consolidation-driver.js +1 -1
  54. package/dist/core/mode-policy.d.ts +25 -1
  55. package/dist/core/mode-policy.js +28 -13
  56. package/dist/core/own-property-receipt.d.ts +18 -0
  57. package/dist/core/own-property-receipt.js +39 -0
  58. package/dist/core/permission-rule-model.d.ts +32 -11
  59. package/dist/core/permission-rule-model.js +12 -7
  60. package/dist/core/persisted-rule-arms.d.ts +3 -3
  61. package/dist/core/prompt-section-seat.d.ts +54 -0
  62. package/dist/core/prompt-section-seat.js +49 -0
  63. package/dist/core/protocol-http-auth.d.ts +44 -0
  64. package/dist/core/protocol-http-auth.js +45 -0
  65. package/dist/core/read-face-seats.d.ts +30 -0
  66. package/dist/core/read-face-seats.js +38 -0
  67. package/dist/core/redaction-passes.d.ts +82 -0
  68. package/dist/core/redaction-passes.js +378 -0
  69. package/dist/core/remote-env.d.ts +4 -3
  70. package/dist/core/resume-startup-cause.d.ts +56 -0
  71. package/dist/core/resume-startup-cause.js +43 -0
  72. package/dist/core/rule-denial-sentence.d.ts +2 -2
  73. package/dist/core/runner/announce-once-ledger.js +2 -2
  74. package/dist/core/runner/classifier-ladder.d.ts +17 -13
  75. package/dist/core/runner/classifier-ladder.js +12 -10
  76. package/dist/core/runner/construction-doors.js +9 -20
  77. package/dist/core/runner/contracts.d.ts +4 -4
  78. package/dist/core/runner/gate-exit.js +2 -2
  79. package/dist/core/runner/memory-consolidation-write-judge.d.ts +53 -9
  80. package/dist/core/runner/memory-consolidation-write-judge.js +39 -1
  81. package/dist/core/runner/permission-rule-lanes.d.ts +8 -13
  82. package/dist/core/runner/person-path-rows.d.ts +4 -3
  83. package/dist/core/runner/policy-throw.js +4 -4
  84. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
  85. package/dist/core/runner/prepare-caps-and-workflow.js +8 -6
  86. package/dist/core/runner/prepare-gate-stations.js +4 -4
  87. package/dist/core/runner/prepare-hands-readface.js +1 -19
  88. package/dist/core/runner/prepare-inherited-gate.js +10 -9
  89. package/dist/core/runner/prepare-policy-chain.d.ts +3 -5
  90. package/dist/core/runner/prepare-policy-chain.js +10 -13
  91. package/dist/core/runner/prepare-project-context.d.ts +4 -4
  92. package/dist/core/runner/prepare-project-context.js +2 -1
  93. package/dist/core/runner/prepare-prompt-assembly.js +1 -0
  94. package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -1
  95. package/dist/core/runner/prepare-prompt-inputs.js +4 -4
  96. package/dist/core/runner/prepare-protocol-tools.d.ts +4 -3
  97. package/dist/core/runner/prepare-protocol-tools.js +3 -2
  98. package/dist/core/runner/prepare-task.js +6 -4
  99. package/dist/core/runner/prepare-turn-wiring.d.ts +16 -13
  100. package/dist/core/runner/prepare-turn-wiring.js +8 -7
  101. package/dist/core/runner/run-harness-handlers.js +2 -2
  102. package/dist/core/runner/run-leg.js +10 -9
  103. package/dist/core/runner/run-settle-and-teardown.js +5 -5
  104. package/dist/core/runner/run-turn-boundary.js +3 -2
  105. package/dist/core/runner/runtask.d.ts +1 -2
  106. package/dist/core/runner/runtask.js +8 -12
  107. package/dist/core/runner/session-rule-policy.d.ts +15 -11
  108. package/dist/core/runner/session-rule-policy.js +43 -33
  109. package/dist/core/runner/stream-lifecycle-verbs.js +2 -2
  110. package/dist/core/runner/stream-settle-backstop.js +2 -1
  111. package/dist/core/runner/sub-agent-deny-lists.d.ts +12 -8
  112. package/dist/core/runner/sub-agent-deny-lists.js +23 -12
  113. package/dist/core/runner/terminal-account.js +3 -3
  114. package/dist/core/runner/tool-output-projection.js +3 -8
  115. package/dist/core/runner/tool-result-media-seat.d.ts +19 -0
  116. package/dist/core/runner/tool-result-media-seat.js +13 -0
  117. package/dist/core/runner/turn-attachments.d.ts +13 -12
  118. package/dist/core/runner/turn-attachments.js +11 -7
  119. package/dist/core/runner-deps.d.ts +4 -3
  120. package/dist/core/safety-merge-corpus.js +21 -21
  121. package/dist/core/secret-env.js +1 -1
  122. package/dist/core/session-policy-store.d.ts +89 -72
  123. package/dist/core/session-policy-store.js +100 -93
  124. package/dist/core/shell-read-silence.d.ts +38 -4
  125. package/dist/core/shell-read-silence.js +102 -0
  126. package/dist/core/side-query.d.ts +7 -8
  127. package/dist/core/skill-grant-binding.d.ts +32 -11
  128. package/dist/core/skill-grant-binding.js +11 -3
  129. package/dist/core/skill-grant.d.ts +4 -3
  130. package/dist/core/skill-grant.js +5 -4
  131. package/dist/core/skills-directory.d.ts +12 -1
  132. package/dist/core/skills-directory.js +17 -8
  133. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
  134. package/dist/core/store-contracts/checkpoint-store-contract.js +3 -0
  135. package/dist/core/swappable-deps.d.ts +15 -1
  136. package/dist/core/swappable-deps.js +25 -1
  137. package/dist/core/synthetic-read-verdict.d.ts +16 -14
  138. package/dist/core/synthetic-read-verdict.js +28 -8
  139. package/dist/core/task-notification.d.ts +13 -3
  140. package/dist/core/task-notification.js +12 -1
  141. package/dist/core/task-spec.d.ts +14 -15
  142. package/dist/core/tool-name-readings.d.ts +0 -4
  143. package/dist/core/tool-name-readings.js +0 -1
  144. package/dist/core/tool-policy.d.ts +40 -9
  145. package/dist/core/tool-policy.js +25 -7
  146. package/dist/core/types.d.ts +3 -3
  147. package/dist/core/types.js +1 -1
  148. package/dist/core/untrusted-egress.d.ts +60 -11
  149. package/dist/core/untrusted-egress.js +120 -9
  150. package/dist/core/untrusted-text.js +16 -0
  151. package/dist/core/wiring-manifest.d.ts +22 -8
  152. package/dist/core/wiring-manifest.js +12 -4
  153. package/dist/engine/harness/agent-harness.d.ts +2 -1
  154. package/dist/engine/harness/agent-harness.js +4 -1
  155. package/dist/engine/harness/types.d.ts +3 -0
  156. package/dist/engine/llm/types.d.ts +7 -9
  157. package/dist/engine/loop/agent-loop.d.ts +8 -3
  158. package/dist/engine/loop/agent-loop.js +26 -9
  159. package/dist/engine/loop/types.d.ts +10 -0
  160. package/dist/index.d.ts +17 -5
  161. package/dist/index.js +14 -5
  162. package/dist/orchestration/run-workflow-tool.js +8 -8
  163. package/dist/orchestration/workflow.js +2 -2
  164. package/dist/prompt-assembly/assemble.d.ts +3 -1
  165. package/dist/prompt-assembly/assemble.js +6 -0
  166. package/dist/prompts/cowork.d.ts +42 -0
  167. package/dist/prompts/cowork.js +529 -0
  168. package/dist/prompts/default.d.ts +14 -1
  169. package/dist/prompts/default.js +3 -0
  170. package/dist/stores/file/session-policy-store.d.ts +6 -3
  171. package/dist/stores/file/session-policy-store.js +5 -16
  172. package/dist/tools/artifact/artifact-tool.js +2 -0
  173. package/dist/tools/fs/image-admission.d.ts +57 -4
  174. package/dist/tools/fs/image-admission.js +85 -0
  175. package/dist/tools/fs/path-containment.d.ts +2 -0
  176. package/dist/tools/fs/path-containment.js +5 -0
  177. package/dist/tools/fs/shell-read-check.d.ts +31 -6
  178. package/dist/tools/fs/shell-read-check.js +2 -4
  179. package/dist/tools/fs/shell-read-environment.d.ts +10 -25
  180. package/dist/tools/fs/shell-read-environment.js +6 -18
  181. package/dist/tools/fs/shell-read-establish.d.ts +3 -2
  182. package/dist/tools/fs/shell-read-establish.js +5 -5
  183. package/dist/tools/fs/shell-read-judge.d.ts +38 -8
  184. package/dist/tools/fs/shell-read-judge.js +12 -10
  185. package/dist/tools/web.js +2 -2
  186. package/package.json +1 -1
  187. package/test/export-surface.snapshot.json +94 -10
  188. package/wire-changes/core@7.51.0.json +2421 -0
@@ -19,7 +19,6 @@ function probeFaceWord(face) {
19
19
  throw new Error(`synthetic probe id: the probe face must be a lowercase word (letters and hyphens), got ${JSON.stringify(face)}`);
20
20
  return face;
21
21
  }
22
- const ABORTED = AbortSignal.abort();
23
22
  function sameArguments(value, probed) {
24
23
  try {
25
24
  return deepJsonEqual(value, probed);
@@ -28,6 +27,22 @@ function sameArguments(value, probed) {
28
27
  return false;
29
28
  }
30
29
  }
30
+ function layerWait(run) {
31
+ const wait = new AbortController();
32
+ const runEnded = () => wait.abort(run?.reason);
33
+ if (run?.aborted === true)
34
+ runEnded();
35
+ else
36
+ run?.addEventListener("abort", runEnded, { once: true });
37
+ return {
38
+ signal: wait.signal,
39
+ end: () => {
40
+ run?.removeEventListener("abort", runEnded);
41
+ if (!wait.signal.aborted)
42
+ wait.abort(new DOMException("the judgment stopped waiting for this layer", "TimeoutError"));
43
+ },
44
+ };
45
+ }
31
46
  export function createSyntheticReadJudge(layers) {
32
47
  const judge = createProbeJudge(layers, "read");
33
48
  return async (call, face, budget) => {
@@ -61,9 +76,10 @@ export function createSyntheticWriteJudge(layers, facts) {
61
76
  }
62
77
  function createProbeJudge(layers, kind) {
63
78
  const everyAskHeld = kind === "write";
64
- return async (probe, budget) => {
79
+ return async (unstated, budget) => {
65
80
  const deadlineAt = budget?.deadlineAt ?? Date.now() + ORG_ADJUDICATION_TIMEOUT_MS;
66
81
  const left = () => Math.max(0, Math.min(ORG_ADJUDICATION_TIMEOUT_MS, deadlineAt - Date.now()));
82
+ const probe = { ...unstated, approvalReach: "none" };
67
83
  const probedArgs = structuredClone(probe.args);
68
84
  const stamped = layers.stampCallTarget?.(probe, layers.signal);
69
85
  const req = stamped !== undefined ? await stamped : probe;
@@ -84,7 +100,7 @@ function createProbeJudge(layers, kind) {
84
100
  cannot(layer, "timed_out", "the judgment's wait budget was spent before this layer was asked");
85
101
  return true;
86
102
  };
87
- const policyLayer = async (layer, policy, signal, ancestorOwned = false) => {
103
+ const policyLayer = async (layer, policy, ancestorOwned = false) => {
88
104
  if (spent(layer))
89
105
  return undefined;
90
106
  const waitMs = left();
@@ -92,18 +108,22 @@ function createProbeJudge(layers, kind) {
92
108
  let thrown = undefined;
93
109
  const TIMEOUT = Symbol("timeout");
94
110
  let d;
111
+ const wait = layerWait(layers.signal);
95
112
  try {
96
113
  d = await settleOrgVerdictWithin(Promise.resolve()
97
- .then(() => policy.check(req, signal))
114
+ .then(() => policy.check(req, wait.signal))
98
115
  .catch((err) => {
99
116
  thrown = { err };
100
117
  return TIMEOUT;
101
- }), TIMEOUT, { ...(layers.signal !== undefined ? { signal: layers.signal } : {}), timeoutMs: waitMs, onFallback: () => void (timedOut = true) });
118
+ }), TIMEOUT, { signal: wait.signal, timeoutMs: waitMs, onFallback: () => void (timedOut = true) });
102
119
  }
103
120
  catch (err) {
104
121
  cannot(layer, "threw", describeThrown(err));
105
122
  return undefined;
106
123
  }
124
+ finally {
125
+ wait.end();
126
+ }
107
127
  if (thrown !== undefined) {
108
128
  cannot(layer, "threw", describeThrown(thrown.err));
109
129
  return undefined;
@@ -144,19 +164,19 @@ function createProbeJudge(layers, kind) {
144
164
  cannot("skill_rows", "threw", describeThrown(err));
145
165
  }
146
166
  if (layers.denyNarrowingPolicy !== undefined) {
147
- const v = await policyLayer("narrowing", layers.denyNarrowingPolicy, layers.signal);
167
+ const v = await policyLayer("narrowing", layers.denyNarrowingPolicy);
148
168
  if (v !== undefined)
149
169
  return v;
150
170
  }
151
171
  if (layers.callerPolicy !== undefined) {
152
- const v = await policyLayer("caller_policy", layers.callerPolicy, ABORTED, (layers.inheritedPolicies ?? []).includes(layers.callerPolicy));
172
+ const v = await policyLayer("caller_policy", layers.callerPolicy, (layers.inheritedPolicies ?? []).includes(layers.callerPolicy));
153
173
  if (v !== undefined)
154
174
  return v;
155
175
  }
156
176
  for (const parent of layers.inheritedPolicies ?? []) {
157
177
  if (parent === layers.callerPolicy)
158
178
  continue;
159
- const v = await policyLayer("inherited_constraint", parent, ABORTED, true);
179
+ const v = await policyLayer("inherited_constraint", parent, true);
160
180
  if (v !== undefined)
161
181
  return v;
162
182
  }
@@ -1,7 +1,11 @@
1
1
  import type { MailboxAppendMessage, MailboxStore } from "./mailbox-store.js";
2
+ /** The TERMINAL words of {@link TaskNotificationStatus}: the one table every "is this frame a completion?"
3
+ * reading asks, exported as a value so a consumer outside the package reads these words rather than its own list. */
4
+ export declare const TASK_NOTIFICATION_TERMINAL_STATUSES: readonly ["completed", "failed", "killed", "cancelled"];
5
+ export type TaskNotificationTerminalStatus = (typeof TASK_NOTIFICATION_TERMINAL_STATUSES)[number];
2
6
  /** design/135 G2: `"event"` is the ONE non-terminal member — a Monitor batch notification (new stdout
3
7
  * lines from a still-running watch). Everything else is a terminal completion notification. */
4
- export type TaskNotificationStatus = "completed" | "failed" | "killed" | "cancelled" | "event";
8
+ export type TaskNotificationStatus = TaskNotificationTerminalStatus | "event";
5
9
  export type SystemInjectionPriority = "now" | "next" | "later";
6
10
  /**
7
11
  * The outcome an injection entry settles with — what the session did with the frame:
@@ -84,8 +88,8 @@ export interface TaskNotificationPayload {
84
88
  * `inlineUntrusted` + attribute-escaping, never as authority. Absent on every internal lane. */
85
89
  source?: string;
86
90
  /** design/134 §3.3: only on `status:"killed"` — who stopped it. MIRROR of task-registry `StopSource`
87
- * (the single source; inlined here to avoid an import cycle — task-registry imports this module;
88
- * third mirror: types.ts `BackgroundChildEvent.stoppedBy` — keep all three in sync). OPEN ENUM:
91
+ * (the single source; inlined here to avoid an import cycle — task-registry imports this module);
92
+ * `BackgroundChildEvent.stoppedBy` is typed by this member and projected from it. OPEN ENUM:
89
93
  * consumers must tolerate unknown values (a future "timeout" etc. is not breaking) and fall back
90
94
  * to default copy. */
91
95
  stoppedBy?: "user" | "parent" | "system" | (string & {});
@@ -507,6 +511,12 @@ export declare function terminalFrameFacets(facts: {
507
511
  warning?: string | undefined;
508
512
  compaction?: TaskNotificationPayload["compaction"] | undefined;
509
513
  }): Pick<TaskNotificationPayload, "spawnName" | "handback" | "compaction">;
514
+ /**
515
+ * A delegated child's report as its completion frame carries it — the one value the parent's notification and the fleet
516
+ * terminal projected from that frame both read: redacted BEFORE the cut, with the pass the task row's copy uses (a token
517
+ * across the cut is never half-shown), then cut with an honest total. Absent in ⇒ absent out.
518
+ */
519
+ export declare function notifyResultField(result: string | undefined): string | undefined;
510
520
  /** The codec version this engine writes into an `engine_frame` record and the highest it reads. */
511
521
  export declare const ENGINE_FRAME_CODEC_VERSION = 1;
512
522
  /** The engine's own lanes — the only frames the durable park may encode (an `external` event never is). */
@@ -1,7 +1,9 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { escapeEnvelopeTag, inlineUntrusted, sanitizeUntrustedText } from "./untrusted-text.js";
3
+ import { egressText } from "./untrusted-egress.js";
3
4
  import { PEER_MESSAGE_NOTICE } from "../agents/peer-admission.js";
4
5
  import { buildCrossSessionEnvelope, CROSS_SESSION_MESSAGE_NOTICE, neutralizePeerBody } from "../agents/cross-session-envelope.js";
6
+ export const TASK_NOTIFICATION_TERMINAL_STATUSES = Object.freeze(["completed", "failed", "killed", "cancelled"]);
5
7
  export const NOTIFICATION_INJECTION_OUTCOMES = Object.freeze(["queued", "parked", "parked_durably", "dropped_duplicate"]);
6
8
  export function isNotificationInjectionOutcome(value) {
7
9
  return typeof value === "string" && NOTIFICATION_INJECTION_OUTCOMES.includes(value);
@@ -175,6 +177,15 @@ export function terminalFrameFacets(facts) {
175
177
  ...(facts.compaction !== undefined ? { compaction: { passes: facts.compaction.passes, breakerOpen: facts.compaction.breakerOpen, ...(facts.compaction.kind !== undefined ? { kind: facts.compaction.kind } : {}) } } : {}),
176
178
  };
177
179
  }
180
+ const COMPLETION_REPORT_MAX = 2_000;
181
+ export function notifyResultField(result) {
182
+ if (!result)
183
+ return undefined;
184
+ const report = egressText("same-principal", result);
185
+ return report.length > COMPLETION_REPORT_MAX
186
+ ? `${report.slice(0, COMPLETION_REPORT_MAX)}\n[result truncated: ${report.length} chars total — ask the agent for it via SendMessage, or read it with TaskOutput where mounted]`
187
+ : report;
188
+ }
178
189
  export const ENGINE_FRAME_CODEC_VERSION = 1;
179
190
  export const ENGINE_FRAME_TASK_TYPES = Object.freeze(["background_agent", "monitor", "workflow"]);
180
191
  const SHELL_LESS_CARRIERS = ["agentMessage", "crossSessionMessage", "crossSessionNotice", "inboxNotice", "_sema_provenance"];
@@ -257,7 +268,7 @@ export function engineFrameRecordOf(payload, priority, box) {
257
268
  };
258
269
  }
259
270
  const MAX_TOMBSTONES = 5_000;
260
- const TERMINAL_STATUSES = new Set(["completed", "failed", "killed", "cancelled"]);
271
+ const TERMINAL_STATUSES = new Set(TASK_NOTIFICATION_TERMINAL_STATUSES);
261
272
  export function isDelegatedAgentTerminal(n) {
262
273
  return n.task_type === "background_agent" && TERMINAL_STATUSES.has(n.status);
263
274
  }
@@ -478,10 +478,10 @@ export interface TaskSpec {
478
478
  *
479
479
  * Entries are read like {@link excludeTools}: a wire name or an alias it declares, `mcp__srv` / `mcp__srv__*` for every tool
480
480
  * of a server (including tools it lists later), a glob over the tool segment. The engine resolves the list against its OWN
481
- * roster at prepare — a host passes the names it was given and never computes a complement. A name no tool answers to, a
482
- * protocol-wide `mcp__*`, or an entry that is not a string is REFUSED at prepare (`config.tool_face_control`). The same
483
- * compiled selector is applied on every later roster change (an MCP refresh or re-dial, the deferred-search lane): an exact name
484
- * admits no new tool. Deferral is reached through ToolSearch, so a list that leaves it out defers nothing.
481
+ * roster at prepare — a host passes the names it was given. A spelling no tool name can have, `mcp__*`, or an entry that is not
482
+ * a string is REFUSED at prepare (`config.tool_face_control`); a name no tool answers to is kept and told once (`onError`, phase
483
+ * `"config"`). The same compiled selector is applied on every later roster change (an MCP refresh or re-dial, the
484
+ * deferred-search lane). Deferral is reached through ToolSearch, so a list that leaves it out defers nothing.
485
485
  *
486
486
  * A tool a leg relies on (a delegated child's ReportBlocked, a teammate's SendMessage, a goal iteration's DeclareDone, an
487
487
  * observer's report tool — the catalog's protocol column) is not an allowlist's to name; a denylist that reaches one on such a
@@ -494,8 +494,8 @@ export interface TaskSpec {
494
494
  * ships the schema bytes every request — measured at zero token savings). The DENY half of the run's tool selector, read
495
495
  * exactly as a sub-agent's denied tools are: a wire name or an alias it declares; `Tool(content)` names the whole `Tool`;
496
496
  * `mcp__srv` / `mcp__srv__*` / a glob over the tool segment for a server's tools, `mcp__*` for every MCP tool (including tools
497
- * a server lists later). A name no tool answers to, `"*"`, or an entry that is not a string is REFUSED at prepare
498
- * (`config.tool_face_control`) — a misspelt exclusion guards nothing. A deny wins over {@link allowTools}.
497
+ * a server lists later). A spelling no tool name can have, `"*"`, or an entry that is not a string is REFUSED at prepare; a name
498
+ * no tool answers to is kept (it hides a tool of that name mounted later) and told once. A deny wins over {@link allowTools}.
499
499
  * ⚠️ A list that reaches a tool this kind of leg relies on (see {@link allowTools}) is refused; excluding a host-requested
500
500
  * structural tool (the structured-output tool) can still strand the task — the caller owns that risk.
501
501
  */
@@ -610,15 +610,14 @@ export interface TaskSpec {
610
610
  * real model ref passed through — core carries no model-word vocabulary). When set (non-empty),
611
611
  * `prepareTask` REBUILDS the mounted delegation tool (via {@link ToolSpec.withAgents}) so its roster
612
612
  * is the UNION of the boot roster and these definitions — a same-name per-task definition WINS over
613
- * the boot one. Validation contract (revision of the posture, dual-leg verified against
614
- * upstream): an `allowTools` entry naming a tool that does not exist is ITEM-FILTERED at spawn (the
615
- * agent stays listed and delegable; the only host disclosure is a prepare-time HEURISTIC advisory
616
- * via `RunnerDeps.onError` phase `"config"` for entries unknown to the parent's assembled roster —
617
- * zero model-facing warning, the verified upstream shape); a `denyTools` entry is read as the
618
- * reference reads it (`Tool(content)` hides the whole `Tool`, with a host notice) and judged once, here and for the
619
- * delegation tool's own lists alike: a spelling that names no tool, or a stale one (another case of a mounted tool, a
620
- * retired name), is REJECTED at task start (`config.agent.unknown_tool`), any other name no tool carries is told to the
621
- * host and hides nothing; and
613
+ * the boot one. Validation contract (the reference's shape — an agent's tool list filters by name,
614
+ * and a name nothing answers never fails the run): both lists are judged once, here and for the
615
+ * delegation tool's own lists alike; a `denyTools` entry is read as the reference reads it
616
+ * (`Tool(content)` hides the whole `Tool`, with a host notice); an entry whose spelling no tool name
617
+ * can have is REJECTED at task start (`config.agent.unknown_tool`); an entry naming a tool that does
618
+ * not exist (a server this deployment does not declare, a stale or misspelt name) is kept as written
619
+ * — it denies, or admits, a tool of that name if one is mounted later — and told to the host once
620
+ * via `RunnerDeps.onError` phase `"config"` (zero model-facing warning); and
622
621
  * providing this field with NO delegation tool mounted is rejected too
623
622
  * (`config.agents.no_delegation_tool`) — never a silent no-op. POLICY-NEUTRAL: a
624
623
  * roster listing only; the 1.294 tighten-only parent-policy inheritance is applied at spawn time,
@@ -126,10 +126,6 @@ export declare const NAME_READINGS: {
126
126
  readonly kind: "any";
127
127
  readonly file: string;
128
128
  };
129
- readonly "policy.sessionRules": {
130
- readonly kind: "any";
131
- readonly file: string;
132
- };
133
129
  readonly "policy.sessionRules.effect": {
134
130
  readonly kind: "wire";
135
131
  readonly file: string;
@@ -33,7 +33,6 @@ export const NAME_READINGS = {
33
33
  "policy.nameLists": any(POLICY),
34
34
  "policy.shellToolsList": any(POLICY),
35
35
  "policy.projectionReplay": any(POLICY),
36
- "policy.sessionRules": any("src/core/runner/session-rule-policy.ts"),
37
36
  "policy.sessionRules.effect": wire("src/core/runner/session-rule-policy.ts"),
38
37
  "allowEntry.names": any("src/core/allow-entry.ts"),
39
38
  "skillGrant.binding": wire("src/core/skill-grant.ts"),
@@ -130,6 +130,18 @@ export interface ToolCallRequest {
130
130
  * a Runner with no `memoryEngineDir` would stamp. Read-only, frozen.
131
131
  */
132
132
  exemptRoots?: readonly string[];
133
+ /**
134
+ * The request's statement that nobody can be asked about THIS call — the word of a run's own declaration
135
+ * (`TaskSpec.approvalReach`, the closed set `APPROVAL_REACH`), stated per request. Present (`"none"`) on every request the
136
+ * engine's synthetic probes hand a policy layer (the "would this call be refused outright?" judgments: the visibility
137
+ * probes of a file the model did not ask to read, the background memory consolidation's write probes); absent on a call
138
+ * the gate adjudicates, and on a bare request. A policy that would put the call to a person — an approval seat it awaits,
139
+ * a hook it invokes, a card or a message it sends — answers at once instead: the engine's own factories read this member
140
+ * before reaching a seat and answer {@link noApproverSeatDeny}; a deployment closure does the same. The signal handed
141
+ * beside such a request is LIVE (the judge's wait for that layer): an aborted one means only that the task ended.
142
+ * Read-only.
143
+ */
144
+ approvalReach?: import("./wiring-manifest.js").ApprovalReach;
133
145
  /**
134
146
  * design/80 D-E-core: read-only per-run budget snapshot so a STATELESS policy can self-limit against
135
147
  * state that survives suspend/resume (a fresh in-memory counter would reset every leg — the supervisor
@@ -272,11 +284,25 @@ export declare function attest<T extends object>(d: T, call: {
272
284
  }, members: AttestationMembers): T;
273
285
  /** `d` carrying `record` (a fresh object), or `d` itself when there is no record to put on it. */
274
286
  export declare function withAttestation<T extends object>(d: T, record: EngineAttestation | undefined): T;
287
+ /** The `no_approver`-attested deny an engine policy answers on a request that states nobody can be asked about it
288
+ * ({@link ToolCallRequest.approvalReach}) instead of reaching a seat — read by a probe as "would ask an approver". */
289
+ export declare function noApproverSeatDeny(req: {
290
+ toolCallId: string;
291
+ toolName: string;
292
+ }, message: string, origin: AskOrigin): PermissionResult;
275
293
  /** The `task_aborted`-attested deny an engine policy answers on an aborted signal instead of reaching a seat. */
276
294
  export declare function abortedSeatDeny(req: {
277
295
  toolCallId: string;
278
296
  toolName: string;
279
297
  }, message: string, origin: AskOrigin): PermissionResult;
298
+ /** THE question every engine factory asks before it would reach a person (an approver it awaits, a hook it invokes): may the
299
+ * seat be reached? Not when the task ended (an aborted signal ⇒ {@link abortedSeatDeny}, read first) nor when the request
300
+ * states that nobody can be asked about the call ({@link ToolCallRequest.approvalReach} ⇒ {@link noApproverSeatDeny});
301
+ * `undefined` ⇒ it may. `say` is the factory's own sentence for each. */
302
+ export declare function unaskedSeatAnswer(req: Pick<ToolCallRequest, "toolCallId" | "toolName" | "approvalReach">, signal: AbortSignal | undefined, say: {
303
+ aborted: string;
304
+ unreachable: string;
305
+ }, origin: AskOrigin): PermissionResult | undefined;
280
306
  /**
281
307
  * design/252 G-7 — how long an approver-attribution identifier may be.
282
308
  *
@@ -575,12 +601,14 @@ export declare function decisionText(d: PermissionResult): string | undefined;
575
601
  *
576
602
  * `check` may be async, which is also how **human-in-the-loop approval** works: a deployment can
577
603
  * hold the promise open until an operator approves/denies. `signal` fires when the task aborts
578
- * (timeout / max turns / cancel) or the asking turn is interrupted (design/384) — honor it to release a
579
- * pending approval (F4; the Runner also races `check` against it). An already-aborted one: see `check`.
604
+ * (timeout / max turns / cancel) or the asking turn is interrupted (design/384), and on a synthetic probe when the judge
605
+ * stops waiting for this layer — honor it to release a pending approval (F4; the Runner also races `check` against it). An
606
+ * already-aborted one: see `check`. A request that states nobody can be asked about it: {@link ToolCallRequest.approvalReach}.
580
607
  */
581
608
  export interface ToolPolicy {
582
609
  /** @contract policy.check.aborted_signal_reaches_nobody — handed an already-aborted signal, `check` reaches no person and
583
- * no external approval (it answers at once, or with a `task_aborted` deny); visibility probes rely on it. */
610
+ * no external approval (it answers at once, or with a `task_aborted` deny). The engine's probes do not rely on it: they hand a
611
+ * live signal and state on the request that nobody can be asked (`approvalReach: "none"`, answered with `noApproverSeatDeny`). */
584
612
  check(req: ToolCallRequest, signal?: AbortSignal): PermissionResult | Promise<PermissionResult>;
585
613
  /**
586
614
  * The policy's CONSTRAINT PROJECTION — its deterministic DENY-verdict half expressed as pure data
@@ -1295,10 +1323,12 @@ export interface AskRequest {
1295
1323
  * mint site added later is covered by construction); optional on the type because a deployment may
1296
1324
  * invoke its approver function directly. */
1297
1325
  readonly boundInputHash?: string;
1298
- /** A call to the engine's `Skill` tool: the hash of {@link boundGrants} — the digest a parked approval of the same card is judged by, so a live card reconciles against its parked row by `(toolCallId, boundGrantDigest)` and an answer is remembered by "this tool + this binding". Present exactly on that call (whoever asked, however the question was worded), absent on every other card; `message` renders the same lists narrowed to what releases in the leg's mode. Reconciliation metadata, never adjudication input. */
1326
+ /** A call to the engine's `Skill` tool: the hash of {@link boundGrants} — the digest a parked approval of the same card is judged by, so a live card reconciles against its parked row by `(toolCallId, boundGrantDigest)` and an answer is remembered by "this tool + this binding". Present exactly on that call (whoever asked, however the question was worded); every other card carries {@link boundGrantAbsence} instead; `message` renders the same lists narrowed to what releases in the leg's mode. Reconciliation metadata, never adjudication input. */
1299
1327
  readonly boundGrantDigest?: string;
1300
1328
  /** The value {@link boundGrantDigest} hashes: `{ skill, allowedTools, disallowedTools }` in canonical spelling (de-duplicated, sorted), `skill: null` when no loaded skill is named; a fresh copy per ask; same presence. Display metadata (deployment text — render with contextual escaping). */
1301
1329
  readonly boundGrants?: import("./skill-grant-binding.js").BoundSkillGrants;
1330
+ /** Why this card carries no {@link boundGrantDigest} — every card the engine mints carries exactly one of the two: `not_bindable` (the call is not an activation of the engine's `Skill` tool, so it binds no grant and an answer may be remembered by the tool) or `unreadable` (a Skill card whose binding cannot be read — said by a stored row's reader; a live card always has its digest). The same factory mints both members; the park row and the inbox projection carry the same member. Reconciliation metadata, never adjudication input. */
1331
+ readonly boundGrantAbsence?: import("./skill-grant-binding.js").BoundGrantAbsence;
1302
1332
  /**
1303
1333
  * design/179 §4, re-formed by design/375 §3.1 (BREAKING — this seat REPLACES the retired
1304
1334
  * `ruleSuggestions` key, which is never emitted again; the new name is the safe failure posture for
@@ -1358,8 +1388,9 @@ export interface AskRequest {
1358
1388
  * seat's to say: it is answered per ask by {@link readRootCandidate}, which is present exactly when
1359
1389
  * widening the session's read directories would clear this one and absent for every other member of
1360
1390
  * the family — so a surface reads that seat and never infers an answer from this word;
1361
- * · `"shadowed"` — a rule the person already wrote is speaking and does not clear this ask (a
1362
- * matching allow rule that the lane refuses to honor here, or their own explicit `ask` rule);
1391
+ * · `"allow_outranked"` — an allow-layer member already matched this call and this question outranks it
1392
+ * ({@link allowRuleShadowed} says which): another allow rule would be outranked the same way;
1393
+ * · `"ask_rule_matched"` — an explicit `ask` rule matched: it keeps asking while it stands;
1363
1394
  * · `"lane_cannot_speak"` — the lane is armed and this ask IS clearable, but the offer lane
1364
1395
  * produced no rule for this command. Usually because the grammar has no text for it (a
1365
1396
  * substitution, a subshell, a backgrounded process, or a simple command carrying a redirection —
@@ -1381,7 +1412,7 @@ export interface AskRequest {
1381
1412
  * and a surface that ignores it renders exactly the card it rendered before. The durable park row
1382
1413
  * carries the same seat under the same contract, filled by the same one factory.
1383
1414
  */
1384
- readonly ruleOffersAbsence?: "mandated" | "lane_cannot_speak" | "shadowed";
1415
+ readonly ruleOffersAbsence?: import("./ask-origin.js").RuleOffersAbsence;
1385
1416
  /** The human/model-readable prompt explaining what needs approval. */
1386
1417
  message: string;
1387
1418
  /** Runner-filled source identity (design S1d §1): the end-user {@link AskRequest} issuing task's
@@ -1444,7 +1475,7 @@ export interface AskRequest {
1444
1475
  * remembered from an earlier call, at any scope, a session included — can retire this question; it is
1445
1476
  * answered only for THIS call, by an answerer (see the ask-arm member of the same name for who may). ABSENT is not the inverse claim: a person's own explicit `ask` rule and a
1446
1477
  * matching allow rule the lane already refused both leave it absent and still block rule clearance
1447
- * ({@link ruleOffersAbsence} words those `shadowed`). The wire twin of the
1478
+ * ({@link ruleOffersAbsence} words them `ask_rule_matched` / `allow_outranked`). The wire twin of the
1448
1479
  * {@link PermissionResult} ask-arm member of the same name, minted once by the engine at the gate's
1449
1480
  * origin stamp and copied here by the four mint stations. The DOORS are enumerated once, at
1450
1481
  * {@link import("./ask-origin.js").ruleClearanceOf}: the tool seat's own egress / irreversibility
@@ -1452,7 +1483,7 @@ export interface AskRequest {
1452
1483
  * that could not answer, a write the protection judge matched or could not resolve, governance
1453
1484
  * ({@link requiresRealApproval}), a hook's screening authority, and an ask an ANCESTOR's approver
1454
1485
  * decides. Not a door: a person's own explicit `ask` rule, which {@link ruleOffersAbsence} words
1455
- * `shadowed` — a rule they wrote is speaking, and a posture answering it crosses no mandate.
1486
+ * `ask_rule_matched` — a rule they wrote is speaking, and a posture answering it crosses no mandate.
1456
1487
  *
1457
1488
  * WHY a surface must read THIS member and not its neighbour. {@link ruleOffersAbsence} answers what
1458
1489
  * the RULE-OFFER LANE has to give, and that lane is structurally silent for any tool it does not
@@ -15,7 +15,7 @@ import { heldBy } from "./tool-name-readings.js";
15
15
  import { boundInputHashOf } from "./canonical-json.js";
16
16
  import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY } from "./untrusted-text.js";
17
17
  import { describeRejectedValue } from "./tool-search-setting.js";
18
- import { thrownText } from "./thrown-value.js";
18
+ import { egressText, egressThrownText } from "./untrusted-egress.js";
19
19
  import { assertNoRetiredToolPolicyKeys, RETIRED_DECISION_MEMBERS } from "./retired-keys.js";
20
20
  import { isEngineRefusal, nestedRecordsOf, receiveContract, receiveNestedRecords, refusedOutOfContract, screenContractValues } from "./decision-receipt.js";
21
21
  export { isEngineRefusal } from "./decision-receipt.js";
@@ -102,8 +102,25 @@ export function attest(d, call, members) {
102
102
  export function withAttestation(d, record) {
103
103
  return record === undefined ? d : { ...d, attestation: record };
104
104
  }
105
+ const UNASKED_SEAT_SETTLEMENTS = new Set(["task_aborted", "no_approver"]);
106
+ function seatNeverAsked(settlement) {
107
+ return settlement !== undefined && UNASKED_SEAT_SETTLEMENTS.has(settlement.kind);
108
+ }
109
+ function unaskedSeatDeny(req, kind, message, origin) {
110
+ return attest({ action: "deny", message }, req, { settlement: settledByNobody(kind), origin });
111
+ }
112
+ export function noApproverSeatDeny(req, message, origin) {
113
+ return unaskedSeatDeny(req, "no_approver", message, origin);
114
+ }
105
115
  export function abortedSeatDeny(req, message, origin) {
106
- return attest({ action: "deny", message }, req, { settlement: settledByNobody("task_aborted"), origin });
116
+ return unaskedSeatDeny(req, "task_aborted", message, origin);
117
+ }
118
+ export function unaskedSeatAnswer(req, signal, say, origin) {
119
+ if (signal?.aborted === true)
120
+ return abortedSeatDeny(req, say.aborted, origin);
121
+ if (req.approvalReach === "none")
122
+ return noApproverSeatDeny(req, say.unreachable, origin);
123
+ return undefined;
107
124
  }
108
125
  export const APPROVER_ATTRIBUTION_MAX_CHARS = 256;
109
126
  const APPROVER_REFUSED_CHARS_RE = /[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u2028\u2029\u202a-\u202e\u2066-\u2069]/u;
@@ -442,8 +459,9 @@ export function createApprovalPolicy(opts) {
442
459
  }
443
460
  if (nameListHolds(need, req) || namespacedCoveringHit(needCovering, req)) {
444
461
  const attestOver = (inputDigest) => (d, settlement) => ({ ...d, attestation: mintAttestation(req, { settlement, origin: "policy", inputDigest }) });
445
- if (signal?.aborted)
446
- return abortedSeatDeny(req, `approval aborted for "${req.toolName}" (run or turn interrupted)`, "policy");
462
+ const unasked = unaskedSeatAnswer(req, signal, { aborted: `approval aborted for "${req.toolName}" (run or turn interrupted)`, unreachable: `approval for "${req.toolName}" was not requested: nobody can be asked about this call` }, "policy");
463
+ if (unasked !== undefined)
464
+ return unasked;
447
465
  const shown = tryCloneArgs(req.args);
448
466
  if (!shown.ok) {
449
467
  return attestOver(boundInputHashOf(req.args))({ action: "deny", message: `approval for "${req.toolName}" could not present the args safely (unclonable value: ${shown.reason}) — denied fail-closed` }, settledByNobody("presentation_failed"));
@@ -565,7 +583,7 @@ export function combinePolicies(...policies) {
565
583
  for (const p of policies) {
566
584
  const d = refuseOutOfContractDecision(await p.check(current, signal));
567
585
  if (d.action === "deny") {
568
- if (attestationOf(d, current)?.settlement?.kind !== "task_aborted")
586
+ if (!seatNeverAsked(attestationOf(d, current)?.settlement))
569
587
  return settleDeny(d);
570
588
  seatDeny ??= settleDeny(d);
571
589
  continue;
@@ -854,10 +872,10 @@ export function tryCloneArgs(v) {
854
872
  }
855
873
  }
856
874
  export function describeThrown(err) {
857
- const contained = containThrownText(thrownText(err));
875
+ const contained = containThrownText(egressThrownText("same-principal", err));
858
876
  if (contained !== "")
859
877
  return contained;
860
- return containThrownText(describeRejectedValue(err));
878
+ return containThrownText(egressText("same-principal", describeRejectedValue(err)));
861
879
  }
862
880
  const THROWN_TEXT_MAX = 300;
863
881
  function containThrownText(text) {
@@ -18,7 +18,7 @@ export type { PermissionMode } from "./ask-origin.js";
18
18
  export type { ToolEffect, ToolContentOrigin, ReversibilityVerdict, ReadRootGrantCandidate, ToolSpec, ToolReturn, NestedUsage, NestedUsageAccum, ToolExecuteContext } from "./tool-spec.js";
19
19
  export type { BeforeWriteRequest, BeforeWriteResult, BeforeWriteHook, TrackEditRequest, TrackEditResult, TrackFileEditHook, FileEditedNotice, FileEditedHook, HandsBandOptions } from "./hands-band.js";
20
20
  export type { AgentDefinition, SkillSpec } from "./agent-definition.js";
21
- export type { McpServerSpec, A2aServerSpec, McpElicitRequest, McpElicitResponse, OnElicit } from "./mcp-server-spec.js";
21
+ export type { McpServerSpec, A2aServerSpec, McpElicitRequest, McpElicitResponse, OnElicit, McpRevocationLedger, McpMountIdentity } from "./mcp-server-spec.js";
22
22
  export type { TaskLimits } from "./task-limits.js";
23
23
  export type { TaskStatus, RemoteEnvFailureNote, EffectiveMemoryScopes, TaskResult, HumanReviewGateRow } from "./task-result.js";
24
24
  export type { TerminalCause, PausedCause } from "./terminal-cause.js";
@@ -26,8 +26,8 @@ export type { TaskEventIdentity, BrainStatusPhase, BrainRetryErrClass, BrainStat
26
26
  export type { TaskStream } from "./task-stream.js";
27
27
  export type { WorkflowGovernanceBaseline, RuntimeCaps, AutoModeDenySource } from "./runtime-caps.js";
28
28
  export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./runtime-caps.js";
29
- export type { BackgroundChildEvent, DelegationLifecycleEvent } from "./delegation-frames.js";
30
- export { __resetMalformedDelegationSeatAnnouncement, deliverDelegationLifecycle, emitsChildRowFrames, taskStartedFrame } from "./delegation-frames.js";
29
+ export type { BackgroundChildEvent, BackgroundChildRow, DelegationLifecycleEvent } from "./delegation-frames.js";
30
+ export { __resetMalformedDelegationSeatAnnouncement, backgroundChildTerminal, deliverDelegationLifecycle, emitsChildRowFrames, taskStartedFrame } from "./delegation-frames.js";
31
31
  export type { ProjectMemoryLoad, ResumePreflightInfo, ResumePreflightVerdict, RunnerDeps } from "./runner-deps.js";
32
32
  export type { EngineNotice, OwedUserInputAccount, UserInputUndrainedCause } from "./engine-notice.js";
33
33
  export { __resetMalformedNoticeSeatAnnouncement, deliverEngineNotice, owedUserInputCounts, parentLegIds, settleOwedUserInput, undrainedUserInputNotices, USER_INPUT_UNDRAINED_CAUSES, USER_INPUT_UNDRAINED_FAMILIES } from "./engine-notice.js";
@@ -1,4 +1,4 @@
1
1
  export { MODEL_ROLES } from "./model-seat.js";
2
2
  export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./runtime-caps.js";
3
- export { __resetMalformedDelegationSeatAnnouncement, deliverDelegationLifecycle, emitsChildRowFrames, taskStartedFrame } from "./delegation-frames.js";
3
+ export { __resetMalformedDelegationSeatAnnouncement, backgroundChildTerminal, deliverDelegationLifecycle, emitsChildRowFrames, taskStartedFrame } from "./delegation-frames.js";
4
4
  export { __resetMalformedNoticeSeatAnnouncement, deliverEngineNotice, owedUserInputCounts, parentLegIds, settleOwedUserInput, undrainedUserInputNotices, USER_INPUT_UNDRAINED_CAUSES, USER_INPUT_UNDRAINED_FAMILIES } from "./engine-notice.js";
@@ -1,18 +1,67 @@
1
- import { type RedactionReport } from "./arg-summary.js";
2
- export type { RedactionFinding, RedactionReport, RedactionConfidence, RedactionSource, SummarizableFinding, } from "./arg-summary.js";
3
- export { summarizeRedactions } from "./arg-summary.js";
4
- export declare function redactSecrets(s: string, report?: RedactionReport): string;
5
- export declare function redactHostLeaks(s: string, report?: RedactionReport): string;
6
- /** Secret-redact + size-bound (NO injection fence) — the SAME-PRINCIPAL tier: workflow outputs/results/errors
7
- * riding back to the launching model/script, run rows behind scope-gated observe. Paths/URLs survive
8
- * (the assertion pattern depends on them); secrets never do. */
9
- export declare function boundedRedactedSummary(value: unknown, max: number): string;
1
+ import { type RedactionReport } from "./redaction-passes.js";
2
+ export type { RedactionFinding, RedactionReport, RedactionConfidence, RedactionSource, SummarizableFinding, } from "./redaction-passes.js";
3
+ export { summarizeRedactions } from "./redaction-passes.js";
10
4
  /**
11
- * The FULL untrusted-egress transform (design/97 CORE-2): full host-leak redaction + size-bound + FENCE. Use
5
+ * Every exit that redacts — into a model's context, a host's event or error lane, a log line, a person's screen — does it
6
+ * with ONE pass, its tier's, and nothing else redacts: a text through {@link egressText}, a thrown value's text through
7
+ * {@link egressThrownText}, a value with members (a thrown error, a marks bag) through {@link egressValue}, which runs the
8
+ * same pass over every string member. What comes out is an {@link Egress}: a string the type checker knows came out of
9
+ * that tier. What a tool RETURNS is its content, read by the model that asked for it, and is not redacted (CC redacts
10
+ * nothing there either); what is redacted is what the engine itself words or carries: errors, summaries, run rows.
11
+ *
12
+ * Two tiers, because the faces have two audiences (CC 2.1.212 alignment):
13
+ * - **`same-principal`** — the model, the script and the host that launched the work (tool results, workflow agent
14
+ * outputs / results / errors, run rows behind the scope-gated observe API, host error lanes): the credential
15
+ * vocabulary only. Paths and URLs in a worker's answer are the caller's OWN machine's paths — redacting them killed the
16
+ * cross-agent assertion pattern ("return the file you wrote" → `[redacted-path]`) while protecting no one;
17
+ * cross-tenant isolation on these faces is ACCESS CONTROL (scope gates), not string surgery. CC redacts nothing here;
18
+ * sema keeps the credential pass as a declared superset.
19
+ * - **`human`** — text shown to a person or exported beyond the launching principal: the credential vocabulary plus
20
+ * every URL and absolute path — host internals, and exfil-lure links in untrusted text.
21
+ * {@link untrustedEgressForHuman} additionally FENCES its text (design/97 CORE-2), so untrusted content cannot pose as
22
+ * instructions to its reader.
23
+ */
24
+ export type EgressTier = "same-principal" | "human";
25
+ declare const EGRESS_TIER: unique symbol;
26
+ /** A string that came out of tier `T`'s pass. Only this module mints one. */
27
+ export type Egress<T extends EgressTier> = string & {
28
+ readonly [EGRESS_TIER]: T;
29
+ };
30
+ /** Tier `tier`'s one pass over a text. */
31
+ export declare function egressText<T extends EgressTier>(tier: T, text: string, report?: RedactionReport): Egress<T>;
32
+ /** The text a thrown value reads as (`thrownText`), through tier `tier`'s pass: the one form a thrown value's words take
33
+ * on their way out of the engine. */
34
+ export declare function egressThrownText<T extends EgressTier>(tier: T, err: unknown): Egress<T>;
35
+ /** The `same-principal` tier's pass, under the name hosts call it by (the engine calls {@link egressText}). */
36
+ export declare function redactSecrets(s: string, report?: RedactionReport): Egress<"same-principal">;
37
+ /** The `human` tier's pass, under the name hosts call it by (the engine calls {@link egressText}). */
38
+ export declare function redactHostLeaks(s: string, report?: RedactionReport): Egress<"human">;
39
+ /**
40
+ * Tier `tier`'s pass over every string member of `value`, returned as a DATA SNAPSHOT — never the value itself, so nothing
41
+ * that leaves can run the value's code again (a getter, a serializer, a proxy trap): a host's serializer, a log's inspector
42
+ * and the loop's own readers (`thrownText`, `thrownMarksOf`) reach what was read, once, and passed. The rules, one each:
43
+ * - a string goes through {@link egressText}, and so does every member NAME; a function is code, not data, and is not
44
+ * carried at any depth (the value handed in included: it comes back `undefined`); any other primitive is itself;
45
+ * - an error (any realm, any class) is re-minted as a plain `Error` — never on the thrown value's prototype, whose
46
+ * serializers and accessors would run again — carrying EVERY own member (`message`, `stack`, `cause`, `errors`,
47
+ * `details`, `code` …) and its `name`, `message`, `code` and `stack` read once through its chain, all as own data;
48
+ * - any other object is what JSON reads: its `toJSON(name)` when it has one (a `URL` is its `href`, a `Date` its ISO
49
+ * text; one that answers itself is read for its members), otherwise its own enumerable members;
50
+ * - a member whose name is a credential name (the vocabulary's record-key view: `password`, `Authorization`,
51
+ * `x-api-key`) and whose reading is a string is the marker whole;
52
+ * - cycles and shared members stay shared on the snapshot.
53
+ */
54
+ export declare function egressValue<T extends EgressTier, V>(tier: T, value: V): V;
55
+ /** The `same-principal` tier's pass + size-bound (NO injection fence): workflow outputs/results/errors riding back to
56
+ * the launching model/script, run rows behind scope-gated observe. Paths/URLs survive (the assertion pattern depends on
57
+ * them); secrets never do. */
58
+ export declare function boundedRedactedSummary(value: unknown, max: number): Egress<"same-principal">;
59
+ /**
60
+ * The FULL untrusted-egress transform (design/97 CORE-2): the `human` tier's pass + size-bound + FENCE. Use
12
61
  * for any worker prompt/output (#5 transcript) or steer content (#6) shown to a HUMAN or exported beyond the
13
62
  * launching principal, so untrusted text can neither leak host internals nor pose as instructions.
14
63
  */
15
64
  export declare function untrustedEgressForHuman(value: unknown, opts: {
16
65
  label: string;
17
66
  max: number;
18
- }): string;
67
+ }): Egress<"human">;