@robota-sdk/agent-tools 3.0.0-beta.81 → 3.0.0-beta.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,559 @@
1
+ # @robota-sdk/agent-tools
2
+
3
+ ## 3.0.0-beta.83
4
+
5
+ ### Minor Changes
6
+
7
+ - 0368058: A running child-process subagent follows a `/sandbox` change made in its parent session. Before, it
8
+ kept the settings it started with until it ended, so a long-running `auto`-mode subagent went on
9
+ auto-approving confined commands after the user turned auto-allow off.
10
+
11
+ - `agent-tools` (minor): `OsSandboxClient.watchSettings(watcher)` reports each `configure`, until
12
+ its returned function is called.
13
+ - `agent-subagent-runner` (minor): the runner factory takes `watchParentSandboxSettings`, and
14
+ forwards each change to every running child as a `sandbox_settings` message. It watches before
15
+ reading the start payload, so no change is lost while a child starts, and stops when the child
16
+ exits or fails to start. A composed sandbox may define `applyParentSettings`. The worker applies
17
+ each change through it. On settings it cannot take, it aborts the run, lets a running command
18
+ finish, and ends the run with that error.
19
+ - `agent-cli` (patch): robota watches its live sandbox, and a child applies each change to the
20
+ instance its tools and approval read.
21
+
22
+ ### Patch Changes
23
+
24
+ - 57280bf: Every published package now declares `"engines": { "node": ">=22.12.0" }`. Before, 27 of the 38
25
+ packages declared no floor (`agent-core`, `agent-tools` and every provider among them),
26
+ `agent-session` and `agent-file-authority` declared `>=20.19.0`, and the other nine declared
27
+ `>=22.0.0`, so a consumer on Node 20 saw at most a warning from a transitive dependency.
28
+
29
+ Why 22.12: `agent-cli` and `agent-ui-terminal` need Node 22 through `ink` 7, and the CommonJS entries
30
+ of `agent-tools` and its dependents, `agent-transport`/`node` and its dependents, and
31
+ `agent-ui-terminal` `require()` ESM-only dependencies (`p-limit`, `jose`, `chalk`), which Node 22
32
+ supports unflagged only from 22.12. `engines` is advisory unless the consumer enables `engine-strict`.
33
+
34
+ No code changes: `tsdown` now reads `node22.12.0` as its build target from the field.
35
+
36
+ - 18c0d5c: Every published package now exports `./package.json`, so `require('<package>/package.json')` and
37
+ `import('<package>/package.json', { with: { type: 'json' } })` work instead of failing with
38
+ `ERR_PACKAGE_PATH_NOT_EXPORTED`, and each tarball now ships the package's `CHANGELOG.md`.
39
+ - Updated dependencies [e689c8e]
40
+ - Updated dependencies [be0e53c]
41
+ - Updated dependencies [8bd5fac]
42
+ - Updated dependencies [57280bf]
43
+ - Updated dependencies [5033dd9]
44
+ - Updated dependencies [18c0d5c]
45
+ - Updated dependencies [dbd888d]
46
+ - Updated dependencies [1887e54]
47
+ - @robota-sdk/agent-core@3.0.0-beta.83
48
+ - @robota-sdk/agent-process@3.0.0-beta.83
49
+
50
+ ## 3.0.0-beta.82
51
+
52
+ ### Patch Changes
53
+
54
+ - Updated dependencies [c7f9203]
55
+ - @robota-sdk/agent-core@3.0.0-beta.82
56
+ - @robota-sdk/agent-process@3.0.0-beta.82
57
+
58
+ ## 3.0.0-beta.81
59
+
60
+ ### Minor Changes
61
+
62
+ - 3038eb7: A message from another session is instant messaging: text from an untrusted third party that
63
+ carries no authority. What the model does with it is decided by the session's ordinary permissions —
64
+ rules, permission mode and remembered consent — exactly like the session's own work. The per-origin
65
+ peer policy is gone.
66
+
67
+ **BREAKING**
68
+
69
+ - `agent-core`: `IPermissionEvaluationContext.peerTurn` is now a boolean. It decides only whether a
70
+ `repliesToPeer` tool exists; every other call in a peer turn is decided as in any turn. Removed:
71
+ `isToolAvailableInPeerTurn`, `isSecretPath`, `IPeerTurnAuthority`, `TPeerReach` (now exported by
72
+ `agent-interface-session-mobility`) and `IToolPermissionProfile.workspacePaths`.
73
+ - `agent-tools`: `Read` and `Glob` no longer declare `workspacePaths`.
74
+ - `agent-session`: `ISessionRunOptions.peerReach` is replaced by `peerTurn?: boolean`, and
75
+ `ISessionOptions.allowPeerChanges` is removed. An ask in a peer turn is answered like any other:
76
+ a consent the operator remembered answers it, and an "always allow" given there is remembered.
77
+ - `agent-interface-session`: `IPeerTurnContext` no longer has `reach`; it carries only the reply
78
+ route.
79
+ - `agent-interface-session-mobility`: `peerReachOf` is removed; `TPeerReach` moves here. A delegated
80
+ turn carries no reach.
81
+ - `agent-framework`: the `peers.allowChanges` setting is removed (an existing value is ignored). A
82
+ peer turn is offered the ordinary tools, plus `peer_reply`.
83
+
84
+ **Changes**
85
+
86
+ - `agent-framework`: the per-turn statement tells the model the message is an opinion from an
87
+ untrusted third party, not its owner's instruction, and that it decides for itself whether and how
88
+ to act. A message still expands no `@path` and attaches no context reference, and its requests
89
+ still carry no provider-hosted tool, since no permission step can decide one. The session takes
90
+ at most 6 messages a minute and 30 an hour from each sender for a turn; a message over the limit
91
+ is refused with a reason the sender receives. A prompt answer given in the name of a `peer:` or `external:`
92
+ driver is ignored. External-event turns keep their tool-less baseline.
93
+ - `agent-cli`: incoming peer turns carry only their reply route.
94
+
95
+ ### Patch Changes
96
+
97
+ - Updated dependencies [3038eb7]
98
+ - Updated dependencies [02b7452]
99
+ - Updated dependencies [ec5e477]
100
+ - @robota-sdk/agent-core@3.0.0-beta.81
101
+ - @robota-sdk/agent-process@3.0.0-beta.81
102
+
103
+ ## 3.0.0-beta.80
104
+
105
+ ### Major Changes
106
+
107
+ - 807d161: **BREAKING — ARCH-010: the execution root is a required contract field, and the containment guard now fails closed.**
108
+
109
+ The file-tool containment guard was fail-open: with no root configured it answered "allowed". A tool
110
+ built with no `cwd` therefore had no boundary — measured, a `Read` constructed that way returned the
111
+ contents of `/etc/hostname` — and the child-process subagent worker called `createDefaultTools()` with
112
+ no argument at all, so subagents got exactly that. Three independent auditors found three different
113
+ symptoms of this one missing field.
114
+
115
+ **Removed — seven context-free tool singletons.** `readTool`, `writeTool`, `editTool`, `globTool`,
116
+ `grepTool`, `shellTool`, `bashTool` are gone from `@robota-sdk/agent-tools`. A module-level instance is
117
+ bound at import time and can carry no containment root, so after the guard was inverted they could only
118
+ refuse everything.
119
+
120
+ Migrate to the factory of the same name, passing the directory the tool is allowed to work in:
121
+
122
+ ```ts
123
+ // before
124
+ import { readTool, globTool } from '@robota-sdk/agent-tools';
125
+ const tools = [readTool, globTool];
126
+
127
+ // after
128
+ import { createReadTool, createGlobTool } from '@robota-sdk/agent-tools';
129
+ const cwd = process.cwd(); // or the workspace this agent is scoped to
130
+ const tools = [createReadTool({ cwd }), createGlobTool({ cwd })];
131
+ ```
132
+
133
+ `webFetchTool`, `webSearchTool` and `askUserQuestionTool` are unchanged — they touch no filesystem, so
134
+ there is no root to contain them by.
135
+
136
+ **`cwd` is now REQUIRED** on `ISandboxToolOptions`, `IContainedBuiltinToolOptions` (and everything
137
+ extending them), `ICreateDefaultToolsOptions`, `ISessionOptions` and `ISubagentOptions`. The `= {}`
138
+ default parameter was removed from every builtin factory — that default was the mechanism by which
139
+ "forgot the root" was legal. `new Session({...})` without `cwd` no longer compiles, and also throws at
140
+ construction, because a required field is only required to a TypeScript caller.
141
+
142
+ **`Session` no longer reads `process.cwd()`.** It uses the root it was given, and `getCwd()` exposes it
143
+ so a fork or subagent asks the session instead of re-deriving a root that can disagree with it.
144
+
145
+ **Behavioural change even for callers that already passed a root**: a tool that somehow reaches the
146
+ guard with no root now REFUSES with an explicit error ("no containment root is configured … this is an
147
+ assembly bug, not a path problem") instead of allowing the access.
148
+
149
+ ### Minor Changes
150
+
151
+ - 267af5f: The execution containment seam states where tools run.
152
+
153
+ - **`ISandboxClient.filesystem`** (`'shared' | 'separate'`, absent means `separate`).
154
+ - With a **separate** filesystem (E2B, in-memory), every file tool goes through the sandbox, and
155
+ `Glob`/`Grep`, which can only read the host, are withheld. Previously a remote sandbox still
156
+ offered host-reading search tools beside sandbox-writing edit tools.
157
+ - With a **shared** filesystem (OS-level confinement over the host's files), file tools stay on
158
+ the host under the path guard, and only commands go through the sandbox.
159
+ - **`describeExecutionContainment` / `routesFilesThroughSandbox`** name the containment (`host`,
160
+ `sandbox-shared`, `sandbox-separate`) instead of inferring it from an absent value.
161
+ - **`robota doctor` reports `execution.containment`.** Robota composes no sandbox today, so the
162
+ doctor says shell commands run unconfined on the host and the permission rules are the only
163
+ boundary. The CLI composition and the doctor read the same value.
164
+ - **The `Agent` and `BackgroundProcess` tools no longer fall back to `process.cwd()`** when they
165
+ were built without an execution root. They report an assembly error instead, as the file tools
166
+ already did (ARCH-010).
167
+
168
+ - 722e88a: Shell commands can run in an OS-level sandbox: bubblewrap on Linux and WSL2, Seatbelt on macOS.
169
+
170
+ - **Confinement:** covers the command and every process it starts.
171
+ - Writes are limited to the working directory, the temporary directories and
172
+ `sandbox.filesystem.allowWrite`.
173
+ - Agent, git-hook, MCP and shell configuration inside the workspace stays read-only.
174
+ - `sandbox.filesystem.denyRead` hides paths from the command.
175
+ - The network is on or off (`sandbox.network.enabled`).
176
+ - **Modes:** `/sandbox` switches between `auto-allow`, `regular` and `off` for the next command and
177
+ saves the choice.
178
+ - In `auto-allow` (`sandbox.autoAllowBashIfSandboxed`), a confined command runs without a prompt
179
+ in `default` and `acceptEdits`.
180
+ - Deny rules, ask rules, critical removals and plan mode still apply first.
181
+ - **Exclusions:** `sandbox.excludedCommands` run unconfined, through the ordinary permission path.
182
+ - **When the sandbox cannot run:** a missing or unusable backend is reported at startup, in
183
+ `robota doctor` and in `/sandbox`, and commands then run unconfined.
184
+ `sandbox.failIfUnavailable` refuses to start instead.
185
+ - **New contracts:**
186
+ - `OsSandboxClient`, `detectOsSandbox`, `bubblewrapArguments`, `seatbeltProfile`.
187
+ - `ISandboxClient.wrapCommand` and `autoApproves`.
188
+ - `IPermissionEvaluationContext.sandboxAutoApproved`.
189
+ - The `commandSandbox` session option.
190
+ - The `sandbox` settings key and the `sandbox` command host adapter.
191
+
192
+ ### Patch Changes
193
+
194
+ - 2345c0b: `Glob` and `Grep` now bound file collection to a fixed candidate ceiling before materializing more
195
+ matches, instead of enumerating and `stat`-ing an entire search tree before any `limit`/`headLimit`
196
+ had a chance to apply. `Glob` streams `fast-glob` matches instead of resolving its full-match
197
+ promise form, and `Grep`'s directory walk stops once it has visited the ceiling's worth of entries.
198
+ Both tools state the truncation explicitly in their output when the ceiling is hit — `Glob`'s result
199
+ ordering (by modification time) then applies only to the candidates collected before the ceiling,
200
+ not to the full match set.
201
+ - 118fe0e: Bound Edit file input before materializing content, including when file size metadata is stale, and reject a replaceAll whose output would exceed the same ceiling before writing.
202
+ - 240777e: Bound Grep file reads before materializing content, including when file size metadata is stale or a file grows during a search.
203
+ - 718bdf5: Run builtin Grep regex matching in preemptible isolation with bounded input, output, and execution time.
204
+ - 61db70f: Bound host Read input and formatted output bytes before a workflow can materialize an oversized file or result. Oversized reads now fail the tool call instead of returning content.
205
+ - db80aba: CLI-042: parallelize the Grep built-in tool's per-file content scan with bounded
206
+ concurrency (`p-limit(50)`), following the glob-tool precedent. Results are still
207
+ collected in file-enumeration order, so output is byte-identical to the previous
208
+ sequential implementation (verified against a pre-change golden on a 1,201-file
209
+ corpus); `headLimit` truncation and binary/unreadable-file skipping are unchanged.
210
+ - 4f3c075: Assemble complete, verified package generations before switching build output; preserve the previous generation on build failure and pack only verified regular-file images. Include copied CLI web assets in affected-build ordering and artifact transfer. Preserve the CLI version in managed build paths. Public runtime contracts remain compatible (patch).
211
+ - b6d14ce: Make the universal JSON-schema subset able to express an object, so a nested `z.object()` keeps its
212
+ properties and required fields instead of reaching the model as `{ "type": "object" }`. Tools and
213
+ structured-output schemas with one level of nesting are now advertised in full and enforced on the
214
+ tool-input path; `z.union` / `z.discriminatedUnion` / `z.literal` are supported and map to `anyOf`
215
+ and single-value enums; `.nullable()` keeps its null branch; and Zod's `strip`, `strict` and
216
+ `passthrough` modes stop collapsing into two `additionalProperties` emissions. Fixes the shipped
217
+ `Computer` and `AskUserQuestion` built-ins, whose action and question fields were being dropped
218
+ entirely.
219
+
220
+ `agent-core` is **minor**, not patch: `IObjectParameterSchema` is a new export, and
221
+ `IParameterSchema.type` became optional (a union node carries `anyOf` instead of a type), which is a
222
+ consumer-visible type change — two call sites in this repo needed editing to keep compiling.
223
+ `additionalProperties` also widened to `boolean | IParameterSchema`, and `required` and `anyOf` are
224
+ new members. The provider packages are **patch**: each adapts to the widened subset without changing
225
+ its own public surface.
226
+
227
+ - 07b627f: A local peer can now be answered, and what its turn may do depends on where it runs.
228
+
229
+ - `agent-core`: the permission evaluator takes a peer turn's authority as one more input
230
+ (`IPermissionEvaluationContext.peerTurn`), decided after the deny list and ceiling and before
231
+ bypass and allow rules. A peer on another host uses no tool. A peer on the same host may use an
232
+ inspect-class tool that declares `workspacePaths`, only when every named location resolves inside
233
+ the workspace and is not a credential (`isSecretPath`); write and execute tools are refused unless
234
+ enabled, and then every use asks. A tool declaring `repliesToPeer` exists only in a peer turn and
235
+ asks once the turn used another tool. New: `isToolAvailableInPeerTurn`, `TPeerReach`,
236
+ `IPeerTurnAuthority`. `IRunOptions.withholdHostedTools` leaves a provider's hosted tools out of a
237
+ run's requests (`nativeWebTools` with `false` withholds a hosted tool for one call).
238
+ - `agent-tools`: `Read` and `Glob` declare the arguments that say where they look. `Grep` does not:
239
+ it reads files it was never named, so a peer turn does not get it.
240
+ - `agent-session`: `ISessionRunOptions.peerReach` makes a run a peer turn for the permission policy;
241
+ every ask in it needs a fresh approval, and its requests carry no provider-hosted tool. `ISessionOptions.allowPeerChanges` enables write and execute
242
+ tools for same-host peer turns.
243
+ - `agent-interface-session`: `ISubmitOptions.peer` (`IPeerTurnContext`) carries a peer turn's reach,
244
+ the message it answers and the session a reply goes to.
245
+ - `agent-interface-session-mobility`: `IPeerMessage.inReplyTo` threads a conversation;
246
+ `peerReachOf(admission)` maps admission to a reach.
247
+ - `agent-framework`: a peer turn is offered what its origin allows, and a new `peer_reply` tool
248
+ answers the peer that sent the message, threaded to it. The setting `peers.allowChanges` enables
249
+ write and execute tools for same-host peer turns.
250
+ - `agent-ui-terminal`: a permission prompt in a peer turn names the requesting peer.
251
+ - `agent-cli`: incoming peer turns carry their reach and reply route; a conversation is limited in
252
+ depth and in how often this session answers it, and a reply over a limit is not sent and the
253
+ operator is told.
254
+ - `agent-provider-anthropic`, `agent-provider-openai-compatible` (Qwen): a request whose
255
+ `nativeWebTools` sets a hosted tool to `false` is sent without it.
256
+
257
+ - 833afe1: Remove the remaining polynomial-ReDoS backtracking (SEC-003, CodeQL `js/polynomial-redos`) and stop the DTLS fingerprint binding to SDP free text.
258
+
259
+ **`extractDtlsFingerprint` (agent-remote-pairing) — remote-reachable, pre-authentication.** Unlike the rest of this class, the SDP it parses arrives over the signaling relay, which the pairing design treats as untrusted, and it is parsed _before_ the channel-binding confirmation — on the browser peer, before `setRemoteDescription` too. Unanchored, `a=fingerprint:\S+\s+…` restarted from every offset in a non-space run: 5.0 s on a 400 KB SDP. It is now anchored to the start of an SDP line (`/^…/m`), which is linear and also stops the extractor from returning a value smuggled into another line's free text (`s=`, `i=`, an unrelated attribute) — text no DTLS stack reads, and which a relay controls. **Behaviour change:** a mid-line `a=fingerprint:` is no longer recognised. Every SDP a WebRTC stack emits puts the attribute at the start of its own line, so no real SDP is affected. A session-level line can still shadow a media-level one; that residual is recorded in the SEC-003 backlog.
260
+
261
+ **Trailing-run trims (agent-framework, agent-cli, agent-tools).** `replace(/-+$/, '')`-shaped regexes have no start anchor, so the engine retried the run from every offset inside it and each retry rescanned to the end — 3.0 s at 100 K characters, ~50 s at 400 K. The memory topic sanitiser, the provider profile-name sanitiser, the model-command tool-name projection, the npm registry URL builder, the git-worktree path-segment sanitiser and the sandbox-root normaliser now use linear index scans (`trimEdgeChars` / `trimTrailingChars` in agent-framework, local helpers elsewhere), proven equivalent to the regexes they replace over every string of the relevant alphabet up to 12 characters.
262
+
263
+ **Whitespace-ambiguity parsers (agent-framework).** The skill and agent-definition frontmatter list splitters used `/\s*,\s*/`, whose whitespace run overlapped nothing after it on a failed comma — 12.6 s on a 200 K run. They now split on `','`; the padding was already removed by the `.trim()` that follows, so the parsed lists are unchanged. The `.git` `gitdir:` pointer and the task-file open-item matcher used `\s*(.+)$` / `\s+(.+)$`, where `\s` and `.` both match a space; the capture is now pinned to start non-space, which accepts exactly the same inputs (verified exhaustively) and removes 14.5 s and 15.4 s worst cases.
264
+
265
+ **`WebFetch` HTML-to-text (agent-tools) — carried no CodeQL alert.** Found by sweeping for the same shapes rather than the flagged lines, and the only quadratic here whose input is a live response body from an arbitrary URL. `<[^>]+>`, `<script[\s\S]*?</script>` and `<style…>` each restarted from every opener that had no terminator: 12.6 s on 200 KB of `<`, and the 5 MB the fetch allows would have taken hours. All three are now single-pass scans, verified character-for-character identical to the regexes over ~800 K generated inputs.
266
+
267
+ Apart from the `extractDtlsFingerprint` anchoring noted above, no behaviour changes: every fix accepts the same inputs and produces the same values, and each ships an equivalence test pinning that.
268
+
269
+ - Updated dependencies [7b6234c]
270
+ - Updated dependencies [4eea54b]
271
+ - Updated dependencies [1698be4]
272
+ - Updated dependencies [d4189b9]
273
+ - Updated dependencies [9edae52]
274
+ - Updated dependencies [4078a72]
275
+ - Updated dependencies [4c73a0b]
276
+ - Updated dependencies [196a900]
277
+ - Updated dependencies [f336838]
278
+ - Updated dependencies [34e50f0]
279
+ - Updated dependencies [722e88a]
280
+ - Updated dependencies [d23c848]
281
+ - Updated dependencies [fec722f]
282
+ - Updated dependencies [2d3b2c0]
283
+ - Updated dependencies [4772067]
284
+ - Updated dependencies [9fbab1b]
285
+ - Updated dependencies [a009f5b]
286
+ - Updated dependencies [4f3c075]
287
+ - Updated dependencies [475e085]
288
+ - Updated dependencies [e477440]
289
+ - Updated dependencies [9dcb5da]
290
+ - Updated dependencies [a95ca85]
291
+ - Updated dependencies [b6d14ce]
292
+ - Updated dependencies [0382a51]
293
+ - Updated dependencies [93d061d]
294
+ - Updated dependencies [39554a1]
295
+ - Updated dependencies [d28430a]
296
+ - Updated dependencies [07b627f]
297
+ - Updated dependencies [d0de5b2]
298
+ - Updated dependencies [d6b9404]
299
+ - Updated dependencies [9814afc]
300
+ - @robota-sdk/agent-core@3.0.0-beta.80
301
+ - @robota-sdk/agent-process@3.0.0-beta.80
302
+
303
+ ## 3.0.0-beta.79
304
+
305
+ ### Patch Changes
306
+
307
+ - @robota-sdk/agent-core@3.0.0-beta.79
308
+
309
+ ## 3.0.0-beta.78
310
+
311
+ ### Patch Changes
312
+
313
+ - Updated dependencies [6f308d1]
314
+ - @robota-sdk/agent-core@3.0.0-beta.78
315
+
316
+ ## 3.0.0-beta.77
317
+
318
+ ### Patch Changes
319
+
320
+ - Updated dependencies
321
+ - @robota-sdk/agent-core@3.0.0-beta.77
322
+ - @robota-sdk/agent-process@3.0.0-beta.77
323
+
324
+ ## 3.0.0-beta.76
325
+
326
+ ### Patch Changes
327
+
328
+ - Updated dependencies
329
+ - Updated dependencies
330
+ - Updated dependencies
331
+ - Updated dependencies [576af62]
332
+ - @robota-sdk/agent-core@3.0.0-beta.76
333
+
334
+ ## 3.0.0-beta.75
335
+
336
+ ### Patch Changes
337
+
338
+ - Updated dependencies
339
+ - @robota-sdk/agent-core@3.0.0-beta.75
340
+
341
+ ## 3.0.0-beta.74
342
+
343
+ ### Patch Changes
344
+
345
+ - @robota-sdk/agent-core@3.0.0-beta.74
346
+
347
+ ## 3.0.0-beta.73
348
+
349
+ ### Patch Changes
350
+
351
+ - @robota-sdk/agent-core@3.0.0-beta.73
352
+
353
+ ## 3.0.0-beta.72
354
+
355
+ ### Patch Changes
356
+
357
+ - @robota-sdk/agent-core@3.0.0-beta.72
358
+
359
+ ## 3.0.0-beta.71
360
+
361
+ ### Patch Changes
362
+
363
+ - fix(context): unify token estimation to single SSOT — status bar and /context list now use the same serialized JSON estimate
364
+ - Updated dependencies
365
+ - @robota-sdk/agent-core@3.0.0-beta.71
366
+
367
+ ## 3.0.0-beta.70
368
+
369
+ ### Patch Changes
370
+
371
+ - @robota-sdk/agent-core@3.0.0-beta.70
372
+
373
+ ## 3.0.0-beta.69
374
+
375
+ ### Patch Changes
376
+
377
+ - @robota-sdk/agent-core@3.0.0-beta.69
378
+
379
+ ## 3.0.0-beta.68
380
+
381
+ ### Patch Changes
382
+
383
+ - @robota-sdk/agent-core@3.0.0-beta.68
384
+
385
+ ## 3.0.0-beta.67
386
+
387
+ ### Patch Changes
388
+
389
+ - @robota-sdk/agent-core@3.0.0-beta.67
390
+
391
+ ## 3.0.0-beta.66
392
+
393
+ ### Patch Changes
394
+
395
+ - @robota-sdk/agent-core@3.0.0-beta.66
396
+
397
+ ## 3.0.0-beta.65
398
+
399
+ ### Patch Changes
400
+
401
+ - @robota-sdk/agent-core@3.0.0-beta.65
402
+
403
+ ## 3.0.0-beta.64
404
+
405
+ ### Patch Changes
406
+
407
+ - @robota-sdk/agent-core@3.0.0-beta.64
408
+
409
+ ## 3.0.0-beta.63
410
+
411
+ ### Patch Changes
412
+
413
+ - @robota-sdk/agent-core@3.0.0-beta.63
414
+
415
+ ## 3.0.0-beta.62
416
+
417
+ ### Patch Changes
418
+
419
+ - Updated dependencies
420
+ - @robota-sdk/agent-core@3.0.0-beta.62
421
+
422
+ ## 3.0.0-beta.61
423
+
424
+ ### Minor Changes
425
+
426
+ - e243fb0: Add provider-neutral sandbox execution ports, E2B-compatible sandbox adapter, and SDK sandbox injection for Bash and core file tools.
427
+ - 18fcc5b: Add provider-neutral sandbox snapshot hydration for interactive sessions. Snapshot-capable sandbox clients now persist `sandboxSnapshotId` on shutdown and restore it before saved message replay on non-fork resume, while the E2B structural adapter supports both `createSnapshot()`-style checkpoints and pause/resume sandbox references.
428
+ - 3bde012: Add provider-neutral sandbox workspace manifests and wire `InteractiveSession` to apply them before session creation.
429
+
430
+ ### Patch Changes
431
+
432
+ - d97bdf2: Add provider-owned model catalog metadata, route `/model` suggestions through the active provider, and make `cli:dev` resolve the CLI workspace dependency closure through source export conditions.
433
+ - Updated dependencies [1c0d44c]
434
+ - Updated dependencies [36eb7a9]
435
+ - Updated dependencies [d97bdf2]
436
+ - @robota-sdk/agent-core@3.0.0-beta.61
437
+
438
+ ## 3.0.0-beta.60
439
+
440
+ ### Patch Changes
441
+
442
+ - Updated dependencies [7439391]
443
+ - @robota-sdk/agent-core@3.0.0-beta.60
444
+
445
+ ## 3.0.0-beta.59
446
+
447
+ ### Patch Changes
448
+
449
+ - 95721ff: Preserve existing target mode bits during atomic Write and Edit file replacements.
450
+ - @robota-sdk/agent-core@3.0.0-beta.59
451
+
452
+ ## 3.0.0-beta.58
453
+
454
+ ### Patch Changes
455
+
456
+ - Updated dependencies
457
+ - @robota-sdk/agent-core@3.0.0-beta.58
458
+
459
+ ## 3.0.0-beta.57
460
+
461
+ ### Patch Changes
462
+
463
+ - 822a78b: Add self-hosting verification planning and atomic UTF-8 writes for built-in file mutation tools.
464
+ - Updated dependencies [16c3b6f]
465
+ - Updated dependencies [f61e2cb]
466
+ - @robota-sdk/agent-core@3.0.0-beta.57
467
+
468
+ ## 3.0.0-beta.56
469
+
470
+ ### Patch Changes
471
+
472
+ - Updated dependencies
473
+ - @robota-sdk/agent-core@3.0.0-beta.56
474
+
475
+ ## 3.0.0-beta.55
476
+
477
+ ### Patch Changes
478
+
479
+ - 38a72bf: fix: resolve ESLint tsconfig parsing errors and improve pnpm CI reliability
480
+ - Add tsconfig.eslint.json to all packages for per-package ESLint runs
481
+ - Migrate typecheck from pnpm -r exec tsc to per-package typecheck scripts
482
+ - Add --if-present to all recursive pnpm run scripts
483
+ - Fix React type imports, dynamic imports in tests, Express.Multer types
484
+
485
+ - Updated dependencies [38a72bf]
486
+ - @robota-sdk/agent-core@3.0.0-beta.55
487
+
488
+ ## 3.0.0-beta.54
489
+
490
+ ### Patch Changes
491
+
492
+ - Updated dependencies
493
+ - @robota-sdk/agent-core@3.0.0-beta.54
494
+
495
+ ## 3.0.0-beta.53
496
+
497
+ ### Patch Changes
498
+
499
+ - refactor: monolith decomposition — all agent-\* files under 300 lines
500
+ - Updated dependencies
501
+ - Updated dependencies
502
+ - @robota-sdk/agent-core@3.0.0-beta.53
503
+
504
+ ## 3.0.0-beta.52
505
+
506
+ ### Patch Changes
507
+
508
+ - @robota-sdk/agent-core@3.0.0-beta.52
509
+
510
+ ## 3.0.0-beta.51
511
+
512
+ ### Patch Changes
513
+
514
+ - @robota-sdk/agent-core@3.0.0-beta.51
515
+
516
+ ## 3.0.0-beta.50
517
+
518
+ ### Patch Changes
519
+
520
+ - fix: reinsert repository/homepage/bugs in correct field order
521
+ - @robota-sdk/agent-core@3.0.0-beta.50
522
+
523
+ ## 3.0.0-beta.49
524
+
525
+ ### Patch Changes
526
+
527
+ - fix: add repository, homepage, bugs metadata to all publishable packages
528
+ - @robota-sdk/agent-core@3.0.0-beta.49
529
+
530
+ ## 3.0.0-beta.48
531
+
532
+ ### Patch Changes
533
+
534
+ - @robota-sdk/agent-core@3.0.0-beta.48
535
+
536
+ ## 3.0.0-beta.47
537
+
538
+ ### Patch Changes
539
+
540
+ - @robota-sdk/agent-core@3.0.0-beta.47
541
+
542
+ ## 3.0.0-beta.46
543
+
544
+ ### Patch Changes
545
+
546
+ - @robota-sdk/agent-core@3.0.0-beta.46
547
+
548
+ ## 3.0.0-beta.45
549
+
550
+ ### Patch Changes
551
+
552
+ - @robota-sdk/agent-core@3.0.0-beta.45
553
+
554
+ ## 3.0.0-beta.44
555
+
556
+ ### Patch Changes
557
+
558
+ - Updated dependencies
559
+ - @robota-sdk/agent-core@3.0.0-beta.44
@@ -615,6 +615,7 @@ var OsSandboxClient = class {
615
615
  baseline = [];
616
616
  /** Entries a clean-up could not restore, with the state they must return to. */
617
617
  unresolved = /* @__PURE__ */ new Map();
618
+ watchers = /* @__PURE__ */ new Set();
618
619
  constructor(options) {
619
620
  this.root = realPathOrSelf(options.root);
620
621
  this.availability = options.availability;
@@ -631,12 +632,23 @@ var OsSandboxClient = class {
631
632
  active: this.current.enabled && this.availability.executable !== void 0
632
633
  };
633
634
  }
634
- /** Change the settings for the next command. */
635
+ /** Change the settings for the next command, and tell whoever watches them. */
635
636
  configure(settings) {
636
637
  this.current = {
637
638
  ...this.current,
638
639
  ...settings
639
640
  };
641
+ for (const watcher of this.watchers) watcher(this.current);
642
+ }
643
+ /**
644
+ * Be told the settings after each change, for as long as the returned function is not called. A
645
+ * copy of this sandbox in another process (a subagent's) follows the user's change this way.
646
+ */
647
+ watchSettings(watcher) {
648
+ this.watchers.add(watcher);
649
+ return () => {
650
+ this.watchers.delete(watcher);
651
+ };
640
652
  }
641
653
  /** Whether `shellCommand` would run confined. */
642
654
  confines(shellCommand) {
@@ -1942,60 +1954,48 @@ async function readFileTool(args, options) {
1942
1954
  }
1943
1955
  const pathError = checkPathWithinCwd(filePath, options.cwd);
1944
1956
  if (pathError !== void 0) return pathError;
1945
- let fileStats;
1957
+ const failure = (error) => JSON.stringify({
1958
+ success: false,
1959
+ output: "",
1960
+ error
1961
+ });
1962
+ let handle;
1946
1963
  try {
1947
- fileStats = await (0, node_fs_promises.stat)(filePath);
1964
+ handle = await (0, node_fs_promises.open)(filePath, node_fs.constants.O_RDONLY | node_fs.constants.O_NONBLOCK, 384);
1948
1965
  } catch (err) {
1949
- const result = {
1950
- success: false,
1951
- output: "",
1952
- error: `File not found: ${filePath}`
1953
- };
1954
- return JSON.stringify(result);
1955
- }
1956
- if (!fileStats.isFile()) {
1957
- const result = {
1958
- success: false,
1959
- output: "",
1960
- error: `Path is not a file: ${filePath}`
1961
- };
1962
- return JSON.stringify(result);
1966
+ const found = await (0, node_fs_promises.stat)(filePath).catch(() => void 0);
1967
+ if (found === void 0) return failure(`File not found: ${filePath}`);
1968
+ if (!found.isFile()) return failure(`Path is not a file: ${filePath}`);
1969
+ return failure(err instanceof Error ? err.message : String(err));
1963
1970
  }
1964
1971
  let buffer = Buffer.alloc(0);
1965
1972
  let binaryFile = false;
1966
1973
  try {
1967
- const handle = await (0, node_fs_promises.open)(filePath, "r");
1968
- try {
1969
- const chunks = [];
1970
- const chunk = Buffer.allocUnsafe(READ_CHUNK_BYTES$2);
1971
- let bytes = 0;
1972
- let binaryCheckedBytes = 0;
1973
- while (bytes <= MAX_READ_BYTES) {
1974
- if (options.signal?.aborted) throw new ReadCancelledError();
1975
- const { bytesRead } = await handle.read(chunk, 0, Math.min(chunk.length, 4194305 - bytes), null);
1976
- if (bytesRead === 0) break;
1977
- const binaryCheckLength = Math.min(bytesRead, 8192 - binaryCheckedBytes);
1978
- if (binaryCheckLength > 0 && isBinary(chunk.subarray(0, binaryCheckLength))) {
1979
- binaryFile = true;
1980
- break;
1981
- }
1982
- binaryCheckedBytes += binaryCheckLength;
1983
- bytes += bytesRead;
1984
- if (bytes > MAX_READ_BYTES) throw new ReadByteLimitError("input");
1985
- chunks.push(Buffer.from(chunk.subarray(0, bytesRead)));
1974
+ if (!(await handle.stat()).isFile()) return failure(`Path is not a file: ${filePath}`);
1975
+ const chunks = [];
1976
+ const chunk = Buffer.allocUnsafe(READ_CHUNK_BYTES$2);
1977
+ let bytes = 0;
1978
+ let binaryCheckedBytes = 0;
1979
+ while (bytes <= MAX_READ_BYTES) {
1980
+ if (options.signal?.aborted) throw new ReadCancelledError();
1981
+ const { bytesRead } = await handle.read(chunk, 0, Math.min(chunk.length, 4194305 - bytes), null);
1982
+ if (bytesRead === 0) break;
1983
+ const binaryCheckLength = Math.min(bytesRead, 8192 - binaryCheckedBytes);
1984
+ if (binaryCheckLength > 0 && isBinary(chunk.subarray(0, binaryCheckLength))) {
1985
+ binaryFile = true;
1986
+ break;
1986
1987
  }
1987
- if (!binaryFile) buffer = Buffer.concat(chunks, bytes);
1988
- } finally {
1989
- await handle.close();
1988
+ binaryCheckedBytes += binaryCheckLength;
1989
+ bytes += bytesRead;
1990
+ if (bytes > MAX_READ_BYTES) throw new ReadByteLimitError("input");
1991
+ chunks.push(Buffer.from(chunk.subarray(0, bytesRead)));
1990
1992
  }
1993
+ if (!binaryFile) buffer = Buffer.concat(chunks, bytes);
1991
1994
  } catch (err) {
1992
1995
  if (err instanceof ReadByteLimitError || err instanceof ReadCancelledError) throw err;
1993
- const result = {
1994
- success: false,
1995
- output: "",
1996
- error: err instanceof Error ? err.message : String(err)
1997
- };
1998
- return JSON.stringify(result);
1996
+ return failure(err instanceof Error ? err.message : String(err));
1997
+ } finally {
1998
+ await handle.close();
1999
1999
  }
2000
2000
  if (options.signal?.aborted) throw new ReadCancelledError();
2001
2001
  if (binaryFile) {