@sema-agent/core 7.50.1 → 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 (189) hide show
  1. package/CHANGELOG.md +275 -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 +87 -11
  40. package/dist/core/gate-outcome.js +171 -61
  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 +1 -1
  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 +3 -3
  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.50.2.json +42 -0
  189. package/wire-changes/core@7.51.0.json +2421 -0
@@ -1,8 +1,9 @@
1
1
  import { delimitUntrusted, neutralizeForFence } from "./untrusted-text.js";
2
- import { egressPasses, runRedactionPasses } from "./arg-summary.js";
3
- import { CREDENTIAL_VOCABULARY, egressPassGroups, URI_SCHEME_SOURCE } from "./credential-vocabulary.js";
2
+ import { egressPasses, runRedactionPasses } from "./redaction-passes.js";
3
+ import { CREDENTIAL_VOCABULARY, egressPassGroups, isSensitiveRecordKey, URI_SCHEME_SOURCE } from "./credential-vocabulary.js";
4
+ import { isErrorLike, readMember, thrownText } from "./thrown-value.js";
4
5
  import { sliceHeadSafe } from "./surrogate-safe-slice.js";
5
- export { summarizeRedactions } from "./arg-summary.js";
6
+ export { summarizeRedactions } from "./redaction-passes.js";
6
7
  const URL_OF_ANY_PROTOCOL = new RegExp(String.raw `${URI_SCHEME_SOURCE}:\/\/[^\s"'\`]+`, "gi");
7
8
  const BARE_USERNAME_MAX_LEN = 20;
8
9
  function redactUserinfo(scheme, userinfoWithAt) {
@@ -18,16 +19,126 @@ function redactUserinfo(scheme, userinfoWithAt) {
18
19
  const SECRET_TIER_PASSES = egressPasses(egressPassGroups(CREDENTIAL_VOCABULARY), {
19
20
  "url-credentials": (_m, scheme, userinfo) => redactUserinfo(scheme, userinfo),
20
21
  });
21
- export function redactSecrets(s, report) {
22
- return runRedactionPasses(s, SECRET_TIER_PASSES, report);
23
- }
24
22
  const HOST_LEAK_PASSES = [
25
23
  ...SECRET_TIER_PASSES,
26
24
  { kind: "url", confidence: "medium", marker: "[redacted-url]", source: "untrusted-egress", re: URL_OF_ANY_PROTOCOL, replace: "[redacted-url]" },
27
25
  { kind: "fs-path", confidence: "low", marker: "[redacted-path]", source: "untrusted-egress", re: /(?:\/[A-Za-z0-9_.-]+){2,}/g, replace: "[redacted-path]" },
28
26
  ];
27
+ const TIER_PASSES = { "same-principal": SECRET_TIER_PASSES, human: HOST_LEAK_PASSES };
28
+ export function egressText(tier, text, report) {
29
+ return runRedactionPasses(text, TIER_PASSES[tier], report);
30
+ }
31
+ export function egressThrownText(tier, err) {
32
+ return egressText(tier, thrownText(err));
33
+ }
34
+ export function redactSecrets(s, report) {
35
+ return egressText("same-principal", s, report);
36
+ }
29
37
  export function redactHostLeaks(s, report) {
30
- return runRedactionPasses(s, HOST_LEAK_PASSES, report);
38
+ return egressText("human", s, report);
39
+ }
40
+ const VALUE_DEPTH_MAX = 32;
41
+ const VALUE_MEMBERS_MAX = 2_000;
42
+ const WHOLE = "[redacted]";
43
+ export function egressValue(tier, value) {
44
+ const snapshots = new Map();
45
+ let budget = VALUE_MEMBERS_MAX;
46
+ const ownKeys = (o, all) => {
47
+ try {
48
+ return all ? Reflect.ownKeys(o) : Object.keys(o);
49
+ }
50
+ catch {
51
+ return undefined;
52
+ }
53
+ };
54
+ const prototypeOf = (o) => {
55
+ try {
56
+ return Object.getPrototypeOf(o);
57
+ }
58
+ catch {
59
+ return Object.prototype;
60
+ }
61
+ };
62
+ const readOwn = (o, key) => {
63
+ try {
64
+ const d = Reflect.getOwnPropertyDescriptor(o, key);
65
+ if (d === undefined)
66
+ return { value: undefined, enumerable: true };
67
+ return { value: "value" in d ? d.value : d.get?.call(o), enumerable: d.enumerable ?? true };
68
+ }
69
+ catch {
70
+ return { value: undefined, enumerable: true };
71
+ }
72
+ };
73
+ const member = (read, depth, name) => {
74
+ const out = pass(read, depth + 1, name);
75
+ return typeof out === "string" && isSensitiveRecordKey(name) ? WHOLE : out;
76
+ };
77
+ const fill = (o, depth, keys, snapshot) => {
78
+ snapshots.set(o, snapshot);
79
+ for (const key of keys ?? []) {
80
+ if (budget-- <= 0)
81
+ break;
82
+ const read = readOwn(o, key);
83
+ const name = typeof key === "string" ? egressText(tier, key) : key;
84
+ Object.defineProperty(snapshot, name, { value: member(read.value, depth, typeof key === "string" ? key : ""), enumerable: read.enumerable, writable: true, configurable: true });
85
+ }
86
+ return snapshot;
87
+ };
88
+ const errorSnapshot = (o, depth) => {
89
+ const snapshot = new Error();
90
+ for (const key of Reflect.ownKeys(snapshot))
91
+ Reflect.deleteProperty(snapshot, key);
92
+ fill(o, depth, ownKeys(o, true), snapshot);
93
+ for (const key of ["name", "message", "code", "stack"]) {
94
+ if (Object.prototype.hasOwnProperty.call(snapshot, key))
95
+ continue;
96
+ const read = readMember(o, key);
97
+ if (read !== undefined && typeof read !== "function")
98
+ Object.defineProperty(snapshot, key, { value: member(read, depth, key), enumerable: false, writable: true, configurable: true });
99
+ }
100
+ return snapshot;
101
+ };
102
+ function pass(v, depth, key) {
103
+ if (typeof v === "string")
104
+ return egressText(tier, v);
105
+ if (typeof v === "function")
106
+ return undefined;
107
+ if (typeof v !== "object" || v === null)
108
+ return v;
109
+ const known = snapshots.get(v);
110
+ if (known !== undefined)
111
+ return known;
112
+ if (depth >= VALUE_DEPTH_MAX)
113
+ return WHOLE;
114
+ if (isErrorLike(v))
115
+ return errorSnapshot(v, depth);
116
+ const toJSON = readMember(v, "toJSON");
117
+ if (typeof toJSON === "function") {
118
+ let reading;
119
+ try {
120
+ reading = toJSON.call(v, key);
121
+ }
122
+ catch {
123
+ return WHOLE;
124
+ }
125
+ if (reading !== v) {
126
+ const out = pass(reading, depth + 1, key);
127
+ snapshots.set(v, out);
128
+ return out;
129
+ }
130
+ }
131
+ const array = (() => {
132
+ try {
133
+ return Array.isArray(v);
134
+ }
135
+ catch {
136
+ return false;
137
+ }
138
+ })();
139
+ return fill(v, depth, ownKeys(v, false), array ? [] : prototypeOf(v) === null ? Object.create(null) : {});
140
+ }
141
+ return pass(value, 0, "");
31
142
  }
32
143
  const REDACTION_MARKER_RE = /\[redacted(?:-[a-z]+)?\]/g;
33
144
  function countRedactionMarkers(s) {
@@ -85,10 +196,10 @@ function boundedString(value, max, redact) {
85
196
  return out;
86
197
  }
87
198
  export function boundedRedactedSummary(value, max) {
88
- return boundedString(value, max, redactSecrets);
199
+ return boundedString(value, max, (s) => egressText("same-principal", s));
89
200
  }
90
201
  export function untrustedEgressForHuman(value, opts) {
91
- const source = boundedString(value, Number.POSITIVE_INFINITY, redactHostLeaks);
202
+ const source = boundedString(value, Number.POSITIVE_INFINITY, (s) => egressText("human", s));
92
203
  const probe = (budget) => {
93
204
  const bounded = boundedString(source, budget, (s) => s);
94
205
  return { bounded, overflow: [...neutralizeForFence(bounded)].length - opts.max };
@@ -378,6 +378,22 @@ export const ENGINE_ENVELOPES = Object.freeze([
378
378
  fenced: false,
379
379
  disclosed: false,
380
380
  },
381
+ ...[
382
+ "additional_skills_reminder", "application_details", "artifacts", "ask_user_question_tool", "citation_requirements",
383
+ "claude_behavior", "computer_use", "env", "evenhandedness", "examples", "file_creation_advice", "file_handling_rules",
384
+ "good_file_sharing_examples", "high_level_computer_use_explanation", "knowledge_cutoff", "legal_and_financial_advice",
385
+ "lists_and_bullets", "notes_on_user_uploaded_files", "package_management", "producing_outputs", "product_information",
386
+ "refusal_handling", "responding_to_mistakes_and_criticism", "sharing_files", "strong", "suggesting_claude_actions",
387
+ "todo_list_tool", "tone_and_formatting", "tool_result_safety", "unnecessary_computer_use_avoidance", "user_wellbeing",
388
+ "verification_step", "web_content_restrictions", "working_with_user_files",
389
+ ].map((tag) => ({
390
+ tag,
391
+ kind: "not-an-envelope",
392
+ mint: "prompts/cowork.ts — the Cowork base template's own section markup",
393
+ guard: "no run-supplied bytes reach it: the template is a constant rendered section by section, and its only fills are the two directory placeholders, engine facts",
394
+ fenced: false,
395
+ disclosed: false,
396
+ })),
381
397
  ]);
382
398
  const ENVELOPES_BY_TAG = new Map(ENGINE_ENVELOPES.map((e) => [e.tag, e]));
383
399
  export function engineEnvelope(tag) {
@@ -646,16 +646,30 @@ export declare function deriveAskEffective(form: AskSeamForm, parkEffective: boo
646
646
  */
647
647
  export declare function askSeatEffective(manifest: Pick<WiringManifest, "ask" | "parkLane">): AskEffective;
648
648
  /**
649
- * Does an ask this leg hands on carrying `requiresRealApproval` reach a PERSON — never an automatic yes? The leg's
650
- * APPROVAL SEAT, read off the ONE ask derivation above, never re-derived: `human_reachable` (a live approver) and
651
- * `park_only` (the durable park; where the park cannot take the ask, a blanket seat behind it refuses the bit) are a
652
- * person; `auto_allow`, `auto_deny` and `unresolved` are nobody (the last the fail-closed reading). The auto-mode
653
- * classifier stations read it to decide where a classification that could not rule goes: to that person (the ask
654
- * carrying the bit), or refused.
649
+ * WHERE an ask carrying `requiresRealApproval` goes from a station — the closed set of the ONE approval-seat decision:
650
+ * `live_approver` (a live approver callback answers it now), `durable_park` (it parks for a person to answer later),
651
+ * `none` (nobody can: a blanket policy seat, no seat, or a run that declared nobody can be asked — each only when no park
652
+ * lane is armed, since an armed park is the deployment's own statement that an answer can wait for a person — or a word that cannot be
653
+ * resolved — the fail-closed reading).
655
654
  */
656
- export declare function askReachesPerson(ask: {
655
+ export declare const APPROVAL_ROUTES: readonly ["live_approver", "durable_park", "none"];
656
+ export type ApprovalRoute = (typeof APPROVAL_ROUTES)[number];
657
+ /**
658
+ * The APPROVAL SEAT as a route, read off the ONE ask derivation above, never re-derived: `human_reachable` ⇒
659
+ * `live_approver`; `park_only` ⇒ `durable_park` (where the park cannot take the ask, a blanket seat behind it refuses the
660
+ * bit); `auto_allow`, `auto_deny` and `unresolved` ⇒ `none`. Every station that forks on "does a handed-on ask reach a
661
+ * person" reads this word — the leg's gate from the leg's seat word ({@link askSeatEffective}), an ancestor's frozen seat
662
+ * through {@link frozenApproverRoute} — and the fork, its observer and the sentence a person reads all read the same word.
663
+ */
664
+ export declare function approvalRouteOf(ask: {
657
665
  readonly effective?: AskEffective;
658
- }): boolean;
666
+ }): ApprovalRoute;
667
+ /**
668
+ * The route from an ancestor's FROZEN approver (the inherited fold's station): the same table read with that seat's own
669
+ * form and no park — the inherited station resolves at the frozen seat, whose only road to a person is a live callback
670
+ * (the leg's park is reached only through a live approver's `unavailable`), so a blanket or absent frozen seat is `none`.
671
+ */
672
+ export declare function frozenApproverRoute(onAsk: OnAsk | undefined): ApprovalRoute;
659
673
  export declare function deriveWiringManifest(facts: WiringFacts): WiringManifest;
660
674
  /** A run's approval seat as its one resolution froze it: the value every reader that needs the approver itself reads
661
675
  * (`undefined` when nothing on this run can be asked), the form the manifest reports and every reader of the word derives
@@ -81,16 +81,18 @@ export function deriveAskEffective(form, parkEffective, mode) {
81
81
  export function askSeatEffective(manifest) {
82
82
  return deriveAskEffective(manifest.ask.form, manifest.parkLane.effective);
83
83
  }
84
- export function askReachesPerson(ask) {
84
+ export const APPROVAL_ROUTES = Object.freeze(["live_approver", "durable_park", "none"]);
85
+ export function approvalRouteOf(ask) {
85
86
  const effective = ask.effective ?? "unresolved";
86
87
  switch (effective) {
87
88
  case "human_reachable":
89
+ return "live_approver";
88
90
  case "park_only":
89
- return true;
91
+ return "durable_park";
90
92
  case "auto_allow":
91
93
  case "auto_deny":
92
94
  case "unresolved":
93
- return false;
95
+ return "none";
94
96
  default: {
95
97
  const _exhaustive = effective;
96
98
  void _exhaustive;
@@ -98,6 +100,12 @@ export function askReachesPerson(ask) {
98
100
  }
99
101
  }
100
102
  }
103
+ export function frozenApproverRoute(onAsk) {
104
+ return approvalRouteOf({ effective: deriveAskEffective(askSeamFormOf(onAsk) ?? "absent", false) });
105
+ }
106
+ function askSeamFormOf(v) {
107
+ return typeof v === "function" ? "callback" : v === "allow" ? "allow" : v === "deny" ? "deny" : v === undefined ? "absent" : undefined;
108
+ }
101
109
  function deriveParkLane(facts) {
102
110
  if (!facts.parkCapable) {
103
111
  return { capable: false, effective: false, reasons: ["no_checkpoint_store"] };
@@ -235,7 +243,7 @@ export function resolveAskSeat(spec, deps) {
235
243
  if (seat === undefined)
236
244
  return Object.freeze(reach === undefined ? { value: undefined, form: "absent" } : { value: undefined, form: "declared_none", reach });
237
245
  const v = seat.value;
238
- const form = typeof v === "function" ? "callback" : v === "allow" ? "allow" : v === "deny" ? "deny" : undefined;
246
+ const form = askSeamFormOf(v);
239
247
  if (form === undefined) {
240
248
  const e = new Error(`the resolved onAsk seat (${seat.provenance}) holds ${JSON.stringify(v)} — not an OnAsk ` +
241
249
  `("allow" | "deny" | approver function); refusing to classify it.`);
@@ -310,6 +310,7 @@ export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate
310
310
  private abortResultDetails?;
311
311
  private loopTrace?;
312
312
  private onToolInputValidationFault?;
313
+ private onToolMediaRefused?;
313
314
  private resilience?;
314
315
  private maxToolConcurrency?;
315
316
  private streamingToolExecution?;
@@ -538,7 +539,7 @@ export declare class AgentHarness<TSkill extends Skill = Skill, TPromptTemplate
538
539
  /**
539
540
  * Finalize a call this harness's caller executed OUTSIDE a turn (a resumed, already-approved call) exactly as the
540
541
  * loop finalizes its own: the same {@link finalizeToolOutcome} over the same `tool_result` emit, so the call meets the
541
- * same handlers, the same patch application and the same throwing-hook posture as a live one.
542
+ * same handlers, the same patch application, the same throwing-hook posture and the same media admission as a live one.
542
543
  */
543
544
  finalizeToolCall(toolCall: AgentToolCall, args: unknown, executed: ExecutedToolCallOutcome): Promise<ExecutedToolCallOutcome & {
544
545
  toolCall: AgentToolCall;
@@ -294,6 +294,7 @@ export class AgentHarness {
294
294
  abortResultDetails;
295
295
  loopTrace;
296
296
  onToolInputValidationFault;
297
+ onToolMediaRefused;
297
298
  resilience;
298
299
  maxToolConcurrency;
299
300
  streamingToolExecution;
@@ -329,6 +330,7 @@ export class AgentHarness {
329
330
  this.abortResultDetails = options.abortResultDetails;
330
331
  this.loopTrace = options.loopTrace;
331
332
  this.onToolInputValidationFault = options.onToolInputValidationFault;
333
+ this.onToolMediaRefused = options.onToolMediaRefused;
332
334
  this.resilience = options.resilience;
333
335
  this.maxToolConcurrency = options.maxToolConcurrency;
334
336
  this.streamingToolExecution = options.streamingToolExecution;
@@ -640,6 +642,7 @@ export class AgentHarness {
640
642
  ...(this.stallTimeoutsPerCall !== undefined ? { stallTimeoutsPerCall: this.stallTimeoutsPerCall } : {}),
641
643
  ...(this.abortResultDetails !== undefined ? { abortResultDetails: this.abortResultDetails } : {}),
642
644
  ...(this.onToolInputValidationFault !== undefined ? { onToolInputValidationFault: this.onToolInputValidationFault } : {}),
645
+ ...(this.onToolMediaRefused !== undefined ? { onToolMediaRefused: this.onToolMediaRefused } : {}),
643
646
  ...(this.resilience !== undefined ? { resilience: this.resilience } : {}),
644
647
  ...(this.maxToolConcurrency !== undefined ? { maxToolConcurrency: this.maxToolConcurrency } : {}),
645
648
  ...(this.streamingToolExecution === true && (this.getHandlers("tool_call")?.size ?? 0) === 0
@@ -1231,7 +1234,7 @@ export class AgentHarness {
1231
1234
  return patch;
1232
1235
  }
1233
1236
  finalizeToolCall(toolCall, args, executed) {
1234
- return finalizeToolOutcome(toolCall, executed, (result, isError) => this.afterToolCallHook(toolCall, args, result, isError));
1237
+ return finalizeToolOutcome(toolCall, executed, (result, isError) => this.afterToolCallHook(toolCall, args, result, isError), this.onToolMediaRefused);
1235
1238
  }
1236
1239
  getModel() {
1237
1240
  return this.model;
@@ -1444,6 +1444,9 @@ export interface AgentHarnessOptions<TSkill extends Skill = Skill, TPromptTempla
1444
1444
  /** Loop-config pass-through — see `AgentLoopConfig.onToolInputValidationFault` (a throwing
1445
1445
  * `AgentTool.validateInput` is read as no verdict and disclosed here). */
1446
1446
  onToolInputValidationFault?: import("../loop/types.js").AgentLoopConfig["onToolInputValidationFault"];
1447
+ /** Loop-config pass-through — see `AgentLoopConfig.onToolMediaRefused` (the tool-result seat's account of the media it
1448
+ * refused); the harness's own finalization of a resumed approved call reports through the same sink. */
1449
+ onToolMediaRefused?: import("../loop/types.js").AgentLoopConfig["onToolMediaRefused"];
1447
1450
  /**
1448
1451
  * design/131 (拍): per-task resilience intent flags, forwarded to every provider
1449
1452
  * call (`StreamOptions.resilience`) so the deployment's decorator stack (degrading / failover /
@@ -1020,21 +1020,19 @@ export interface RouteCredential {
1020
1020
  * knob+header pair counts as ONE deployment credential — see `resolveRouteCredential`). */
1021
1021
  carrierBag?: RouteCarrierBag;
1022
1022
  }
1023
- /** The deployment half of the pairing judgment: the brain construction config's DECLARED root.
1024
- * Absent/empty ⇒ the deployment did not declare its credential's URL half (the unpinned
1025
- * quick-start posture — see `adjudicateModelRoute`). */
1023
+ /** The deployment half of the pairing judgment: the root the brain sends to when an entry names none —
1024
+ * its construction config's DECLARED root (`baseUrl`), else the lane's own default endpoint
1025
+ * (`laneDefault`, a constant the brain owns; absent for a lane with no default). That root is the
1026
+ * deployment credential's one root (see `adjudicateModelRoute`). */
1026
1027
  export interface RoutePairingConfig {
1027
1028
  baseUrl?: string;
1029
+ laneDefault?: string;
1028
1030
  }
1029
1031
  /** How an OK verdict is paired.
1030
1032
  * - `"per-model"` — per-model credential, paired by construction.
1031
- * - `"paired"` — deployment credential on the deployment's own declared root.
1032
- * - `"unpinned"` — deployment credential whose URL half is UNDECLARED (no `config.baseUrl`): allowed
1033
- * for compatibility with single-endpoint configs (key on config, URL on the model), but the pairing
1034
- * is unverifiable — a multi-entry deployment in this posture should declare `config.baseUrl` or move
1035
- * to per-model credentials. Surfaced (not refused) so read faces can annotate it.
1033
+ * - `"paired"` — deployment credential on the deployment's own root (declared, or the lane default).
1036
1034
  * - `"keyless"` — no credential anywhere; nothing to protect. */
1037
- export type RoutePairingPosture = "per-model" | "paired" | "unpinned" | "keyless";
1035
+ export type RoutePairingPosture = "per-model" | "paired" | "keyless";
1038
1036
  /** Machine codes an adjudication refusal carries (minted here, forwarded verbatim by consumers). */
1039
1037
  export type RouteRefusalCode = "route.credential_mismatch" | "route.credential_missing";
1040
1038
  /** The structured account of a refusal — the two halves and the fix, every URL a DISPLAY spelling. */
@@ -183,10 +183,15 @@ export declare const TOOL_INPUT_VALIDATION_TIMEOUT_MS = 10000;
183
183
  * The post-execution half EVERY executed tool call passes through — the loop's own calls here, and a call
184
184
  * the harness executed outside a turn (a resumed approved call, `AgentHarness.finalizeToolCall`) through the
185
185
  * same function, so the two cannot drift into two dialects of one finalization: the `afterToolCall` patch
186
- * (content / details / terminate / isError), the D4 posture on a throwing hook, and the delivery mark.
187
- * `after` absent ⇒ the executed outcome stands as is (same object).
186
+ * (content / details / terminate / isError), the D4 posture on a throwing hook, the delivery mark, and last
187
+ * the media admission — the tool-result seat every supply of media reaches after the last hand that can change
188
+ * it (`admitToolResultMedia`): a refused block becomes its text placeholder in the result, and `onMediaRefused`
189
+ * hears the call's refusals once. `after` absent and no media ⇒ the executed outcome stands as is (same object).
188
190
  */
189
- export declare function finalizeToolOutcome<C>(toolCall: C, executed: ExecutedToolCallOutcome, after: ((result: AgentToolResult<unknown>, isError: boolean) => Promise<AfterToolCallResult | undefined>) | undefined): Promise<ExecutedToolCallOutcome & {
191
+ export declare function finalizeToolOutcome<C extends {
192
+ id: string;
193
+ name: string;
194
+ }>(toolCall: C, executed: ExecutedToolCallOutcome, after: ((result: AgentToolResult<unknown>, isError: boolean) => Promise<AfterToolCallResult | undefined>) | undefined, onMediaRefused?: AgentLoopConfig["onToolMediaRefused"]): Promise<ExecutedToolCallOutcome & {
190
195
  toolCall: C;
191
196
  }>;
192
197
  export {};
@@ -3,7 +3,9 @@ import { truncateError } from "../../core/tool-errors.js";
3
3
  import { judgeToolNameMiss, mountedToolNames } from "../../core/tool-name-aliases.js";
4
4
  import { INTERRUPTED_BY_USER_FOR_TOOL_USE_MARKER, INTERRUPTED_BY_USER_MARKER, } from "../../core/session-reconcile.js";
5
5
  import { resolveAgentCoreStreamFn } from "./runtime-deps.js";
6
- import { isErrorLike, readMember, thrownMarksOf, thrownText } from "../../core/thrown-value.js";
6
+ import { isErrorLike, readMember, thrownMarksOf } from "../../core/thrown-value.js";
7
+ import { egressThrownText, egressValue } from "../../core/untrusted-egress.js";
8
+ import { admitToolResultMedia } from "../../tools/fs/image-admission.js";
7
9
  function appendTextDeltaToAssistantMessage(message, contentIndex, delta) {
8
10
  const content = [...message.content];
9
11
  const currentContent = content[contentIndex];
@@ -1111,7 +1113,7 @@ class StreamToolExecutor {
1111
1113
  this.retainCancellation(error);
1112
1114
  entry.immediate = {
1113
1115
  kind: "immediate",
1114
- result: createErrorToolResult(thrownText(error), error),
1116
+ result: thrownErrorToolResult(error),
1115
1117
  isError: true,
1116
1118
  };
1117
1119
  }
@@ -1374,7 +1376,7 @@ async function prepareToolCall(currentContext, assistantMessage, toolCall, confi
1374
1376
  catch (error) {
1375
1377
  return {
1376
1378
  kind: "immediate",
1377
- result: createErrorToolResult(thrownText(error), error),
1379
+ result: thrownErrorToolResult(error),
1378
1380
  isError: true,
1379
1381
  };
1380
1382
  }
@@ -1415,7 +1417,7 @@ async function executePreparedToolCall(prepared, signal, emit, abortResultDetail
1415
1417
  }
1416
1418
  catch (error) {
1417
1419
  outcome = {
1418
- result: createErrorToolResult(thrownText(error), error),
1420
+ result: thrownErrorToolResult(error),
1419
1421
  isError: true,
1420
1422
  };
1421
1423
  }
@@ -1425,7 +1427,7 @@ async function executePreparedToolCall(prepared, signal, emit, abortResultDetail
1425
1427
  return outcome;
1426
1428
  const reason = deliveryFailure.reason;
1427
1429
  const details = annotateDeliveryFailure(outcome.result, {
1428
- message: thrownText(reason),
1430
+ message: egressThrownText("same-principal", reason),
1429
1431
  });
1430
1432
  return details === UNANNOTATED ? outcome : { result: { ...outcome.result, details }, isError: outcome.isError };
1431
1433
  })();
@@ -1497,7 +1499,7 @@ async function executePreparedWithDisclosure(currentContext, assistantMessage, p
1497
1499
  if (isAbortSemanticsError(error))
1498
1500
  throw error;
1499
1501
  executed = {
1500
- result: createErrorToolResult(`[tool execution harness failed: ${thrownText(error)}]`, error),
1502
+ result: thrownErrorToolResult(error, (words) => `[tool execution harness failed: ${words}]`),
1501
1503
  isError: true,
1502
1504
  };
1503
1505
  }
@@ -1506,9 +1508,9 @@ async function executePreparedWithDisclosure(currentContext, assistantMessage, p
1506
1508
  function finalizeExecutedToolCall(currentContext, assistantMessage, prepared, executed, config, signal) {
1507
1509
  return finalizeToolOutcome(prepared.toolCall, executed, config.afterToolCall === undefined
1508
1510
  ? undefined
1509
- : (result, isError) => config.afterToolCall({ assistantMessage, toolCall: prepared.toolCall, args: prepared.args, result, isError, context: currentContext }, signal));
1511
+ : (result, isError) => config.afterToolCall({ assistantMessage, toolCall: prepared.toolCall, args: prepared.args, result, isError, context: currentContext }, signal), config.onToolMediaRefused);
1510
1512
  }
1511
- export async function finalizeToolOutcome(toolCall, executed, after) {
1513
+ export async function finalizeToolOutcome(toolCall, executed, after, onMediaRefused) {
1512
1514
  let result = executed.result;
1513
1515
  let isError = executed.isError;
1514
1516
  const deliveryMark = readDeliveryFailureMark(executed.result.details);
@@ -1525,7 +1527,7 @@ export async function finalizeToolOutcome(toolCall, executed, after) {
1525
1527
  }
1526
1528
  }
1527
1529
  catch (error) {
1528
- const note = truncateError(`[post-tool processing failed (the tool already executed): ${thrownText(error)}]`);
1530
+ const note = truncateError(`[post-tool processing failed (the tool already executed): ${egressThrownText("same-principal", error)}]`);
1529
1531
  result = { ...result, content: [...result.content, { type: "text", text: note }] };
1530
1532
  }
1531
1533
  if (deliveryMark !== undefined && readDeliveryFailureMark(result.details) === undefined) {
@@ -1534,6 +1536,18 @@ export async function finalizeToolOutcome(toolCall, executed, after) {
1534
1536
  result = { ...result, details };
1535
1537
  }
1536
1538
  }
1539
+ const admitting = admitToolResultMedia(result.content, toolCall.name);
1540
+ if (admitting !== undefined) {
1541
+ const admitted = await admitting;
1542
+ result = { ...result, content: admitted.content };
1543
+ if (admitted.refused.length > 0) {
1544
+ try {
1545
+ onMediaRefused?.({ toolName: toolCall.name, toolCallId: toolCall.id, refused: admitted.refused });
1546
+ }
1547
+ catch {
1548
+ }
1549
+ }
1550
+ }
1537
1551
  return { toolCall, result, isError };
1538
1552
  }
1539
1553
  function safeAbortDetails(fn) {
@@ -1563,6 +1577,9 @@ function createErrorToolResult(message, source) {
1563
1577
  details,
1564
1578
  };
1565
1579
  }
1580
+ function thrownErrorToolResult(error, frame = (words) => words) {
1581
+ return createErrorToolResult(frame(egressThrownText("same-principal", error)), { details: egressValue("same-principal", thrownMarksOf(error)) });
1582
+ }
1566
1583
  async function emitToolExecutionEnd(finalized, emit) {
1567
1584
  await emit({
1568
1585
  type: "tool_execution_end",
@@ -566,6 +566,16 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
566
566
  toolCallId: string;
567
567
  error: unknown;
568
568
  }) => void;
569
+ /**
570
+ * Told, once per executed call, of the media blocks the tool-result seat refused (`admitToolResultMedia`, the last step
571
+ * of the post-execution half): each already stands in the result as a text placeholder naming the tool and the refusal,
572
+ * so this is the run's account of it, not a decision. Advisory: a throwing sink changes nothing.
573
+ */
574
+ onToolMediaRefused?: (info: {
575
+ toolName: string;
576
+ toolCallId: string;
577
+ refused: readonly import("../../tools/fs/image-admission.js").ToolResultMediaRefusal[];
578
+ }) => void;
569
579
  /**
570
580
  * Called after a tool finishes executing, before `tool_execution_end` and tool-result message events are emitted.
571
581
  *