@sema-agent/core 7.49.5 → 7.50.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 (193) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/dist/agents/observer.js +3 -10
  3. package/dist/agents/subagent-steps.d.ts +6 -14
  4. package/dist/agents/subagent-steps.js +3 -17
  5. package/dist/brain/stream-shared.d.ts +1 -1
  6. package/dist/core/allow-entry.d.ts +1 -1
  7. package/dist/core/allow-entry.js +1 -1
  8. package/dist/core/ask-origin.d.ts +15 -11
  9. package/dist/core/ask-origin.js +6 -14
  10. package/dist/core/auto-mode-prompt.js +6 -2
  11. package/dist/core/checkpoint-store.d.ts +6 -4
  12. package/dist/core/checkpoint-store.js +1 -1
  13. package/dist/core/context-edit.d.ts +2 -2
  14. package/dist/core/decision-receipt.d.ts +2 -2
  15. package/dist/core/decision-receipt.js +30 -11
  16. package/dist/core/delegation-frames.d.ts +66 -18
  17. package/dist/core/delegation-frames.js +22 -0
  18. package/dist/core/engine-notice.d.ts +7 -5
  19. package/dist/core/frame-settlement.d.ts +6 -1
  20. package/dist/core/gate-fold.js +19 -13
  21. package/dist/core/gate-lanes.js +12 -11
  22. package/dist/core/gate-outcome.d.ts +2 -2
  23. package/dist/core/governance-codes.js +2 -0
  24. package/dist/core/hooks.d.ts +28 -20
  25. package/dist/core/human-input-projection.js +2 -0
  26. package/dist/core/lsp-diagnostics.d.ts +3 -0
  27. package/dist/core/lsp-diagnostics.js +6 -0
  28. package/dist/core/lsp-session.d.ts +39 -17
  29. package/dist/core/lsp-session.js +118 -58
  30. package/dist/core/lsp.d.ts +83 -3
  31. package/dist/core/lsp.js +146 -16
  32. package/dist/core/mcp.js +2 -1
  33. package/dist/core/media-byte-cap.d.ts +14 -0
  34. package/dist/core/media-byte-cap.js +15 -3
  35. package/dist/core/memory-engine/consolidation-driver.d.ts +23 -1
  36. package/dist/core/memory-engine/consolidation-driver.js +54 -6
  37. package/dist/core/memory-engine/consolidation-write-gate.d.ts +46 -0
  38. package/dist/core/memory-engine/consolidation-write-gate.js +82 -0
  39. package/dist/core/memory-engine/consolidation.d.ts +17 -0
  40. package/dist/core/memory-engine/consolidation.js +1 -0
  41. package/dist/core/memory-engine/distiller.d.ts +8 -2
  42. package/dist/core/memory-engine/distiller.js +8 -6
  43. package/dist/core/memory-engine/engine.d.ts +37 -4
  44. package/dist/core/memory-engine/engine.js +127 -38
  45. package/dist/core/memory-engine/file-backend.d.ts +8 -3
  46. package/dist/core/memory-engine/file-backend.js +44 -16
  47. package/dist/core/memory-engine/index.d.ts +2 -2
  48. package/dist/core/memory-engine/index.js +1 -1
  49. package/dist/core/memory-engine/landing-admission.d.ts +65 -0
  50. package/dist/core/memory-engine/landing-admission.js +68 -0
  51. package/dist/core/memory-engine/types.d.ts +23 -0
  52. package/dist/core/mode-policy.d.ts +3 -2
  53. package/dist/core/mode-policy.js +2 -0
  54. package/dist/core/permission-rule-model.d.ts +19 -13
  55. package/dist/core/permission-rule-model.js +7 -4
  56. package/dist/core/permission-rule-provider.d.ts +11 -0
  57. package/dist/core/permission-rules.d.ts +1 -1
  58. package/dist/core/permission-rules.js +5 -2
  59. package/dist/core/persisted-rule-arms.d.ts +15 -7
  60. package/dist/core/persisted-rule-arms.js +13 -6
  61. package/dist/core/reminder-mint.d.ts +5 -0
  62. package/dist/core/reminder-mint.js +1 -0
  63. package/dist/core/retired-keys.d.ts +8 -0
  64. package/dist/core/retired-keys.js +4 -0
  65. package/dist/core/rule-lane-reads.d.ts +4 -4
  66. package/dist/core/rule-lane-reads.js +10 -1
  67. package/dist/core/runner/construction-doors.d.ts +10 -4
  68. package/dist/core/runner/construction-doors.js +25 -2
  69. package/dist/core/runner/contracts.d.ts +20 -3
  70. package/dist/core/runner/env-capability-census.js +1 -1
  71. package/dist/core/runner/gate-exit.js +2 -1
  72. package/dist/core/runner/memory-consolidation-driver.d.ts +6 -4
  73. package/dist/core/runner/memory-consolidation-driver.js +5 -2
  74. package/dist/core/runner/memory-consolidation-write-judge.d.ts +17 -0
  75. package/dist/core/runner/memory-consolidation-write-judge.js +14 -0
  76. package/dist/core/runner/permission-rule-lanes.d.ts +16 -22
  77. package/dist/core/runner/permission-rule-lanes.js +13 -43
  78. package/dist/core/runner/person-path-rows.d.ts +244 -0
  79. package/dist/core/runner/person-path-rows.js +387 -0
  80. package/dist/core/runner/prepare-acquire-reconcile.js +8 -1
  81. package/dist/core/runner/prepare-artifact.d.ts +5 -3
  82. package/dist/core/runner/prepare-artifact.js +2 -8
  83. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
  84. package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
  85. package/dist/core/runner/prepare-context-lane.js +2 -2
  86. package/dist/core/runner/prepare-gate-stations.d.ts +3 -3
  87. package/dist/core/runner/prepare-gate-stations.js +1 -1
  88. package/dist/core/runner/prepare-hands-readface.d.ts +19 -10
  89. package/dist/core/runner/prepare-hands-readface.js +57 -43
  90. package/dist/core/runner/prepare-lsp.d.ts +15 -13
  91. package/dist/core/runner/prepare-lsp.js +11 -11
  92. package/dist/core/runner/prepare-policy-chain.d.ts +8 -4
  93. package/dist/core/runner/prepare-policy-chain.js +6 -7
  94. package/dist/core/runner/prepare-run-refs.js +1 -1
  95. package/dist/core/runner/prepare-task.js +24 -16
  96. package/dist/core/runner/prepare-turn-wiring.d.ts +13 -6
  97. package/dist/core/runner/prepare-turn-wiring.js +19 -8
  98. package/dist/core/runner/read-root-grant.d.ts +1 -25
  99. package/dist/core/runner/read-root-grant.js +1 -17
  100. package/dist/core/runner/retired-runner-deps.d.ts +2 -2
  101. package/dist/core/runner/run-brain-sinks.d.ts +1 -5
  102. package/dist/core/runner/run-brain-sinks.js +2 -4
  103. package/dist/core/runner/run-harness-handlers.js +5 -4
  104. package/dist/core/runner/run-identity-wiring.d.ts +4 -4
  105. package/dist/core/runner/run-identity-wiring.js +10 -1
  106. package/dist/core/runner/run-leg.d.ts +1 -1
  107. package/dist/core/runner/run-leg.js +22 -9
  108. package/dist/core/runner/run-settle-and-teardown.d.ts +0 -4
  109. package/dist/core/runner/run-settle-and-teardown.js +7 -4
  110. package/dist/core/runner/run-terminal-adoption.d.ts +1 -1
  111. package/dist/core/runner/run-terminal-adoption.js +1 -1
  112. package/dist/core/runner/run-turn-boundary.js +2 -2
  113. package/dist/core/runner/runtask.js +8 -8
  114. package/dist/core/runner/session-file-state-replay.d.ts +14 -4
  115. package/dist/core/runner/session-file-state-replay.js +8 -0
  116. package/dist/core/runner/stream-steer-verb.js +3 -0
  117. package/dist/core/runner/tool-end-body.d.ts +5 -4
  118. package/dist/core/runner/tool-end-body.js +4 -4
  119. package/dist/core/runner/tool-output-projection.d.ts +6 -4
  120. package/dist/core/runner/tool-output-projection.js +66 -47
  121. package/dist/core/runner-deps.d.ts +6 -3
  122. package/dist/core/shared-memory/types.d.ts +2 -2
  123. package/dist/core/shell-read-silence.d.ts +23 -0
  124. package/dist/core/shell-read-silence.js +18 -0
  125. package/dist/core/synthetic-read-verdict.d.ts +80 -21
  126. package/dist/core/synthetic-read-verdict.js +89 -18
  127. package/dist/core/task-event.d.ts +113 -25
  128. package/dist/core/task-spec.d.ts +44 -0
  129. package/dist/core/task-tool-shape.d.ts +1 -1
  130. package/dist/core/tool-catalog-entries.js +16 -2
  131. package/dist/core/tool-policy.d.ts +25 -15
  132. package/dist/core/tool-policy.js +9 -13
  133. package/dist/core/trace.d.ts +3 -2
  134. package/dist/core/types.d.ts +3 -3
  135. package/dist/core/types.js +1 -1
  136. package/dist/core/untrusted-text.d.ts +2 -2
  137. package/dist/core/user-attachment.d.ts +164 -0
  138. package/dist/core/user-attachment.js +437 -0
  139. package/dist/engine/compaction/compaction.js +10 -3
  140. package/dist/engine/compaction/utils.js +6 -0
  141. package/dist/engine/execution-env/node-execution-env.d.ts +19 -1
  142. package/dist/engine/execution-env/node-execution-env.js +125 -0
  143. package/dist/engine/harness/agent-harness.d.ts +12 -2
  144. package/dist/engine/harness/agent-harness.js +15 -3
  145. package/dist/engine/harness/types.d.ts +21 -0
  146. package/dist/engine/llm/content-block-render.d.ts +15 -0
  147. package/dist/engine/llm/content-block-render.js +10 -0
  148. package/dist/engine/llm/index.d.ts +1 -0
  149. package/dist/engine/llm/index.js +1 -0
  150. package/dist/engine/llm/types.d.ts +80 -4
  151. package/dist/engine/llm/types.js +43 -1
  152. package/dist/engine/session/import-validate.js +2 -0
  153. package/dist/index.d.ts +6 -4
  154. package/dist/index.js +2 -1
  155. package/dist/internal/harness-types.d.ts +1 -1
  156. package/dist/internal/llm.d.ts +2 -2
  157. package/dist/internal/llm.js +1 -1
  158. package/dist/orchestration/workflow.d.ts +4 -5
  159. package/dist/orchestration/workflow.js +2 -2
  160. package/dist/prompts/default.d.ts +1 -1
  161. package/dist/tools/artifact/artifact-tool.js +1 -1
  162. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
  163. package/dist/tools/fs/dir-entries.d.ts +4 -10
  164. package/dist/tools/fs/dir-entries.js +4 -11
  165. package/dist/tools/fs/file-read-check.js +3 -3
  166. package/dist/tools/fs/fs-pdf.js +2 -4
  167. package/dist/tools/fs/fs-read.d.ts +47 -4
  168. package/dist/tools/fs/fs-read.js +63 -19
  169. package/dist/tools/fs/fs-search-tools.d.ts +3 -3
  170. package/dist/tools/fs/fs-search-tools.js +7 -2
  171. package/dist/tools/fs/fs-write.d.ts +1 -1
  172. package/dist/tools/fs/fs-write.js +7 -6
  173. package/dist/tools/fs/index.d.ts +7 -1
  174. package/dist/tools/fs/index.js +56 -34
  175. package/dist/tools/fs/read-deny-home.d.ts +19 -1
  176. package/dist/tools/fs/read-deny-home.js +32 -22
  177. package/dist/tools/fs/read-deny.d.ts +14 -0
  178. package/dist/tools/fs/read-deny.js +8 -0
  179. package/dist/tools/fs/repo-map.d.ts +3 -2
  180. package/dist/tools/fs/repo-map.js +11 -3
  181. package/dist/tools/fs/safety.d.ts +41 -2
  182. package/dist/tools/fs/safety.js +12 -3
  183. package/dist/tools/fs/shell-read-check.d.ts +12 -3
  184. package/dist/tools/fs/shell-read-check.js +28 -15
  185. package/dist/tools/fs/shell-read-device.d.ts +1 -1
  186. package/dist/tools/fs/shell-read-judge.d.ts +9 -4
  187. package/dist/tools/fs/shell-read-judge.js +1 -1
  188. package/dist/tools/loop-tick.d.ts +2 -2
  189. package/package.json +3 -1
  190. package/test/export-surface.snapshot.json +39 -1
  191. package/wire-changes/core@7.50.0.json +1699 -0
  192. package/dist/core/runner/persisted-path-rows.d.ts +0 -88
  193. package/dist/core/runner/persisted-path-rows.js +0 -108
@@ -31,6 +31,39 @@ export type ImageInput = {
31
31
  /** URL the runner will fetch and inline as base64 before sending to the model. */
32
32
  url: string;
33
33
  };
34
+ /**
35
+ * One file the user attached to the objective, by WHO HOLDS ITS BYTES (never by what kind of file it is — the engine
36
+ * finds that out and reports it):
37
+ * - `{ path, lines? }` — a file or directory the engine can reach in its workspace: the engine reads it through the
38
+ * Read tool's own read and admission (read-deny table, roots, the run's deny rules), as if the model had read it,
39
+ * minus the questions — the user's declaration is the answer to an ordinary ask; a read some layer would put to a
40
+ * person other than the asker (an organization rule, a deployment-authored or real-approval ask) is not attached.
41
+ * `lines` reads a line range (1-based, `end` inclusive).
42
+ * - `{ name, text, startLine? }` — text the host holds (a local file a remote engine cannot reach, an unsaved buffer):
43
+ * delivered as given, line-numbered from `startLine` (default 1). It opens nothing an objective could not carry,
44
+ * and records no read state.
45
+ * - `{ name, image: { data, mimeType } }` — an image file the host holds (base64), admitted like an image the Read tool
46
+ * serves.
47
+ * Each attachment reaches the model as its own message right after the objective, marked as the engine's: two
48
+ * marked sentences around the data, the name or path only in the data. See `USER_ATTACHMENT_SEAT` for the limits.
49
+ */
50
+ export type UserAttachment = {
51
+ readonly path: string;
52
+ readonly lines?: {
53
+ readonly start: number;
54
+ readonly end?: number;
55
+ };
56
+ } | {
57
+ readonly name: string;
58
+ readonly text: string;
59
+ readonly startLine?: number;
60
+ } | {
61
+ readonly name: string;
62
+ readonly image: {
63
+ readonly data: string;
64
+ readonly mimeType: string;
65
+ };
66
+ };
34
67
  /** A single self-contained task with its full configuration. */
35
68
  /** The shell-gate FLOOR vocabulary — {@link TaskSpec.shellGate}'s value set, the word the engine persists on a
36
69
  * shell-gated ask (`RiskDescriptor.shellGateDoctrine`) and carries down a delegation chain. One value: `"always"`
@@ -133,6 +166,17 @@ export interface TaskSpec {
133
166
  interactiveQuestionFallback?: boolean;
134
167
  /** Optional images attached to this turn's user message (model must support vision). */
135
168
  images?: ImageInput[];
169
+ /**
170
+ * Files the user attached to this objective ({@link UserAttachment}): at most 20, delivered after the
171
+ * UserPromptSubmit hook lets the objective through (a hook that blocks it reads nothing), each as its own
172
+ * engine-minted message right after the objective. A path entry needs a workspace (hands on). A malformed entry,
173
+ * a text entry over the Read limit (100 000 rendered characters), an image the API would not accept, a path entry
174
+ * on a run without a workspace, or any entry on a durable resume leg is refused at the door:
175
+ * `config.user_attachment_invalid`, the message opening with the field path (`TaskSpec.userAttachments[2].text: …`).
176
+ * A file the engine may not read is not refused: its message says it was not attached and why, and the
177
+ * `human_input` event's `attachments[]` reports each outcome.
178
+ */
179
+ userAttachments?: readonly UserAttachment[];
136
180
  /** Reuse an existing in-memory session for chat continuity; omit to start fresh. */
137
181
  sessionId?: string;
138
182
  /**
@@ -44,7 +44,7 @@ export interface TaskToolCapabilities {
44
44
  /** TaskStop accepts a background agent's spawn NAME as task_id (registry name resolution only). */
45
45
  nameAddressing: boolean;
46
46
  }
47
- /** Full-capability profile = the registry-backed mount with notifications wired (CC-209 verbatim form). */
47
+ /** Full-capability profile = the registry-backed mount with notifications wired (CC 209 verbatim form). */
48
48
  export declare const REGISTRY_TASK_TOOL_CAPS: TaskToolCapabilities;
49
49
  /** The env-direct thin-shell profile: cursor polling over a background shell, nothing else. */
50
50
  export declare const ENV_DIRECT_TASK_TOOL_CAPS: TaskToolCapabilities;
@@ -97,8 +97,22 @@ export const TOOL_CATALOG_ENTRIES = [
97
97
  compactable: true,
98
98
  },
99
99
  },
100
- { id: "repo-map", name: "RepoMap", source: "builtin", definedIn: "src/tools/fs/repo-map.ts", mountedBy: ["hands"], cards: ["repo-map"], face: { family: "search", effect: "read", contract: c("core.repo_map@1") } },
101
- { id: "lsp", name: "LSP", source: "builtin", definedIn: "src/core/lsp.ts", mountedBy: ["hands"], cards: [], face: { family: "search", effect: "read", contract: c("core.lsp@1") } },
100
+ {
101
+ id: "repo-map", name: "RepoMap", source: "builtin", definedIn: "src/tools/fs/repo-map.ts", mountedBy: ["hands"], cards: ["repo-map"],
102
+ face: {
103
+ family: "search", effect: "read", contract: c("core.repo_map@1"),
104
+ pathTarget: { param: "path", access: "read", base: "root", absent: "base" },
105
+ ruleFace: { params: ["path", "max_chars", "max_files"] },
106
+ },
107
+ },
108
+ {
109
+ id: "lsp", name: "LSP", source: "builtin", definedIn: "src/core/lsp.ts", mountedBy: ["hands"], cards: [],
110
+ face: {
111
+ family: "search", effect: "read", contract: c("core.lsp@1"),
112
+ pathTarget: { param: "filePath", access: "read", base: "cwd", absent: "none" },
113
+ ruleFace: { params: ["operation", "filePath", "line", "character", "query"] },
114
+ },
115
+ },
102
116
  {
103
117
  id: "artifact", name: ARTIFACT_TOOL_NAME, source: "builtin", definedIn: "src/tools/artifact/artifact-tool.ts", mountedBy: ["optional"], cards: ["artifact"],
104
118
  face: { family: "other", effect: "write", contract: c("core.artifact@1"), ruleFace: { primaryParams: ["action"] }, renderHints: { summaryParams: ["action", "file_path", "url"] } },
@@ -234,6 +234,9 @@ export interface EngineAttestation {
234
234
  readonly unresolvableCause?: import("./ask-unresolvable-notice.js").AskUnresolvableCause;
235
235
  /** The classes of the asks a policy fold collapsed — the proof behind a decision's `foldedAskClasses`. */
236
236
  readonly foldedClasses?: readonly import("./ask-origin.js").AskClass[];
237
+ /** Where a path deny row refused the call — the row, the view of the target it met, the face the call is on. The gate's
238
+ * exit mints the refusal's card from it; the judge that found the row is its only writer. */
239
+ readonly pathDenial?: import("../tools/fs/safety.js").PathDenialEvidence;
237
240
  }
238
241
  /** The members a station attests — everything but the call binding. */
239
242
  export type AttestationMembers = Omit<EngineAttestation, "toolCallId" | "toolName">;
@@ -302,6 +305,8 @@ export declare function screenApproverAttribution(v: unknown): {
302
305
  approver?: string;
303
306
  defect?: string;
304
307
  };
308
+ /** An integrity guard an ask stands on ({@link PermissionResult}'s `guards`). */
309
+ export type AskGuard = "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "artifact_shared_read";
305
310
  /**
306
311
  * A three-state permission decision for a tool call (design/37). Upgrades the old two-state
307
312
  * `{allow|deny}`:
@@ -327,11 +332,13 @@ export declare function screenApproverAttribution(v: unknown): {
327
332
  * record ({@link EngineAttestation}) the receipt refuses in any other form, and the gate's exit mints the one
328
333
  * settlement record ({@link import("./gate-outcome.js").GateOutcome}) from it; a policy that puts a `settledBy`
329
334
  * or `approver` member on its verdict is putting an unread property on it.
330
- * RB-479-B① (ruled 2026-08-02): the legacy `reason` field is REMOVED — one name for one thing.
335
+ * RB-479-B① (ruled 2026-08-02): the legacy `reason` field is REMOVED — one name for one thing. The ask arm's one-word
336
+ * `guard` is retired the same way, for `guards`.
331
337
  *
332
338
  * The TYPE carries that retirement only where a decision is a literal in an annotated position; an inferred `check`
333
- * return compiles green with a stray `reason`, so the enforcement is at RUNTIME — every point that consumes a decision
334
- * refuses one carrying an own `reason` property, fail-closed ({@link refuseOutOfContractDecision}).
339
+ * return compiles green with a stray `reason` or `guard`, so the enforcement is at RUNTIME — every point that consumes a
340
+ * decision refuses one that carries a retired member ({@link RETIRED_DECISION_MEMBERS}), fail-closed
341
+ * ({@link refuseOutOfContractDecision}).
335
342
  */
336
343
  export type PermissionResult = {
337
344
  action: "allow";
@@ -454,18 +461,21 @@ export type PermissionResult = {
454
461
  /** The ask's whole class set, in {@link import("./ask-origin.js").ASK_CLASSES} order (see
455
462
  * {@link AskRequest.classes}). Stamped beside {@link askClass}, under the same rule. */
456
463
  classes?: readonly import("./ask-origin.js").AskClass[];
457
- /** WHICH integrity guard minted this ask — a removal of a protected location, an unverifiable recursive delete,
458
- * shell text the engine cannot read or the session-transcript write. ENGINE-STAMPED by the guard policies; a fold-internal fact the class
459
- * derivation reads ({@link import("./ask-origin.js").classifyAsk}) so the guards, which mint the same
460
- * `requiresRealApproval` ask, are told apart by a fact and not by their prose. Never copied onto the
461
- * approval request or the park row. */
462
- guard?: "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "artifact_shared_read";
464
+ /** WHICH integrity guards stand behind this ask — a removal of a protected location, an unverifiable recursive delete,
465
+ * shell text the engine cannot read (or an environment that did not answer the read station), the session-transcript
466
+ * write. A set: one ask may stand on several, and each keeps its class. ENGINE-STAMPED by the guard policies and the
467
+ * gate fold; a fold-internal fact the class derivation reads ({@link import("./ask-origin.js").classifyAsk}) so the
468
+ * guards, which mint the same `requiresRealApproval` ask, are told apart by a fact and not by their prose. Never
469
+ * copied onto the approval request or the park row. Replaces the one-word `guard`, which a decision no longer carries:
470
+ * a decision that still does is refused by name ({@link RETIRED_DECISION_MEMBERS}). */
471
+ guards?: readonly AskGuard[];
463
472
  /** The call could not be READ against a rule that may reach it — a content-form deny row, a content-form
464
- * ask row, or a persisted-rule row (the lexer's word). ENGINE-STAMPED at the three unreadable mint sites;
465
- * the class derivation reads it as `rule_unreadable` (a mode may release it, a person may clear it, no
473
+ * ask row, a persisted-rule row (the lexer's word), or rows the shell read station's boundary stands on that
474
+ * could not be read (`persisted_rows`). ENGINE-STAMPED at the four unreadable mint sites;
475
+ * the class derivation reads it as `rule_unreadable` (no mode releases it, a person may clear it, no
466
476
  * automatic lane may). It replaces the former practice of stamping `matchedAskRule` on an unreadable
467
477
  * read: a rule that COULD NOT be matched is not a rule that matched. Fold-internal; not on the request. */
468
- ruleUnreadable?: "content_deny" | "content_ask" | "persisted_call";
478
+ ruleUnreadable?: "content_deny" | "content_ask" | "persisted_call" | "persisted_rows";
469
479
  /** The ask was authored by the DEPLOYMENT's own governance (an operator's must-ask list, a managed command
470
480
  * policy's default ask) — a fact a host's policy states on its own ask so the class derivation reads it
471
481
  * as `operator_forced`: a permission mode releases nothing an operator mandated (`dontAsk` refuses it like
@@ -853,8 +863,8 @@ export declare const DECISION_MEMBERS: readonly DecisionMember[];
853
863
  * taken off the carrier's own descriptor (a member outside the contract is neither read nor carried — a policy's self-declared
854
864
  * record on another name finds no reader); any other spelling refused by name — an accessor (never invoked), a member the
855
865
  * prototype or a non-enumerable property supplies unless that descriptor spells absence (an `undefined` data value) — judged by
856
- * descriptor, never read; (3) the retired `reason` field as an OWN property, judged without a read (authorship of the old
857
- * spelling: a prototype exposing it never wrote it; ignoring it silently dropped the policy's text); (4) the VALUES, off the engine's
866
+ * descriptor, never read; (3) a retired member ({@link RETIRED_DECISION_MEMBERS}: `reason`, `guard`) carried by the same presence law as a contract
867
+ * member — own, held by an accessor, or supplied by the prototype — judged without a read (ignoring it silently dropped the policy's text or the ask's guard); (4) the VALUES, off the engine's
858
868
  * one-read record, never a second read of the caller's object — the retired window absent, `action` in
859
869
  * its closed set (`screenContractValues`, decision-receipt.ts — every closed-set member is held to its set there; a consumer ranks a decision by testing two of its words, so any other value used to rank as an
860
870
  * allow; a `canUseTool` result outside its set is a failure in CC) and `decisionReason` in {@link DECISION_REASONS} when
@@ -1085,7 +1095,7 @@ export declare function createCoarseCommandNamePolicy(opts: {
1085
1095
  tools?: string[];
1086
1096
  }): ToolPolicy;
1087
1097
  /**
1088
- * CC 220 "Unverifiable Deletion Target" parity (auto-mode rule): ASK on a recursive+forced delete whose
1098
+ * CC 220 "Unverifiable Deletion Target" parity (auto-mode rule; its text is in 220–245, gone from 2.1.250 on): ASK on a recursive+forced delete whose
1089
1099
  * target is a variable / substitution that cannot be resolved from the command itself — fail-closed in the
1090
1100
  * sense that with no live classifier/approver the ask degrades to deny (see {@link PermissionResult}'s
1091
1101
  * `ask` doc), never to allow. The guard reads ONE plan, the read station's (`removalReadingOf`,
@@ -16,7 +16,7 @@ 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
18
  import { thrownText } from "./thrown-value.js";
19
- import { assertNoRetiredToolPolicyKeys } from "./retired-keys.js";
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";
22
22
  import { isNamespacedCoveringRuleName, namespacedRuleNameCovers, parsePermissionRule } from "./permission-rules.js";
@@ -43,6 +43,7 @@ const ATTESTATION_GROUP_OF = {
43
43
  classifierRound: "classifierRound",
44
44
  unresolvableCause: "unresolvableCause",
45
45
  foldedClasses: "foldedClasses",
46
+ pathDenial: "pathDenial",
46
47
  };
47
48
  const ATTESTATION_MEMBERS = Object.keys(ATTESTATION_GROUP_OF);
48
49
  const groupLead = (g) => ATTESTATION_MEMBERS.find((k) => ATTESTATION_GROUP_OF[k] === g);
@@ -238,18 +239,15 @@ export function constraintChainDigest(chain) {
238
239
  }
239
240
  const ALLOW = Object.freeze({ action: "allow" });
240
241
  const ALLOW_BY_RULE = Object.freeze({ action: "allow", decisionReason: "rule" });
241
- const RETIRED_TEXT_FIELD = "reason";
242
242
  export const DECISION_MEMBERS = Object.keys({
243
243
  action: 0, updatedInput: 0, message: 0, decisionReason: 0, requiresRealApproval: 0, denialLimitFallback: 0, classifierNoRuling: 0, allowRuleShadowed: 0, probeReason: 0,
244
244
  probeCause: 0, probeMandated: 0, mandated: 0, mandate: 0, probeReadRootCandidate: 0, matchedAskRule: 0, ruleEvidence: 0, segmentCoverage: 0,
245
- origin: 0, askClass: 0, classes: 0, guard: 0, ruleUnreadable: 0, authoredBy: 0, foldedAskClasses: 0, attestation: 0,
245
+ origin: 0, askClass: 0, classes: 0, guards: 0, ruleUnreadable: 0, authoredBy: 0, foldedAskClasses: 0, attestation: 0,
246
246
  });
247
247
  const HOOK_RESULT_MEMBERS = [...DECISION_MEMBERS, "additionalContext"];
248
248
  const HOOK_SEAM_UNREAD = new Set(["decisionReason"]);
249
249
  const HOOK_SEAM_OPTIONAL = new Set(["action"]);
250
250
  const HOOK_RECEIPTS = new WeakSet();
251
- const RETIRED_TEXT_FIELD_DENY_MESSAGE = `a permission decision carries the retired "${RETIRED_TEXT_FIELD}" field — rename it to "message" (the one text field ` +
252
- `a decision carries); denied fail-closed rather than executing a decision whose text this layer cannot read`;
253
251
  const ENGINE_RECEIPTS = new WeakSet();
254
252
  export function refuseOutOfContractDecision(d, opts) {
255
253
  const hookSeam = opts?.reasonIsNonInput === true;
@@ -260,11 +258,9 @@ export function refuseOutOfContractDecision(d, opts) {
260
258
  }
261
259
  if ((hookSeam ? HOOK_RECEIPTS : ENGINE_RECEIPTS).has(d) || isEngineRefusal(d))
262
260
  return d;
263
- const decision = receiveContract(d, hookSeam ? HOOK_RESULT_MEMBERS : DECISION_MEMBERS, "");
261
+ const decision = receiveContract(d, hookSeam ? HOOK_RESULT_MEMBERS : DECISION_MEMBERS, "", RETIRED_DECISION_MEMBERS);
264
262
  if (decision.kind === "refused")
265
263
  return decision.refused;
266
- if (Object.prototype.hasOwnProperty.call(d, RETIRED_TEXT_FIELD))
267
- return refusedOutOfContract(RETIRED_TEXT_FIELD_DENY_MESSAGE);
268
264
  const contract = decision.held;
269
265
  const refused = receiveNestedRecords(contract) ?? screenContractValues(contract, hookSeam ? { skip: HOOK_SEAM_UNREAD, optional: HOOK_SEAM_OPTIONAL } : undefined);
270
266
  if (refused !== undefined)
@@ -293,9 +289,9 @@ export function refuseOutOfContractDecision(d, opts) {
293
289
  `the engine keeps its own copy of what it read; denied fail-closed`);
294
290
  }
295
291
  const receipt = Object.freeze(attestation !== undefined ? { ...detached.value, attestation } : detached.value);
296
- for (const k of ["classes", "foldedAskClasses"])
297
- if (Object.hasOwn(receipt, k) && Array.isArray(receipt[k]))
298
- Object.freeze(receipt[k]);
292
+ for (const v of Object.values(receipt))
293
+ if (Array.isArray(v))
294
+ Object.freeze(v);
299
295
  for (const record of nestedRecordsOf(receipt)) {
300
296
  for (const v of Object.values(record))
301
297
  if (Array.isArray(v))
@@ -711,7 +707,7 @@ function removalAsk(guard, text) {
711
707
  unverifiable_delete: `Unverifiable recursive delete (fail-closed unless cleared): ${text}. Re-run the delete with the resolved literal path written into the command itself (or assign the variable a literal path in the same command, e.g. \`DIR=/exact/path; rm -rf "$DIR"\`) so the target can be verified.`,
712
708
  unverifiable_shell_text: `Unverifiable shell text (fail-closed unless cleared): ${text}. Write the text into the command itself (a \`-c\` string, a here-document or a here-string) and the engine reads it as it reads any command.`,
713
709
  };
714
- return { action: "ask", decisionReason: "safety", requiresRealApproval: true, guard, message: messages[guard] };
710
+ return { action: "ask", decisionReason: "safety", requiresRealApproval: true, guards: [guard], message: messages[guard] };
715
711
  }
716
712
  function pathSegments(p) {
717
713
  return p.split(/[\\/]/).filter(Boolean);
@@ -784,7 +780,7 @@ export function createTranscriptIntegrityPolicy(opts) {
784
780
  action: "ask",
785
781
  decisionReason: "safety",
786
782
  requiresRealApproval: true,
787
- guard: "transcript_write",
783
+ guards: ["transcript_write"],
788
784
  message: `Session-transcript write (fail-closed unless cleared): ${what}. Session transcripts (the .jsonl files ` +
789
785
  `under the agent data dir's sessions/ directory) are harness-written session state, not agent working ` +
790
786
  `files — modifying or deleting them tampers with the run's own audit trail. Reading them (ls/cat/grep ` +
@@ -475,8 +475,9 @@ export type TraceEvent = {
475
475
  } | {
476
476
  /**
477
477
  * design/179 — the person's persisted rule store could not be read (at prepare, or for one call), so
478
- * that read's rows took no part: the decision it served was made with ZERO persisted rows. The operator's
479
- * per-read record; the person hears it once per run (`permission.rule_store_unavailable`).
478
+ * that read's rows took no part: a call it served was judged without them — and, at a call, asks a person (the
479
+ * rows could hold a deny). The operator's per-read record; the person hears it once per run
480
+ * (`permission.rule_store_unavailable`).
480
481
  */
481
482
  kind: "permission.rule_store_unreadable";
482
483
  version: 1;
@@ -13,7 +13,7 @@ import type { AgentTool, ThinkingLevel, ToolInputValidationContext, ToolInputVer
13
13
  import type { Model, StreamFn } from "../internal/llm.js";
14
14
  export type { ModelRef, ModelRole, RoleSpec, ModelRoles, Brain } from "./model-seat.js";
15
15
  export { MODEL_ROLES } from "./model-seat.js";
16
- export type { StaleToolResultOffloadOptions, ImageInput, TaskSpec, ShellGateDoctrine } from "./task-spec.js";
16
+ export type { StaleToolResultOffloadOptions, ImageInput, TaskSpec, ShellGateDoctrine, UserAttachment } from "./task-spec.js";
17
17
  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";
@@ -22,12 +22,12 @@ export type { McpServerSpec, A2aServerSpec, McpElicitRequest, McpElicitResponse,
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";
25
- export type { TaskEventIdentity, BrainStatusPhase, BrainRetryErrClass, BrainStatus, ToolActivity, HumanInputSource, HumanInputDelivery, DelegationTaskType, TaskEvent, HumanInputEvent, CompactOutcome } from "./task-event.js";
25
+ export type { TaskEventIdentity, BrainStatusPhase, BrainRetryErrClass, BrainStatus, ToolActivity, HumanInputSource, HumanInputDelivery, DelegationTaskType, TaskEvent, HumanInputEvent, HumanInputAttachment, CompactOutcome } from "./task-event.js";
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
29
  export type { BackgroundChildEvent, DelegationLifecycleEvent } from "./delegation-frames.js";
30
- export { __resetMalformedDelegationSeatAnnouncement, deliverDelegationLifecycle } from "./delegation-frames.js";
30
+ export { __resetMalformedDelegationSeatAnnouncement, 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 } from "./delegation-frames.js";
3
+ export { __resetMalformedDelegationSeatAnnouncement, 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";
@@ -330,8 +330,8 @@ export declare function cutEngineSegments(text: string, segments: unknown): {
330
330
  * offsets are DEFINED: a user-lane message qualifies only when its content is a plain string or a
331
331
  * SINGLE text block (whose text is that same string). Multi-block / image-bearing content returns
332
332
  * `undefined` and consumers render the message VERBATIM with their own local flatten — the two
333
- * projection sides flatten blocks DIFFERENTLY (the auto-mode window joins with `\n` + `[image]`
334
- * placeholders; the compaction serializer drops non-text blocks and joins with no separator), so a
333
+ * projection sides flatten blocks DIFFERENTLY (the auto-mode window joins with `\n` + `[image: …]`
334
+ * block markers; the compaction serializer drops non-text blocks and joins with no separator), so a
335
335
  * single `[start, end)` range would point at different characters on each side. The restriction
336
336
  * costs nothing real: every engine-assembled prefixed/segmented message (first-frame objective
337
337
  * concatenation, resume continuation) is minted from ONE plain string — the sole exception is a
@@ -0,0 +1,164 @@
1
+ /**
2
+ * The USER-ATTACHMENT SEAT — the one road a file the user hands the model takes:
3
+ *
4
+ * the host declares it on the objective (`TaskSpec.userAttachments`) → the engine screens the declaration at the
5
+ * door → after the UserPromptSubmit hook lets the objective through, the engine reads a path entry with the Read
6
+ * tool's own read (`ModelFileReader.readForModel`) under the run's synthetic-Read judge, or takes the host's bytes →
7
+ * it mints ONE message per attachment that trails the human message: an engine note whose marked blocks hold only
8
+ * static sentences and whose data (the name or path, then the contents or the refusal) sits between them → the
9
+ * message's delivery record (`UserMessage.attachment`) is the one source of "what was delivered".
10
+ *
11
+ * This module owns the three steps no other module may repeat: the screen (`screenUserAttachments`), the mint
12
+ * (`readUserAttachments` → the frame seeds, the `human_input` projection and the pending commits) and the settlement
13
+ * (`createAttachmentDelivery`: a read's effects commit when its frame reaches the transcript, keyed by
14
+ * `(inputId, ordinal)` — never at read time, never by text).
15
+ *
16
+ * The frame, per attachment (blocks in order):
17
+ * <system-reminder mark="…">\n<opening sentence>\n</system-reminder>\n — static, the attachment's number only
18
+ * Path: "<declared path>"\n or Name: "<name>"\n — data (lossless quoted label)
19
+ * <data blocks> — the file's own blocks / the refusal
20
+ * \n\n<system-reminder mark="…">\n<note sentence>\n</system-reminder> — one per engine note, static
21
+ * \n\n<system-reminder mark="…">\nEnd of attachment <n>.\n</system-reminder>
22
+ * Every marked block after data opens after a blank line (`MARKED_BLOCK_AFTER_DATA`): the completions lanes join a
23
+ * message's text blocks with no separator.
24
+ */
25
+ import type { ImageContent, TextContent, UserAttachmentSupply, UserMessageAttachment } from "../internal/llm.js";
26
+ import type { HumanInputAttachment } from "./task-event.js";
27
+ import { type ModelFileReader } from "../tools/fs/fs-read.js";
28
+ import type { SyntheticReadJudge } from "./synthetic-read-verdict.js";
29
+ import { type ReminderDisclosureCounts } from "./reminder-disclosure.js";
30
+ /** At most this many attachments on one objective. */
31
+ export declare const USER_ATTACHMENTS_MAX = 20;
32
+ /** The longest path or name an entry may carry (the platform path limit). */
33
+ export declare const USER_ATTACHMENT_LABEL_MAX = 4096;
34
+ /** What the engine found an attachment to be (an output, never declared): the Read lanes, a directory, host bytes. */
35
+ export declare const USER_ATTACHMENT_KINDS: readonly ["text", "notebook", "image", "pdf", "directory", "client_text", "client_image"];
36
+ /** Who held the bytes — the entry's arm. */
37
+ export declare const USER_ATTACHMENT_SUPPLIES: readonly ["path", "content_text", "content_image"];
38
+ /** What reached the model of an attachment's contents — the Read layer's own outcome words, the same frozen array. */
39
+ export declare const USER_ATTACHMENT_OUTCOMES: readonly ["attached", "partial", "unchanged", "not_attached"];
40
+ /**
41
+ * The seat's capability descriptor — what a host reads to decide, per attachment, whether to declare it here: its kind
42
+ * is in `kinds` and its arm in `supplies` ⇒ declare it on the objective; otherwise (a kind this seat does not carry, or
43
+ * an engine without the descriptor) the host carries it some other way. `entries`: the objective only — a steer carries
44
+ * no attachments (they go with the next objective).
45
+ */
46
+ export declare const USER_ATTACHMENT_SEAT: Readonly<{
47
+ entries: readonly ["objective"];
48
+ kinds: readonly ["text", "notebook", "image", "pdf", "directory", "client_text", "client_image"];
49
+ supplies: readonly ["path", "content_text", "content_image"];
50
+ limits: Readonly<{
51
+ maxEntries: 20;
52
+ maxRenderedTextChars: 100000;
53
+ }>;
54
+ }>;
55
+ /** One declaration as the door captured it: the caller's object read once, members copied. */
56
+ export type ScreenedUserAttachment = {
57
+ readonly supply: "path";
58
+ readonly path: string;
59
+ readonly lines?: {
60
+ readonly start: number;
61
+ readonly end?: number;
62
+ };
63
+ } | {
64
+ readonly supply: "content_text";
65
+ readonly name: string;
66
+ readonly text: string;
67
+ readonly startLine: number;
68
+ readonly rendered: string;
69
+ } | {
70
+ readonly supply: "content_image";
71
+ readonly name: string;
72
+ readonly data: string;
73
+ readonly mimeType: string;
74
+ };
75
+ /** The door's refusal: one code, the field named first. */
76
+ export declare function userAttachmentRefusal(field: string, detail: string): Error & {
77
+ code: "config.user_attachment_invalid";
78
+ };
79
+ /**
80
+ * The door: screen the seat's value (synchronous; the caller's object is read once, member by member, before the
81
+ * first await of the run). `undefined` / an empty array ⇒ no attachments. `resumeLeg`: a durable resume mints no new
82
+ * human message, so any entry there is refused. Throws `config.user_attachment_invalid` naming the field.
83
+ */
84
+ export declare function screenUserAttachments(seat: unknown, opts: {
85
+ resumeLeg: boolean;
86
+ }): readonly ScreenedUserAttachment[] | undefined;
87
+ /** A path entry on a run with no workspace: there is nothing the engine could read it from. */
88
+ export declare function refuseUserAttachmentPathsWithoutWorkspace(screened: readonly ScreenedUserAttachment[] | undefined): void;
89
+ /** One declared attachment as the UserPromptSubmit hook sees it (`UserPromptSubmitContext.userAttachments`). */
90
+ export interface UserAttachmentDeclaration {
91
+ /** 1-based position among the objective's attachments. */
92
+ readonly ordinal: number;
93
+ /** Who holds the bytes: `path` (the engine reads it), `content_text` / `content_image` (the host sent them). */
94
+ readonly supply: UserAttachmentSupply;
95
+ readonly path?: string;
96
+ readonly name?: string;
97
+ /** A host entry's size in bytes (text as UTF-8, an image decoded). */
98
+ readonly bytes?: number;
99
+ }
100
+ /** The UserPromptSubmit hook's view of the declarations. */
101
+ export declare function userAttachmentDeclarations(screened: readonly ScreenedUserAttachment[]): readonly UserAttachmentDeclaration[];
102
+ /**
103
+ * The host images' byte admission — the one function an image the Read tool serves passes. Asynchronous (it decodes),
104
+ * run on the captured values before any attachment is read or delivered. Throws `config.user_attachment_invalid`.
105
+ */
106
+ export declare function admitUserAttachmentImages(screened: readonly ScreenedUserAttachment[]): Promise<ReadonlyMap<number, ImageContent>>;
107
+ /** One minted frame: the trailing message's seed, and the commit its delivery owes (absent ⇒ nothing to commit). */
108
+ export interface MintedAttachmentFrame {
109
+ readonly seed: {
110
+ readonly content: ReadonlyArray<TextContent | ImageContent>;
111
+ readonly attachment: UserMessageAttachment;
112
+ };
113
+ readonly commit?: () => void;
114
+ }
115
+ export interface ReadUserAttachmentsInput {
116
+ screened: readonly ScreenedUserAttachment[];
117
+ /** The human input the attachments belong to (minted before the frames and the event, shared by both). */
118
+ inputId: string;
119
+ /** The leg's one file reader (absent on a run without a workspace — then only host entries exist). */
120
+ reader: ModelFileReader | undefined;
121
+ /** The run's synthetic-Read judge. */
122
+ judge: SyntheticReadJudge;
123
+ /** The admitted host images, by ordinal. */
124
+ images: ReadonlyMap<number, ImageContent>;
125
+ /** The tracked working directory a relative path resolves against (the Read tool's own). */
126
+ cwd: string | undefined;
127
+ mark: string | undefined;
128
+ counts: ReminderDisclosureCounts | undefined;
129
+ signal: AbortSignal;
130
+ }
131
+ /**
132
+ * Read every declared attachment and mint its frame. Side-effect free toward the run: nothing is committed here — each
133
+ * frame carries the commit its delivery owes, run only when the frame is in the transcript.
134
+ */
135
+ export declare function readUserAttachments(input: ReadUserAttachmentsInput): Promise<MintedAttachmentFrame[]>;
136
+ /** The objective lane's read: once the UserPromptSubmit hook let the objective through (a blocked objective reads
137
+ * nothing), and before the pre-call gate, which prices the frames (`chars`). Commits nothing. */
138
+ export declare function readObjectiveAttachments(prepared: Pick<import("./runner/contracts.js").Prepared, "userAttachments" | "modelFileReader" | "syntheticReadVerdict" | "cwdRef" | "reminderMark" | "reminderDisclosureCounts" | "abortController">, inputId: string, blocked: boolean): Promise<{
139
+ frames: MintedAttachmentFrame[];
140
+ chars: number;
141
+ }>;
142
+ /** The last attachment frame of the group the message `id` opens, or `id` when it has none (a `resumeAt` that keeps the
143
+ * message keeps its group). The frames of one input are appended one after another under its message (each the child of
144
+ * the one before), all carrying its input id. */
145
+ export declare function lastAttachmentFrameOf(entries: ReadonlyArray<{
146
+ id: string;
147
+ parentId: string | null;
148
+ type: string;
149
+ message?: unknown;
150
+ }>, id: string): string;
151
+ /** The `human_input` projection of the minted frames. */
152
+ export declare function humanInputAttachmentsOf(frames: readonly MintedAttachmentFrame[]): HumanInputAttachment[];
153
+ /**
154
+ * The run's pending attachment commits, keyed by `(inputId, ordinal)`. A frame's commit runs exactly once, when the
155
+ * harness reports that frame in the transcript; a frame that never got there (a refused pre-call budget, an abort, a
156
+ * failed append) commits nothing — its entry is simply never settled.
157
+ */
158
+ export interface AttachmentDelivery {
159
+ /** Stage the minted frames of one input — called only once every gate before the append has passed. */
160
+ stage(inputId: string, frames: readonly MintedAttachmentFrame[]): void;
161
+ /** The frame `(inputId, ordinal)` is in the transcript: run its commit (once) and forget it. */
162
+ settle(inputId: string, ordinal: number): void;
163
+ }
164
+ export declare function createAttachmentDelivery(): AttachmentDelivery;