@robota-sdk/agent-command 3.0.0-beta.82 → 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.
Files changed (50) hide show
  1. package/CHANGELOG.md +1044 -0
  2. package/dist/node/index.cjs +76 -70
  3. package/dist/node/index.d.cts +100 -8
  4. package/dist/node/index.d.cts.map +1 -1
  5. package/dist/node/index.d.ts +100 -8
  6. package/dist/node/index.d.ts.map +1 -1
  7. package/dist/node/index.js +76 -70
  8. package/dist/node/index.js.map +1 -1
  9. package/package.json +14 -9
  10. package/src/command-module-utils.test.ts +37 -0
  11. package/src/command-module-utils.ts +2 -0
  12. package/src/default/__tests__/default-command-modules.test.ts +50 -11
  13. package/src/default/__tests__/model-exposure.test.ts +16 -3
  14. package/src/default/__tests__/org-policy-forwarding.test.ts +10 -2
  15. package/src/default/default-command-modules.ts +7 -8
  16. package/src/devices/__tests__/devices-command-module.test.ts +99 -12
  17. package/src/devices/devices-command-module.ts +108 -24
  18. package/src/devices/devices-command-port.ts +62 -1
  19. package/src/devices/index.ts +3 -0
  20. package/src/doctor/doctor-node-deps.ts +6 -7
  21. package/src/editor/editor-command-module.ts +5 -0
  22. package/src/events/__tests__/events-command.test.ts +73 -0
  23. package/src/events/events-command-module.ts +49 -0
  24. package/src/events/events-command.ts +69 -0
  25. package/src/events/index.ts +2 -0
  26. package/src/git/__tests__/git-command-module.test.ts +10 -2
  27. package/src/handoff/__tests__/handoff-command-module.test.ts +2 -1
  28. package/src/handoff/handoff-command-module.ts +1 -1
  29. package/src/handoff/handoff-command.ts +2 -2
  30. package/src/help/__tests__/help-command-module.test.ts +3 -0
  31. package/src/help/__tests__/help-command.test.ts +3 -0
  32. package/src/index.ts +8 -0
  33. package/src/keybindings/keybindings-command-module.ts +17 -3
  34. package/src/mcp-activation/__tests__/mcp-activation-command.test.ts +41 -0
  35. package/src/mcp-activation/mcp-activation-command.ts +8 -3
  36. package/src/peers/__tests__/peers-command-module.test.ts +24 -0
  37. package/src/peers/__tests__/peers-command.test.ts +78 -0
  38. package/src/peers/peers-command-module.ts +1 -1
  39. package/src/peers/peers-command.ts +33 -4
  40. package/src/rewind/__tests__/rewind-command-module.test.ts +31 -1
  41. package/src/rewind/rewind-command.ts +23 -2
  42. package/src/schedule/__tests__/schedule-redos.test.ts +33 -0
  43. package/src/schedule/loop-command.ts +3 -1
  44. package/src/shell/shell-command-module.ts +5 -0
  45. package/src/terminal-client/__tests__/terminal-client-commands.test.ts +216 -0
  46. package/src/terminal-client/index.ts +5 -0
  47. package/src/terminal-client/terminal-client-commands.ts +109 -0
  48. package/src/theme/__tests__/theme-command.test.ts +1 -1
  49. package/src/theme/theme-command-module.ts +12 -2
  50. package/src/user-local/__tests__/user-local-command.test.ts +2 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,1044 @@
1
+ # @robota-sdk/agent-command
2
+
3
+ ## 3.0.0-beta.83
4
+
5
+ ### Minor Changes
6
+
7
+ - 724fabb: External-event grants are given at start, carried exactly to a background session, listed without their
8
+ principal, and revoked by the owner.
9
+
10
+ - `agent-framework` (breaking) — `openExternalEventSource` and `ExternalEventIngress.open` take only
11
+ `{ grant, audit? }`: the session builds each grant's verifier from `grant.verifier` with the
12
+ `externalEventVerifierFactory` its host passed when the session was built (without one, no grant opens), and an
13
+ open that supplies a verifier or a factory is refused. `IExternalEventSource.revoke()` stops the grant's queued and running turns,
14
+ refuses its later events as `grant-revoked`, and keeps the label from being opened again. A submission the
15
+ session refuses is `shutting-down` only while it shuts down, and `session-unavailable` otherwise. New command
16
+ host adapter `externalEvents` (`ICommandExternalEventsAdapter`).
17
+ - `agent-ui-terminal` — forwards `externalEventVerifierFactory` from the render options to the session.
18
+ - `agent-interface-transport` — `TExternalEventRefusal` gains `session-unavailable`.
19
+ - `agent-command` — `/events` lists the session's grants (label, principal kind, state, counts) and
20
+ `/events revoke <grant-id>` withdraws one. User-only.
21
+ - `agent-cli` — a grant file (`grantId`, `issuer`, `resource` ending in `/events/<grantId>`, exactly one of
22
+ `subject` or `client`, `scopes`, optional `algorithms` and `rate`) is validated before anything starts, with a
23
+ reason that names the grant and no configured value. `robota --external-event-grant <file>` (TUI) and
24
+ `robota session start --background --external-event-grant <file>` open every grant or fail the start; a
25
+ background session receives its grants through a private file, opens them before it reports ready, and the
26
+ launcher refuses a readiness that names other grants. `robota session events list <id> [--json]` and
27
+ `robota session events revoke <id> <grant-id>` work over the generation-bound control socket, and
28
+ `robota session list --format json` shows each grant's counts. The retired `--external-event-allow` now points
29
+ at `--external-event-grant`.
30
+
31
+ - 28fa8a7: `/devices add` and `/devices join` enrol a new device into your devices.
32
+
33
+ - On a device that holds the signing key, `/devices add` shows a one-time code on the terminal. It
34
+ works once, for five minutes.
35
+ - On the new device, `/devices join [name]` asks for the code on the terminal, creates the device's
36
+ keys, and meets the other device through the signaling relay in
37
+ `transports.webrtc.options.relayUrl`. Both devices need that setting.
38
+ - The new device proves the code over the WebRTC connection's DTLS fingerprints before anything else
39
+ crosses, so a relay in the middle cannot enrol anyone.
40
+ - Both devices then show the same six digits, and both operators compare them and confirm. Someone
41
+ who saw the code cannot choose the digits: the new device commits to its part before it sees the
42
+ existing device's.
43
+ - Only when both operators say yes does the existing device certify the new one and issue a new
44
+ roster. The new device keeps its identity only once the chain it receives verifies.
45
+ - A wrong, expired or already used code is refused. So is a code after a few failed attempts. When
46
+ either operator declines, nothing is issued or kept.
47
+ - The code appears only on the two terminals. It never reaches history, transcripts or the model. A
48
+ code typed as a command argument is refused. Both commands stay user-only and refuse remote
49
+ surfaces.
50
+ - `agent-remote-pairing`: enrollment codes, the enrollment proof, the signed request with its
51
+ commitment, the short authentication string and the frame decoder.
52
+ - `agent-transport-webrtc`: `dialEnrollment` and `listenForEnrollment` provide a data channel bound to
53
+ the negotiated fingerprints.
54
+
55
+ - 4f49d14: The device mesh can be turned on, and `/peers`, `/handoff` and `/devices` reach the user's other devices.
56
+
57
+ - New user setting `transports.mesh.enabled` (default `false`). When it is `true`, an interactive
58
+ session opens the device mesh at startup and closes it on exit. Only the user settings count; a
59
+ project's settings cannot turn it on. Print and serve runs never open it, and a device without an
60
+ identity is told to run `/devices init`. Only one session of a device opens it at a time; another
61
+ session on the same device says so in `/devices` and links nothing.
62
+ - By default a linked device may send messages, files and sessions. Each file and each session still
63
+ waits for the operator's yes on this machine's terminal; with no terminal the answer is no.
64
+ Delegating, observing and driving stay off unless `transports.mesh.options.capabilities` lists
65
+ them.
66
+ - A message from a linked device arrives like one from a session on this host: a peer turn with no
67
+ authority, attributed to the device the handshake proved, under the same rate limit and
68
+ conversation limits.
69
+ - `/peers` lists linked devices beside the sessions on this host; `/peers send` and
70
+ `/peers send-file` take a device id. `/handoff` lists linked devices and pushes the session to one.
71
+ Files arrive in the usual place aside, and sessions are saved without starting.
72
+ - `/devices` shows whether the mesh is on, how this device finds the others, and which are linked.
73
+ - The mesh uses the relay at `transports.webrtc.options.relayUrl` when one is set. Without one it
74
+ finds devices on the local network and over the public ways in `transports.mesh.options`.
75
+ - `/peers`, `/handoff` and `/devices` stay user-only. Their descriptions tell the model they cover
76
+ linked devices.
77
+ - `agent-framework`: the `/peers` port can list linked devices (`listDevices`, `ILinkedDeviceSummary`).
78
+ - `agent-command`: the `/devices` port can report the mesh (`meshStatus`, `IDevicesMeshStatus`).
79
+
80
+ - 7b72344: Every client can offer the session's commands and skills and show its status. The GUI uses them as a
81
+ desktop-style composer.
82
+
83
+ - `agent-interface-session` is **`major` because `IInteractiveSession` gains required members**:
84
+ `ISessionCommands.listSkills()` and a `statusRead` capability, `getStatusSnapshot()`. The snapshot
85
+ holds session, model, permission mode, effort, context and goal. An external implementation stops
86
+ compiling until it adds both.
87
+ - `agent-transport` is **`major` for the same reason on `IProtocolSession`**. It also gains the wire
88
+ messages `get-commands` → `commands` and `get-status` → `session_status`. Both are reads that the
89
+ observe role may send.
90
+ - `agent-interface-command` gains `ICommandSkillListEntry`, moved from `agent-framework`, which
91
+ re-exports it unchanged.
92
+ - `agent-ui-web` is **`major` because its state changes shape**:
93
+ - A command's outcome and a finished turn's tool calls are now conversation entries: `messages` is
94
+ `TConversationEntry[]`.
95
+ - The `uiIntentNotices` list, `dismissUiIntentNotice`, `applyUiIntentEvent`,
96
+ `removeUiIntentNotice` and `IUiIntentNotice` are removed. An intent now answers with an info
97
+ line in the conversation.
98
+ - Session notices keep only `session-error` and `protocol-error`.
99
+ - The state gains `commandCatalog` and `sessionStatus`.
100
+ - `agent-command` registers `/theme` and `/keybindings` even without a terminal. They then answer
101
+ that they belong to the robota terminal, instead of being unknown.
102
+ - `agent-framework`:
103
+ - `InteractiveSession.getStatusSnapshot()`.
104
+ - The main-thread row previews the last chat message instead of the last record's type.
105
+ - `agent-cli` serves the full GUI web app, renamed from `agent-cli-web` to `agent-gui-web`, on
106
+ `robota --serve --open`.
107
+
108
+ - 9721162: The command catalog says who runs a command, and which surfaces can run it.
109
+
110
+ - **`agent-interface-command` (major).**
111
+ - Adds `TCommandRunner` (`'runtime' | 'client'`) and `TCommandSurface` (`'terminal' | 'gui'`).
112
+ - `ICommand` gains optional `runner` and `surfaces`.
113
+ - `ICommandListEntry` gains optional `surfaces` and a **required** `runner`. An implementation of `listCommands()`, or any code that constructs an `ICommandListEntry`, must now emit `runner`; a command that declares none gets `'runtime'`.
114
+ - **`agent-framework` (minor).**
115
+ - `ISystemCommand` gains optional `runner` and `surfaces`, and the command listing carries both.
116
+ - An undeclared runner resolves to `'runtime'`.
117
+ - `SessionTerminalHandoffGate` is exported for a terminal client that hands its own terminal to a command.
118
+ - **`agent-command` (minor).**
119
+ - `/shell`, `/editor`, `/theme` and `/keybindings` declare `runner: 'client'` and `surfaces: ['terminal']`.
120
+ - `createTerminalClientCommands()` builds the same four commands, from the same execute functions, for a terminal client to run itself. The set follows the preset's module selection.
121
+ - **`agent-ui-web` (minor).** The `/` menu marks a command that runs in the terminal with a "terminal" badge.
122
+
123
+ ### Patch Changes
124
+
125
+ - 4241fc5: Device mesh follow-ups.
126
+
127
+ - A device with no identity is pointed to `/devices add` on one of the user's devices and `/devices join`
128
+ here, as well as to `/devices init`. The message says that `init` is for the first device only,
129
+ because it creates a separate identity that can never link to the user's other devices. The `/devices`
130
+ description and the `init` subcommand say the same.
131
+ - An identity created mid-session (`/devices init`, or a successful `/devices join`) opens the mesh
132
+ without a restart when `transports.mesh.enabled` is on.
133
+ - If a session stalls for longer than the mesh lock's stale window (for example while the machine
134
+ sleeps), another session can take the mesh over. The stalled session now notices this on its next
135
+ lock refresh, closes its own mesh, and says why. Two sessions no longer run it together.
136
+ `holdExclusiveFileLock` has a new `onLost` option for this.
137
+ - `/peers` and `/handoff` still list and reach linked mesh devices when local same-host peer discovery
138
+ fails. `/peers` says why sessions on this host are not listed. `ICommandLocalPeersAdapter` has a new
139
+ optional `localDiscoveryOff` field for this.
140
+ - `agent-transport-webrtc`: when lists become newer on a node (reissued, revoked, or adopted from a
141
+ peer), the node sends them over every admitted connection instead of waiting for the next handshake.
142
+ Reissues, revocations and enrolments in the CLI take effect this way at once. A receiver adopts a
143
+ pushed list only if it is newer, issued by this user's signing key, and verifies. The push does not
144
+ depend on the peer's capabilities, and it never reaches the application's message handlers.
145
+
146
+ - 57280bf: Every published package now declares `"engines": { "node": ">=22.12.0" }`. Before, 27 of the 38
147
+ packages declared no floor (`agent-core`, `agent-tools` and every provider among them),
148
+ `agent-session` and `agent-file-authority` declared `>=20.19.0`, and the other nine declared
149
+ `>=22.0.0`, so a consumer on Node 20 saw at most a warning from a transitive dependency.
150
+
151
+ Why 22.12: `agent-cli` and `agent-ui-terminal` need Node 22 through `ink` 7, and the CommonJS entries
152
+ of `agent-tools` and its dependents, `agent-transport`/`node` and its dependents, and
153
+ `agent-ui-terminal` `require()` ESM-only dependencies (`p-limit`, `jose`, `chalk`), which Node 22
154
+ supports unflagged only from 22.12. `engines` is advisory unless the consumer enables `engine-strict`.
155
+
156
+ No code changes: `tsdown` now reads `node22.12.0` as its build target from the field.
157
+
158
+ - 18c0d5c: Every published package now exports `./package.json`, so `require('<package>/package.json')` and
159
+ `import('<package>/package.json', { with: { type: 'json' } })` work instead of failing with
160
+ `ERR_PACKAGE_PATH_NOT_EXPORTED`, and each tarball now ships the package's `CHANGELOG.md`.
161
+ - caaab20: `/rewind` works again. Every session the CLI builds for a trusted workspace captures edit
162
+ checkpoints; before, none did, and `/rewind` failed everywhere with "Edit checkpoints require
163
+ project authority."
164
+
165
+ - `agent-cli` (patch): a trusted workspace composes an edit checkpoint store for the terminal UI, a
166
+ print run, a served runtime and each session a daemon keeps live. Every session gets its own
167
+ store, pooled or switched to in the terminal UI, since two can run turns at the same time. On a host that cannot prove a project write
168
+ stays inside the project (every platform but Linux), none is composed: a checkpoint could be
169
+ neither saved nor restored there.
170
+ - `agent-command` (patch): where a session has no checkpoints, `/rewind` says why: a restricted
171
+ workspace is told to run `robota trust --yes` and restart robota, and a host that cannot write
172
+ the project safely says so. `/rewind list` reports this as a failed command instead of throwing.
173
+ - `agent-framework` (minor): a checkpoint operation on a session without a store throws
174
+ `EditCheckpointsUnavailableError`, whose `reason` is `host-cannot-write-project`,
175
+ `restricted-workspace` or `no-checkpoint-store`. It is a `WorkspaceAuthorityRequiredError`, as
176
+ before, named `EditCheckpointsUnavailableError`. `HeadlessInteractionChannel` takes an
177
+ `editCheckpointStore` option.
178
+ - `agent-ui-terminal` (minor): `renderApp` takes `createEditCheckpointStore` in place of
179
+ `editCheckpointStore`, and builds each session its own store, since a session switch can build the
180
+ next session before the old one's turn ends. `TuiInteractionChannel` keeps `editCheckpointStore`:
181
+ a channel runs one session.
182
+
183
+ - Updated dependencies [3c81769]
184
+ - Updated dependencies [724fabb]
185
+ - Updated dependencies [997f2fb]
186
+ - Updated dependencies [bfe8ed5]
187
+ - Updated dependencies [e689c8e]
188
+ - Updated dependencies [4241fc5]
189
+ - Updated dependencies [4f49d14]
190
+ - Updated dependencies [7b72344]
191
+ - Updated dependencies [6ae3f28]
192
+ - Updated dependencies [9721162]
193
+ - Updated dependencies [be0e53c]
194
+ - Updated dependencies [57f57f5]
195
+ - Updated dependencies [ba822c1]
196
+ - Updated dependencies [9721162]
197
+ - Updated dependencies [6e6b06b]
198
+ - Updated dependencies [7d77ce4]
199
+ - Updated dependencies [8bd5fac]
200
+ - Updated dependencies [57280bf]
201
+ - Updated dependencies [5033dd9]
202
+ - Updated dependencies [18c0d5c]
203
+ - Updated dependencies [dbd888d]
204
+ - Updated dependencies [1887e54]
205
+ - Updated dependencies [caaab20]
206
+ - Updated dependencies [f01868f]
207
+ - Updated dependencies [e8779c9]
208
+ - Updated dependencies [5a0ee96]
209
+ - @robota-sdk/agent-framework@3.0.0-beta.83
210
+ - @robota-sdk/agent-core@3.0.0-beta.83
211
+ - @robota-sdk/agent-interface-session@3.0.0-beta.83
212
+ - @robota-sdk/agent-interface-command@3.0.0-beta.83
213
+ - @robota-sdk/agent-interface-execution@3.0.0-beta.83
214
+ - @robota-sdk/agent-preset@3.0.0-beta.83
215
+
216
+ ## 3.0.0-beta.82
217
+
218
+ ### Minor Changes
219
+
220
+ - c7f9203: Connected sessions can send each other files.
221
+
222
+ - `/peers send-file <session-id> <path>` sends a copy of any file the operator can read to another
223
+ live session on this host.
224
+ - The model sends a file only through the `peer_send_file` tool. Every call asks the user, showing
225
+ the path, size, hash and destination; no permission mode, rule or remembered consent answers it.
226
+ The tool reaches only files inside the workspace whose path does not look like it holds secrets
227
+ (`.env*`, `~/.ssh`, keys and credentials), and it does not exist in a turn a peer's message started.
228
+ - The receiving operator approves every file. A received file is kept as an inert copy (mode 0600)
229
+ under `~/.robota/peer-files/<sender>/`. It is never run and never placed in the model's context.
230
+ The conversation is told only its name, size and sha256. A name that leaves that directory is
231
+ refused, a symbolic link is never written through, and nothing is overwritten.
232
+ - Transfers travel on a channel of their own (a separate connection on this host, a separate data
233
+ channel between devices), in chunks the receiver paces, up to 32 MiB, and are kept only when the
234
+ whole content matches the offered sha256. A transfer that ends early is discarded; there is no
235
+ resume.
236
+
237
+ **API**
238
+
239
+ - `agent-interface-session-mobility`: the `file` capability, which asks the operator for every
240
+ request; `ConnectionAuthority.authorizeFile`; `IFileOffer` and `IFileFrameChannel`.
241
+ - `agent-transport/node`: `sendFileOverChannel` and `receiveFileOverChannel`, the carrier over any
242
+ `IFileFrameChannel`; `DEFAULT_MAX_FILE_BYTES`.
243
+ - `agent-transport-webrtc`: `IDeviceMeshLink.openFileChannel` and `onFileChannel`.
244
+ - `agent-remote-pairing`: `file` joins `DEVICE_CAPABILITIES`. A device certificate that names it is
245
+ refused as malformed by an earlier version.
246
+ - `agent-core`: `IToolPermissionProfile.notInPeerTurn` withholds a tool from a turn a peer's message
247
+ started.
248
+ - `agent-framework`: `ICommandLocalPeersAdapter.prepareFile`.
249
+
250
+ - 004fe7f: `/handoff` moves a session over a real connection.
251
+
252
+ - `/handoff <session-id>` pushes this conversation to another Robota session on this machine. The
253
+ same carrier also runs between two of the user's devices over their mesh connection; no command
254
+ opens that connection yet.
255
+ - A hand-off is push-only: only the operator of the session that holds it starts one. A session or
256
+ device that asks another for its session is refused.
257
+ - The source signs a grant for that one transfer over that one channel with its device key, so a
258
+ hand-off needs the device identity from `/devices init`. The receiving side checks it against the
259
+ sender's certificate, then asks its own operator; without a yes nothing is sent.
260
+ - The session travels on the file-transfer carrier, is kept aside until it matches the manifest, and
261
+ is saved without being started. The operator there resumes it with `robota --resume <id>`.
262
+ - The source gives the session up, and ends, only once the receiving side confirms it saved it.
263
+ Every other outcome leaves the session where it was; if the confirmation is lost, `/handoff` to the
264
+ same session again resends the same transfer, which the receiver settles without saving it twice.
265
+ Peer attribution (`driverId`, `turnSource`)
266
+ travels with it.
267
+ - `/handoff` stays user-only. Its description tells the model to suggest the command to the user.
268
+ - `agent-transport-webrtc`: an admitted mesh link exposes the DTLS fingerprints it is bound to, and
269
+ `judgeHandoffGrant` is exported.
270
+ - `agent-interface-session-mobility`: a hand-off carrier may move the sealed payload whole
271
+ (`sendPayload`), the destination verifies it with `receivePayload`, and the source can report its
272
+ open transfer (`status`) and abandon it for any refusal the destination names.
273
+
274
+ ### Patch Changes
275
+
276
+ - e15e22b: Two of one user's devices can connect to each other over WebRTC, admitted by the device handshake.
277
+
278
+ - `agent-transport-webrtc` — `DeviceMeshNode` keeps one connection per device pair, in either role: the
279
+ device with the lower id offers and the other answers, so concurrent attempts resolve by rule. Both
280
+ roles bind the handshake to the certificate the DTLS layer verified and take one remote description
281
+ with exactly one fingerprint; before admission only handshake frames cross, and a connection counts
282
+ as admitted only once both sides admitted each other. Each connection gets its own DTLS certificate
283
+ (`createDtlsKeys`). A new attempt never displaces an admitted connection until it is admitted
284
+ itself, and attempts per pair are paced. Lists adopted in a handshake, or handed over through
285
+ `refresh`, apply at once, and a device they revoke loses its connection. Signaling runs through
286
+ `IMeshRelay`: `WsMeshRelayClient` for the self-hosted relay's `presence` / `message` frames
287
+ (topics capped per source and relay-wide), `createInMemoryMeshRelayHub` for tests.
288
+ - `agent-remote-pairing` — the pair's two relay inbox topics, one per direction, come from their
289
+ pairwise secret (`derivePairRendezvous(...).relayInbox()`).
290
+ - `agent-cli` — a device holding the signing key reissues its roster and revocation list before they
291
+ expire while an interactive session runs. The host can open this device's mesh endpoint from the
292
+ identity under `~/.robota/devices`, saving newer lists a peer hands over; no command starts it yet.
293
+ - `agent-command` — `/devices add|join` still reports that enrolment is not available yet.
294
+
295
+ - Updated dependencies [c7f9203]
296
+ - @robota-sdk/agent-core@3.0.0-beta.82
297
+ - @robota-sdk/agent-framework@3.0.0-beta.82
298
+ - @robota-sdk/agent-interface-command@3.0.0-beta.82
299
+ - @robota-sdk/agent-interface-execution@3.0.0-beta.82
300
+ - @robota-sdk/agent-interface-session@3.0.0-beta.82
301
+ - @robota-sdk/agent-preset@3.0.0-beta.82
302
+
303
+ ## 3.0.0-beta.81
304
+
305
+ ### Minor Changes
306
+
307
+ - 44fc732: Add `/devices` for this device's identity among the user's devices: `list`, `init` (creates the identity: a recovery phrase, the master-certified signing key, this device's keys and certificate, the first roster and revocation list), `revoke <device-id>` (signing key only, no phrase; confirmed at the terminal) and `recover` (rotates the signing key from the phrase and revokes the old ones). Enrolling another device (`add` / `join`) comes with the device connection.
308
+
309
+ The recovery phrase never enters the session: it is shown once and read with no echo on the controlling terminal (opened apart from the session's input), on the alternate screen that is cleared afterwards, and never reaches prompt history, conversation history, transcripts, traces or the model. Without an interactive terminal the phrase commands refuse. `/devices` is operator-only: never model-invocable and refused from remote surfaces. Private keys live in the host credential store; certificates, roster, revocation lists and sequence marks are kept in owner-only files under `~/.robota/devices`.
310
+
311
+ `agent-remote-pairing` adds `isRecoveryPhraseWord`, so a phrase can be checked one word at a time.
312
+
313
+ - 18b52cc: Built-in commands are offered to the model deliberately, each described for the model, and never
314
+ with a trust, credential or permission-widening action.
315
+
316
+ - `agent-interface-command` — `ICommand` gains `modelDescription?` (what the model is told, beside the
317
+ short `/help` line), and `modelInvocable` on a subcommand entry now narrows what the model may run.
318
+ - `agent-framework` — `ISystemCommand` gains `modelDescription?` and `modelRequiresPermission?`.
319
+ Once any subcommand of a model-invocable command declares `modelInvocable`, the model may run only
320
+ the bare command and the subcommands declared `true`; everything else, including an alias or an
321
+ undeclared subcommand, is refused before the command runs. The model-facing descriptor lists only
322
+ that subset. A model-requested monitor's command is now decided by the shell tool's gate (its
323
+ Bash/Shell rules, the mode and the prompt) and a refusal rejects with the new
324
+ `MonitorCommandRefusedError`. The `scriptedSession` test harness accepts `permissions` patterns.
325
+ - `agent-session` — `Session.checkToolPermission(toolName, toolArgs)` decides an action that has a
326
+ tool's effect by another route: that tool's PreToolUse hooks, then the gate's rules, mode,
327
+ remembered consent and prompt — never the command sandbox's auto-approval, since the action does
328
+ not run inside the sandbox.
329
+ - `agent-framework` also: `ICommandMCPActivationAdapter.userActionSurface?` tells `/mcp` whether the
330
+ user can type a session command, so the model's status names the terminal sign-in otherwise.
331
+ - `agent-command` — `/context` (bare and `list`), `/cost` (the report, not `budget`) and `/mcp`
332
+ (`status` only, without a prompt) are now model-invocable; `/memory approve` and `/memory reject`
333
+ are now user-only; the model's `/monitor` is decided by the shell gate rather than by consent to
334
+ the command's name. Every model-invocable command carries a model-facing description. The model's
335
+ `/mcp status` shows only safe names, states and the command to suggest, and a caller that does not
336
+ identify itself gets that view. `/context list` accounts only for turns still in context. New
337
+ `mcpUserActionNotice`, `mcpUserActionCommand` and `mcpUnavailableServersNotice` build the fixed
338
+ notices.
339
+ - `agent-command-workflows` — `/workflows` carries a model-facing description.
340
+ - `agent-mcp` — `createDiscoveredTool` accepts `authFailureNotice`: a call the server refuses for
341
+ authentication returns that host text instead of the generic failure.
342
+ - `agent-cli` — an MCP server that did not start because the user must approve it, trust the
343
+ workspace or sign in is named to the model at the start of an interactive session with the
344
+ command to suggest, and a
345
+ signed-in OAuth server that refuses a call tells the model to suggest `/mcp login <server>` — or, in print and serve runs, the terminal
346
+ `robota mcp login <server>`.
347
+
348
+ A command whose bare form is a complete action declares `runsBare`, so choosing `/cost` or `/mcp`
349
+ from the autocomplete menu still runs it even though they now declare subcommands.
350
+
351
+ - 9843fe6: Sign in to a remote MCP server from inside a session, and use its tools without restarting.
352
+
353
+ - **`/mcp login <server> [--no-browser]`** runs the same per-server OAuth sign-in as
354
+ `robota mcp login` (discovery checks, PKCE, `state`, RFC 9207 `iss`, RFC 8707 resource, the
355
+ loopback listener, the lock-guarded store). It opens the browser through the argv opener; with
356
+ `--no-browser`, or when no browser can be opened, it shows the authorization URL and asks for the
357
+ redirect URL in the session's own prompt (masked), held to the same rules as a pasted redirect in
358
+ the terminal. A failed, refused, timed-out or cancelled sign-in changes nothing and is reported by
359
+ a fixed reason only. `/mcp login <server> --client-secret` is refused: a secret is never typed into
360
+ a session, and `robota mcp login <server> --client-secret` is named instead (also after a failed
361
+ token exchange for a pre-registered client). `/mcp` stays user-only (`modelInvocable: false`).
362
+ - **Connected in the same session:** after a sign-in, a server that could not connect for want of
363
+ one goes through the normal admission (approval, fingerprint, trust) and connects, and its tools
364
+ are offered from the next message; a server that was already connected is admitted again and
365
+ reconnects, and its authenticator drops what it held and reads the new credential. A tool left out
366
+ because the session already has its name is reported, and only tools actually added get
367
+ provenance. In browser mode the authorization URL is shown in the session prompt before the
368
+ browser opens, where the user can also choose to paste the redirect instead or cancel.
369
+ - **`runMCPOAuthLogin`** takes `readRedirectWhenBrowserFails`: when `openBrowser` rejects, the
370
+ loopback listener stops and the pasted redirect is read instead, for the same redirect URI. The
371
+ loopback listener's time limit now runs from its first `wait()` — once the authorization page is
372
+ handed over — so time spent at a prompt before the browser opens does not count against it.
373
+ - **`Session.addTools`** (and `ICommandSessionTools.addTools`) offers tools that became usable
374
+ mid-session, through the same permission gate and — via `ISessionOptions.wrapAddedTools`, which
375
+ `createSession` sets — the same edit-checkpoint and reversible-execution wraps as the assembled
376
+ tools. A name the session already has is left out, never replaced. Calls are serialized, and tools
377
+ added while a turn runs are applied when the next turn starts, so a turn's rounds all see one
378
+ tool list and the prompt cache misses once, at that boundary.
379
+ - **Breaking (`agent-framework`, major):** `addTools` is a new required member of the
380
+ `ICommandSessionTools` role port (and so of `ICommandSessionRuntime`). A host that implements the
381
+ port itself must add it; one that passes an `agent-session` `Session` already has it.
382
+ - **`ICommandMCPActivationAdapter.oauthLogin`** (`ICommandMCPOAuthLoginRequest`,
383
+ `ICommandMCPOAuthLoginResult`, `ICommandMCPOAuthRedirectPrompt`) is the port behind it.
384
+ - The sign-in notice and `/mcp status` now suggest `/mcp login <server>` in a session and
385
+ `robota mcp login <server>` in a terminal; the server's name is shown only when it is safe to paste
386
+ into any shell, otherwise `<server>`.
387
+
388
+ ### Patch Changes
389
+
390
+ - ec5e477: Add a credential store port (`ICredentialStore` in `agent-core`) and keep the CLI's secrets behind it: the OS keychain through the optional `@napi-rs/keyring` binding (macOS Keychain, Windows Credential Manager, Linux Secret Service), else an owner-only file under `~/.robota/credentials`. The backend is chosen at first use, recorded, and named by `/remote-control status`; a recorded keychain that stops working fails closed instead of degrading to the file.
391
+
392
+ The remote-control host identity key moves into the store. The old `~/.robota/remote-host-identity.json` may have been copied by backups or dotfile sync, so it is not carried over: on the first run after upgrading a new host key is generated, the old file is removed, and the operator is told once that trusted devices must pair again.
393
+
394
+ - 007fd90: Authority per connection: pairing stays with the local operator, and driving needs the operator's yes.
395
+
396
+ - `agent-interface-session-mobility` — `ConnectionAuthority` decides what one connection may do.
397
+ `presence` and `message` are allowed; `delegate` and `handoff` ask the receiving operator for every
398
+ request; `observe` and `drive` ask once per connection. With no `IOperatorApprover` the answer is
399
+ no. `authorizeDelegation` turns an approved task into a peer turn from where admission placed the
400
+ peer, ignoring anything else on the request, so the receiver's policy decides what it may do.
401
+ - `agent-transport-webrtc` — `connectionApproval` asks the operator before a connection reaches the
402
+ session, after every proof has run. Frames the peer sends meanwhile are held (bounded) and delivered
403
+ only after a yes. A channel that closes first withdraws the question (the approval context carries
404
+ an `AbortSignal`), and a later answer admits nothing. A first-pairing device is pinned for
405
+ reconnect only once it is admitted.
406
+ - `agent-command` — `/remote-control enable` and `revoke` from a connected surface are refused, and
407
+ `status` never shows a connected surface the pairing link.
408
+ - `agent-framework` — the `remote-control-enable` host action runs only for the operator's own
409
+ command, whichever command asked for it.
410
+ - `agent-cli` — every remote-control connection, a returning trusted device included, is put to the
411
+ operator on the host terminal; without an interactive terminal it is refused.
412
+
413
+ - Updated dependencies [3038eb7]
414
+ - Updated dependencies [02b7452]
415
+ - Updated dependencies [ec5e477]
416
+ - Updated dependencies [007fd90]
417
+ - Updated dependencies [18b52cc]
418
+ - Updated dependencies [9843fe6]
419
+ - @robota-sdk/agent-core@3.0.0-beta.81
420
+ - @robota-sdk/agent-interface-session@3.0.0-beta.81
421
+ - @robota-sdk/agent-framework@3.0.0-beta.81
422
+ - @robota-sdk/agent-interface-command@3.0.0-beta.81
423
+ - @robota-sdk/agent-interface-execution@3.0.0-beta.81
424
+ - @robota-sdk/agent-preset@3.0.0-beta.81
425
+
426
+ ## 3.0.0-beta.80
427
+
428
+ ### Major Changes
429
+
430
+ - 64ba748: **BREAKING — ARCH-042: project filesystem access is now an explicit, host-issued authority instead of an ambient consequence of `cwd`.**
431
+
432
+ `@robota-sdk/agent-framework` adds `WorkspaceTrustService` and the opaque
433
+ `IWorkspaceProjectAuthority`, plus bounded reader, settings-writer, state-storage, and mutation
434
+ facets. Public session, settings, context, checkpoint, memory, contribution, query, and replay
435
+ contracts consume those facets. A caller that does not supply `projectAccess` is deliberately
436
+ restricted to user-owned host state and receives no project filesystem capability.
437
+
438
+ The framework removes or renames ambient Node/project exports. Migrate `checkSettingsFile` to
439
+ `checkNodeHostSettingsFile`, `readMergedProviderSettingsFromPaths` to
440
+ `readMergedProviderSettingsFromSources`, `resolveProviderSettingsWriteTargetPath` to
441
+ `resolveProviderSettingsWriteTarget`, `FileSystemMemoryStore` / `createFileSystemMemoryStore` to
442
+ `WorkspaceMemoryStore` / `createWorkspaceMemoryStore`, and `PluginSettingsStore` to
443
+ `NodeHostPluginSettingsStore`. Host-only git helpers now carry the `FromNodeHost` suffix.
444
+ `projectPaths`, `resolveSettingsPathForScope`, and `getProviderSettingsPaths` are removed; project
445
+ consumers must use the authority facets rather than recover absolute paths.
446
+
447
+ `@robota-sdk/agent-session` renames the Node filesystem implementation `SessionStore` to
448
+ `NodeSessionStore` and adds explicit session-log/external-payload source and sink ports. Session
449
+ replay no longer resolves external payload files from an ambient directory.
450
+
451
+ `@robota-sdk/agent-interface-transport` changes `ISkillExecutionPort.loadCommands(cwd, home?)` to
452
+ the authority-bound `loadCommands()` and removes the optional absolute-path leak
453
+ `IInteractiveSessionStore.getFilePath`. `@robota-sdk/agent-command` consequently replaces the
454
+ `cwd` option of `createSkillsCommandModule` with required `contributionSources`; default command
455
+ composition accepts explicit contribution sources and discovers no project skills when none are
456
+ provided.
457
+
458
+ `@robota-sdk/agent-cli`, `@robota-sdk/agent-transport`, and
459
+ `@robota-sdk/agent-transport-tui` thread the trusted-or-restricted project decision through every
460
+ session surface. Embedded callers that need project settings, state, context, skills, checkpoints,
461
+ or mutation must mint access through `WorkspaceTrustService` and pass the returned
462
+ `projectAccess` (and a separately approved mutation/settings facet where required). Omitting it is
463
+ still type-compatible but is behaviorally breaking: the surface now fails closed instead of
464
+ reading or writing the current directory.
465
+
466
+ ### Minor Changes
467
+
468
+ - 9c19c50: `/fork [name] [--same-dir]` copies the live conversation into a background session.
469
+
470
+ The session writes a copy of its own record under a fresh id — messages, system prompt, tool schemas
471
+ and full history, and deliberately not the sandbox snapshot, goal, plan or branch — then spawns a
472
+ background job carrying only `resumeSessionId`. The child restores that record itself, so no
473
+ conversation crosses the child-process boundary and the worker start payload's key set is unchanged.
474
+ The background panel gains an `attach` control that switches the terminal onto the forked session; it
475
+ is a view switch, never a merge, and a missing record or a terminal task is refused with the task's
476
+ own status.
477
+
478
+ **`@robota-sdk/agent-framework` is `major` for one reason: `ICommandHostSessionAccess` gains a
479
+ required member.** `forkSession({ name? })` is not optional, so an external implementation of that
480
+ role port stops compiling until it adds the method. Every other change in this set is additive:
481
+
482
+ - `agent-interface-execution` — `TExecutionControl` gains `'attach'`.
483
+ - `agent-interface-command` — `TCommandUiIntent` gains `{ type: 'switch-session'; sessionId }`.
484
+ - `agent-subagent-runner` — `ISubagentWorkerComposition` gains the optional `openSessionStore`, and
485
+ the worker resumes a record when the job names one. A composition that registers no store and
486
+ receives no `resumeSessionId` behaves exactly as before.
487
+ - `agent-framework` also gains the fork-record builder, `SessionTurnMemory`, and
488
+ `IAgentBackgroundTaskRequest.resumeSessionId?`; `loadSessionRecord` now returns
489
+ `restoredSystemPrompt`, which makes a record field that was written and never read take effect on
490
+ restore.
491
+ - `agent-executor`, `agent-command`, `agent-transport-tui`, `agent-cli` — the spawn input, the
492
+ `/fork` module and the attach control that carry it to the surface.
493
+
494
+ - 9368d00: Advisor escalation: the main model can consult a second model at the decision points it chooses.
495
+
496
+ With an advisor configured (`--advisor <profile>[:<model>]`, or the `advisorModel` setting that
497
+ `/advisor` saves; the flag wins), the session gets an `Advisor({ question? })` tool. The advisor reads
498
+ the whole conversation — system prompt, messages, tool calls and results — serialized into one prompt
499
+ and sent with `toolChoice: 'none'`, truncated from the front to fit its window with the system prompt
500
+ kept, and declines when even that does not fit. Its answer comes back framed as guidance to verify;
501
+ an empty or refusing answer reads as declined. Calls are limited to two per turn and a fixed number
502
+ per session, parallel calls in one round share those limits, and a repeated question in the same
503
+ turn returns the earlier answer. A request that was sent counts even when the provider failed (the
504
+ decline is reported by class, never by its text); only a call declined before sending gives its slot
505
+ back.
506
+
507
+ Advisor usage, including in-process subagents', is recorded where each turn's usage is recorded —
508
+ the persisted session history, under the advisor's own provider and model — so `/cost`, usage reports
509
+ and resumed sessions include it. `/cost` now totals that history and prices each part on its own
510
+ model, showing "mixed" when more than one model was priced.
511
+
512
+ `/advisor <model>` and `/advisor off` change only where calls go, never the tool list, so the main
513
+ model's prompt cache is not invalidated mid-session; the tool is added only when a session starts
514
+ with an advisor. Sending history to a destination (provider type and endpoint host) the main model
515
+ does not already use needs a one-time consent per destination, kept in the user settings file; a
516
+ refusal is remembered for the session. The organization's `allowedProviders` applies, and
517
+ `ROBOTA_DISABLE_ADVISOR=1` turns it off completely. In-process subagents inherit the advisor, bound
518
+ to their own conversation; child-process subagents do not get it.
519
+
520
+ **`@robota-sdk/agent-framework` is `major` for one reason: `ICommandHostSessionAccess` gains a
521
+ required member,** `getSessionUsage()`, the session's persisted usage records. An external
522
+ implementation of that role port stops compiling until it adds the method. The rest is additive.
523
+
524
+ - `agent-session` — `formatConversationEntries`, the one text rendering of a conversation, now used
525
+ by compaction too. It keeps tool calls and results, marks a user message a peer session sent
526
+ (`user [from "peer:<id>"]`), and JSON-encodes every message onto one line so no content can forge
527
+ another entry; compaction previously flattened all of this. `Session.getProvider()` returns the
528
+ provider the session currently uses.
529
+ - `agent-framework` — `AdvisorController`, `createAdvisorTool`, the advisor spec helpers, provider
530
+ destinations (`describeProviderDestination`, `rememberProviderDestination`), `getSessionUsage` and
531
+ `ISessionUsageRecord`, the `onUsageRecorded` session option, and the optional `advisor` command
532
+ host adapter. Session assembly binds a host-supplied Advisor tool to the session holding it.
533
+ - `agent-command` — the `/advisor` command module; `/cost` reads the session's persisted usage.
534
+ - `agent-cli` — the `--advisor` flag, `advisorModel` setting, per-destination consent store and kill
535
+ switch.
536
+ - `agent-ui-terminal` — a usage line from another source (the advisor, a background task) names that
537
+ source and leaves out the context window it does not have.
538
+ - `agent-session-analytics` — personal usage counts an advisor call's tokens and cost toward its
539
+ turn without counting it as a turn.
540
+
541
+ - d4189b9: The rest of the per-server MCP OAuth lifecycle: signing in to a server without a local browser,
542
+ signing out of it with token revocation, and each server's sign-in state in `/mcp`.
543
+
544
+ - **`robota mcp login <name> --no-browser`** prints the authorization URL and reads the redirect URL
545
+ the user pastes back (not echoed). `runMCPOAuthLogin` takes `readRedirect` for this; nothing
546
+ listens on the redirect URI then. The pasted URL is held to the loopback listener's rules through
547
+ `createPastedRedirectAcceptor`: it must be the registered redirect URI (same origin and path, no
548
+ user info), carry the sign-in's `state` (compared in constant time), and is accepted once; an
549
+ error redirect is `authorization-denied` without its `error_description`, and the RFC 9207 `iss`
550
+ check applies as before. `openBrowser` now also receives the redirect URI. `readRedirect` gets a
551
+ signal that aborts on cancel or at `callbackTimeoutMs` (5 minutes by default, as for the loopback
552
+ listener); a paste longer than the prompt accepts fails as `redirect-too-long`.
553
+ - **Signing out** (`runMCPOAuthLogout`; `robota mcp logout <name>`; `/mcp logout <serverId>`):
554
+ deletes the stored credential under the refresh lock, then revokes the refresh token and the
555
+ access token (RFC 7009) when the stored issuer advertises `revocation_endpoint`. Revocation is a
556
+ POST through the egress policy that never follows a redirect and is byte-bounded; it authenticates
557
+ the client by `revocation_endpoint_auth_methods_supported` when advertised, otherwise the way its
558
+ refresh does. The local delete always happens. Each token's outcome is reported in `tokens`, and
559
+ overall as `revoked`, `partial`, `unsupported`, `failed` or `not-attempted`, with fixed reasons
560
+ only (new: `revocation-failed`, and `token-type-not-revocable` for RFC 7009
561
+ `unsupported_token_type`). `/mcp logout` also makes the session's authenticator drop the token it
562
+ holds (`IMCPOAuthAuthenticator.forget`).
563
+ - **Sign-in state:** `readMCPOAuthCredentialState` answers `signed-in`, `expired-refreshable`,
564
+ `sign-in-required` or `signed-out` — never anything token-derived. `/mcp` shows it for every
565
+ OAuth server; a server this session was told needs a sign-in reads `sign-in-required`.
566
+ `ICommandMCPActivationAdapter` gains optional `oauthStatus` and `oauthLogout`
567
+ (`ICommandMCPOAuthStatus`, `ICommandMCPOAuthLogoutResult`).
568
+ - **No in-session sign-in:** signing in stays `robota mcp login <server>` in a terminal, since it
569
+ needs the terminal (browser, pasted redirect, hidden secret prompt) a running session owns. `/mcp`
570
+ names that command for a server that needs a sign-in, as does the session's sign-in notice; the
571
+ server name is shown there only when it is safe to paste into any shell — a plain token not
572
+ starting with `-` or `=` — and is otherwise replaced by `<server>` (`shellArgumentForDisplay` in
573
+ `agent-core`; nothing is quoted, since quoting rules differ between shells). Server names in OAuth
574
+ notices have control and format characters escaped.
575
+ - **Refresh hardening:** on `invalid_grant`, the store is read again under the lock; a refresh token
576
+ another holder rotated in meanwhile is kept (and used when still valid) instead of being deleted.
577
+ The expiry skew is capped at half the token's lifetime, so a short-lived token is not refreshed on
578
+ every request; credentials now record `issuedAt` for this.
579
+
580
+ - af2f2ad: `/cd <directory>` continues the conversation in another directory.
581
+
582
+ - **A move is a new session in the target directory.** In the TUI, robota saves a copy of the
583
+ conversation where the target's session store will find it, ends the current run through its
584
+ normal end-of-life flow, and starts again in the target directory resuming that copy. The target's
585
+ settings, trust decision, tools, skills and `AGENTS.md` apply, exactly as if robota had been
586
+ launched there. The process boundary makes the move atomic, so no tool call can straddle it.
587
+ - **The system prompt is kept as recorded**, so a provider's prompt cache survives. One appended
588
+ `<workspace-move>` message tells the model the new directory, and which project instructions now
589
+ apply.
590
+ - **Refused** while a turn is running or a background task is still running, for a missing directory
591
+ or the current one, and for a target a `Cd(...)` deny rule names.
592
+ - **Access never widens:** a restricted session stays restricted after a move, and a trusted session
593
+ takes the target's own trust decision.
594
+ - New contracts: the `workspace-move` host action, `ICommandWorkspaceAdapter` /
595
+ `IWorkspaceMoveRequest`, `InteractiveSession.moveWorkspace`, and the `workspaceMovedFrom` session
596
+ option. The CLI's `--moved-from` and `--restricted-workspace` flags are internal.
597
+
598
+ - 34e50f0: A new permission mode, `auto`, lets a model classifier approve or block what would otherwise prompt.
599
+
600
+ - **What it decides:**
601
+ - Reads and in-workspace edits run as in `acceptEdits`.
602
+ - Commands and other calls the mode leaves open go to the classifier, a side call to the
603
+ session's own model. It sees the call, the working directory and the git remotes, never the
604
+ conversation.
605
+ - A block reaches the model with its reason, so it can take another route.
606
+ - **What still reaches a person, or is refused:**
607
+ - Deny rules and background ceilings apply first.
608
+ - `ask` rules, critical removals and protected paths ask a person.
609
+ - After 3 refusals in a row (blocks, or no usable verdict), or 20 blocks in the session, the
610
+ mode asks a person until one approves.
611
+ With no one to ask, the call is denied.
612
+ - **Allow rules:** in `auto` mode, allow rules that approve any command are set aside while the
613
+ mode is on. Examples are `Bash(*)`, an interpreter (`Bash(python *)`), a package runner
614
+ (`Bash(npm run *)`, `Bash(pnpm exec *)`), `Agent`, `ExecuteCommand` or `Computer`. Narrow
615
+ rules still apply.
616
+ - **Retry:** `/permissions` lists classifier blocks. `/permissions retry <n>` lets that exact call
617
+ run once, unjudged, when the model tries it again.
618
+ - **Turning it on and off:**
619
+ - `--permission-mode auto`, `/mode auto` or `/permissions auto`.
620
+ - An organization turns it off with `disableAutoMode` in the org policy.
621
+ - **New contracts:**
622
+ - `TPermissionMode` gains `'auto'`.
623
+ - `allowRulesForAutoMode` and `isBroadExecutionAllowRule`.
624
+ - `IPermissionClassifier` and `AutoModeGate`.
625
+ - The `permissionClassifier` session option.
626
+ - `Session.retryPermissionDenial`, and `retryDenial` on the permission-mode adapter.
627
+ - The `'classifier'` denial reason.
628
+ - `createModelPermissionClassifier`.
629
+ - The `disableAutoMode` option on `createSession` and `IOrgPolicy`.
630
+
631
+ - 722e88a: Shell commands can run in an OS-level sandbox: bubblewrap on Linux and WSL2, Seatbelt on macOS.
632
+
633
+ - **Confinement:** covers the command and every process it starts.
634
+ - Writes are limited to the working directory, the temporary directories and
635
+ `sandbox.filesystem.allowWrite`.
636
+ - Agent, git-hook, MCP and shell configuration inside the workspace stays read-only.
637
+ - `sandbox.filesystem.denyRead` hides paths from the command.
638
+ - The network is on or off (`sandbox.network.enabled`).
639
+ - **Modes:** `/sandbox` switches between `auto-allow`, `regular` and `off` for the next command and
640
+ saves the choice.
641
+ - In `auto-allow` (`sandbox.autoAllowBashIfSandboxed`), a confined command runs without a prompt
642
+ in `default` and `acceptEdits`.
643
+ - Deny rules, ask rules, critical removals and plan mode still apply first.
644
+ - **Exclusions:** `sandbox.excludedCommands` run unconfined, through the ordinary permission path.
645
+ - **When the sandbox cannot run:** a missing or unusable backend is reported at startup, in
646
+ `robota doctor` and in `/sandbox`, and commands then run unconfined.
647
+ `sandbox.failIfUnavailable` refuses to start instead.
648
+ - **New contracts:**
649
+ - `OsSandboxClient`, `detectOsSandbox`, `bubblewrapArguments`, `seatbeltProfile`.
650
+ - `ISandboxClient.wrapCommand` and `autoApproves`.
651
+ - `IPermissionEvaluationContext.sandboxAutoApproved`.
652
+ - The `commandSandbox` session option.
653
+ - The `sandbox` settings key and the `sandbox` command host adapter.
654
+
655
+ - a5cc36e: `/permissions` shows the rules the session enforces and the calls it refused.
656
+
657
+ - **Rules by source:** each allow, deny and ask rule the gate reads is listed under the settings file
658
+ that declares it. Rules added by a CLI flag, a preset or a command are listed under "this session".
659
+ A rule a settings file declares but the session does not enforce is not shown.
660
+ - **Recent denials:** the latest refused calls, most recent first, with the reason: a rule or the
661
+ mode, the user declining, or no one available to approve.
662
+ - New contracts: `Session.getRecentPermissionDenials()`, `PermissionEnforcer.getRecentDenials()`,
663
+ `IPermissionDenial`, the `permissionRules` host adapter (`createSettingsPermissionRulesAdapter`),
664
+ and `getPermissionRules` / `listRecentDenials` on `ICommandPermissionModeAdapter`. Settings
665
+ provenance now covers `permissions.ask`.
666
+
667
+ - 4dd45cc: `/peers` shows how each other local session's workspace relates to this one's: `same worktree`,
668
+ `same repo`, `different repo`, or `workspace unknown`.
669
+
670
+ A session now publishes a workspace claim with its rendezvous entry — the root commits reachable
671
+ from HEAD, a SHA-256 of its normalized `origin` URL (never the URL itself), and the real path of its
672
+ worktree — and re-reads it as it runs, so a first commit or a checkout does not leave it stale. The
673
+ reader does not take the claim as written: it reads git at the claimed path itself, and a claim whose
674
+ contents disagree is shown as `workspace claim mismatched, not believed` and relates to nothing. Those
675
+ git reads are local-only and asynchronous, run only for the view that shows the relation (and for
676
+ the sender of an incoming peer message), and are cached per announcement. The relation also reaches
677
+ the peer-turn origin, computed by the receiver; a relation the sender put on the wire is discarded,
678
+ and the admission never carries it.
679
+
680
+ The relation is display and routing information only and is never an authorization input.
681
+
682
+ Additive: `agent-interface-session-mobility` exports `TWorkspaceRelation` and gains the optional
683
+ `IPeerOrigin.workspaceRelation`; `agent-framework`'s `ILocalPeerSummary` gains the optional
684
+ `workspaceRelation` and `workspaceClaim`, and `ICommandLocalPeersAdapter` the optional
685
+ `listWithWorkspace()`.
686
+
687
+ - 2d3b2c0: ARCH-024 replaces framework knowledge of command-owner ids with optional, owner-declared semantic
688
+ roles. `ISystemCommand` can declare `skillActivation`, `contextReduction`, or `subagentSpawn`;
689
+ `SystemCommandExecutor` exposes the selected role projection and rejects duplicate owners with the
690
+ typed `DuplicateSystemCommandSemanticRoleError`, atomically across construction, registration, and
691
+ replacement.
692
+
693
+ This is a beta-line breaking contract correction: unannotated commands named `skills`, `compact`, or
694
+ `agent` no longer receive special framework behavior. `@robota-sdk/agent-command` now declares the
695
+ roles on its shipped skills, compact, and agent commands.
696
+
697
+ - 1e3f91a: CMD-004 Phase 2 Stage E (breaking, beta line): the legacy `TCommandEffect` union and
698
+ `ICommandResult.effects` are DELETED. Commands emit the split contract directly —
699
+ `hostActions` (session-executed via `ICommandHostAdapters`; works headless and remote) and
700
+ `uiIntents` (requester-routed `ui_intent` session events with UI-neutral `show-*` names).
701
+ Final carriers for the former notification effects: `session_renamed` and the new
702
+ `history_cleared` are BROADCAST session events (forwarded to every WS surface, folded by the
703
+ TUI transcript/title and the GUI reducer's new `sessionName`/transcript-reset state);
704
+ `session-execution-started` rides `result.data.sessionExecution`; the plugin-registry refresh
705
+ rides `result.data.pluginRegistryReloaded`. A mechanical grep floor
706
+ (`command-effect-grep-floor.test.ts`) keeps `tui-requested`/`TCommandEffect`/`effects:` out of
707
+ every `packages/*/src` production tree.
708
+ - 44393be: Add the `/remote-control` enable path (REMOTE-008 Stage B4-2b) — turn on P2P remote control locally, get
709
+ a QR + link, and a paired device co-drives the SAME live session over pairing-gated WebRTC. The command
710
+ is a declarative trigger returning `remote-control-enable-requested`/`-stop-requested` effects (SSOT
711
+ agent-interface-transport) and reads state via a new `ICommandHostAdapters.remoteControl.getStatus()`;
712
+ the TUI dispatches the effects to injected callbacks; all transport construction lives at the agent-cli
713
+ composition root (`WsSignalingClient` + pairing-gated `WebRtcTransport`, relay URL from
714
+ `transports.webrtc.options.relayUrl`, QR/link rendered into history). Fail-closed: no relay configured ⇒
715
+ does nothing; pairing mismatch/timeout ⇒ the session is never exposed. Consumes REMOTE-007 so a paired
716
+ remote owner answers their own permission/ask prompts over the WebRTC channel.
717
+ - cbee54e: Surface unmatched preset command-module names instead of silently dropping them (INFRA-032). A preset `enabledCommandModules`/`disabledCommandModules` entry that matches no built command module — a short form like `"editor"` instead of `agent-command-editor`, or a typo — is now reported as a non-fatal notice on both the startup `--preset` path (CLI terminal) and the in-session `/preset` path (command result). Detection lives once in agent-framework's new pure `findUnknownModuleNames`, and agent-command's duplicate module filter now delegates to the framework's `selectCommandModules`; `createDefaultCommandModules` returns `{ modules, unknownModuleNames }`.
718
+
719
+ ### Patch Changes
720
+
721
+ - b462ee7: Discriminate `IBackgroundTaskState` by kind, the same way `TBackgroundTaskRequest` and (as of the
722
+ prior change) `TBackgroundTaskResult` already are. Fields only one runner ever produces now live
723
+ only on that kind's member instead of being optional-and-cross-kind-reachable on every kind:
724
+
725
+ - `agent`-only: `agentType`, `isolation`, `resumeSessionId`, `promptPreview`, and the
726
+ worktree-isolation fields (`worktreePath`, `branchName`, `worktreeStatus`, `worktreeNextAction`,
727
+ `worktreeBaseRevision`, `parentWorktreeStatus`).
728
+ - `scheduled`-only: `schedule`, `nextFireAt`.
729
+ - Every kind except `agent`: `commandPreview` (a process command, an MCP tool-invocation summary, or
730
+ a schedule's shell command / wake instruction).
731
+ - `state.result` is now `IBackgroundTaskResult<K>` — correlated with `state.kind`, not the free
732
+ full-result union.
733
+
734
+ `pid`, `logPath`, and `transcriptPath` stay on the shared base rather than becoming agent- or
735
+ process-exclusive: the runner handle SPI already reports them for whichever runner's process
736
+ happens to produce them, and a subagent run via the worktree/child-process runner carries a `pid`
737
+ exactly as a `process`-kind task does. `timeoutReason` also stays base — session restore sets
738
+ `'stale_worker'` on any non-terminal, non-rearmable task regardless of kind, not only agent ones.
739
+
740
+ `IBackgroundTaskState<K>` narrows to the kind-specific member; called with no type argument it is
741
+ still the full union. The session-record decoder (`agent-session`) now rejects a persisted task
742
+ state carrying a field outside its own kind as corrupt, reported at that field's own path, the same
743
+ way it already rejects a `state.result` whose kind disagrees with `state.kind`.
744
+
745
+ **Breaking for `@robota-sdk/agent-interface-execution` and `@robota-sdk/agent-executor`**: code that
746
+ read a kind-specific field (e.g. `state.agentType`, `state.schedule`, `state.commandPreview`) off an
747
+ unnarrowed `IBackgroundTaskState` needs to narrow on `state.kind` first, since the fallback default
748
+ no longer carries every field on every kind. `agent-session`, `agent-framework`, and `agent-command`
749
+ land the corresponding narrowing at every read site the type change touched; no runtime behavior
750
+ changes there beyond the state decoder's new corruption checks.
751
+
752
+ - 3a8876b: ARCH-029: decompose the command host into role ports
753
+
754
+ `ICommandHostContext`, `ICommandSessionRuntime` and `IAgentJobHostContext` are now empty `extends`
755
+ aggregates over 26 named role ports, so a command declares only the capability it uses. A role port
756
+ is a supertype of the aggregate, so narrowing a declared parameter still satisfies
757
+ `ISystemCommand.execute` by contravariance.
758
+
759
+ All 79 members are preserved (46 + 18 + 15) with the declaration kind unchanged, so implementors and
760
+ callers stay source-compatible.
761
+
762
+ **Breaking:** every role-port member is now required except the adapter bag, whose contents are
763
+ genuinely variational. 38 members went from optional to required. A host that previously omitted a
764
+ member must now provide one — including `validateCurrentSessionReplayLog`, which was an override
765
+ with a framework-computed default and no implementor. `createTestCommandHost`,
766
+ `createTestAgentJobHost` and `createTestSessionRuntime` are published from
767
+ `@robota-sdk/agent-framework/testing` as conformant, cast-free doubles for exactly this.
768
+
769
+ Also removed: the `clearConversationHistory` fallback that reached past the host into
770
+ `getSession().clearHistory()`. Those were never the same operation — the host path also broadcasts
771
+ `history_cleared` to every attached surface, so a fallback clear left other surfaces still showing
772
+ the transcript.
773
+
774
+ - 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).
775
+ - d6b9404: Remove polynomial-ReDoS backtracking (SEC-003, CodeQL `js/polynomial-redos`) from three parsers whose input is not repo-controlled.
776
+
777
+ `parseStructuredResponseText` (agent-core) parses raw model output; its fenced-code-block regex used `\s*\n`, and because `\s` also matches a newline the two overlapped, so an unterminated fence containing many blank lines was rejected in O(n^2) — 12.7s for a 400 KB string, now ~1ms. The whitespace run is now restricted to horizontal whitespace, which makes the newline split point unique.
778
+
779
+ `/schedule cron` and `/monitor` (agent-command) are declared `modelInvocable: true`, so their argument string is composed by the model. Both matched the trailing instruction with `\s+(.+)$`; since `.` also matches a space the split point was ambiguous and a non-matching argument cost O(n^2) — ~15s for a 200 KB argument, now <1ms. The instruction is now required to start with a non-space character, which pins the split point without changing which inputs are accepted.
780
+
781
+ No behaviour change: the set of accepted inputs and the parsed values are identical in every case.
782
+
783
+ - Updated dependencies [9c19c50]
784
+ - Updated dependencies [7b6234c]
785
+ - Updated dependencies [5307f8a]
786
+ - Updated dependencies [b462ee7]
787
+ - Updated dependencies [08a9bd6]
788
+ - Updated dependencies [37b4bd7]
789
+ - Updated dependencies [818f0c8]
790
+ - Updated dependencies [4eea54b]
791
+ - Updated dependencies [1698be4]
792
+ - Updated dependencies [9368d00]
793
+ - Updated dependencies [d4189b9]
794
+ - Updated dependencies [9edae52]
795
+ - Updated dependencies [4078a72]
796
+ - Updated dependencies [4c73a0b]
797
+ - Updated dependencies [af2f2ad]
798
+ - Updated dependencies [267af5f]
799
+ - Updated dependencies [196a900]
800
+ - Updated dependencies [f336838]
801
+ - Updated dependencies [34e50f0]
802
+ - Updated dependencies [722e88a]
803
+ - Updated dependencies [a5cc36e]
804
+ - Updated dependencies [d23c848]
805
+ - Updated dependencies [b70fa3d]
806
+ - Updated dependencies [2711ec6]
807
+ - Updated dependencies [4dd45cc]
808
+ - Updated dependencies [378c585]
809
+ - Updated dependencies [4c5148e]
810
+ - Updated dependencies [37af5dc]
811
+ - Updated dependencies [807d161]
812
+ - Updated dependencies [fec722f]
813
+ - Updated dependencies [e82215f]
814
+ - Updated dependencies [52b7346]
815
+ - Updated dependencies [2ebff01]
816
+ - Updated dependencies [2ebff01]
817
+ - Updated dependencies [2d3b2c0]
818
+ - Updated dependencies [b078afa]
819
+ - Updated dependencies [2d3b2c0]
820
+ - Updated dependencies [baa6863]
821
+ - Updated dependencies [2d3b2c0]
822
+ - Updated dependencies [3a8876b]
823
+ - Updated dependencies [4772067]
824
+ - Updated dependencies [7b85767]
825
+ - Updated dependencies [0f98419]
826
+ - Updated dependencies [64ba748]
827
+ - Updated dependencies [9fbab1b]
828
+ - Updated dependencies [d312755]
829
+ - Updated dependencies [a009f5b]
830
+ - Updated dependencies [1e3f91a]
831
+ - Updated dependencies [4f3c075]
832
+ - Updated dependencies [475e085]
833
+ - Updated dependencies [e477440]
834
+ - Updated dependencies [9dcb5da]
835
+ - Updated dependencies [a95ca85]
836
+ - Updated dependencies [b6d14ce]
837
+ - Updated dependencies [0382a51]
838
+ - Updated dependencies [93d061d]
839
+ - Updated dependencies [39554a1]
840
+ - Updated dependencies [d28430a]
841
+ - Updated dependencies [bed26ea]
842
+ - Updated dependencies [fe48835]
843
+ - Updated dependencies [1e40b5b]
844
+ - Updated dependencies [4b76cfa]
845
+ - Updated dependencies [d9bd9ec]
846
+ - Updated dependencies [8865acf]
847
+ - Updated dependencies [90e7a10]
848
+ - Updated dependencies [fcb0da3]
849
+ - Updated dependencies [07b627f]
850
+ - Updated dependencies [d0de5b2]
851
+ - Updated dependencies [9665c6e]
852
+ - Updated dependencies [44393be]
853
+ - Updated dependencies [c7fa299]
854
+ - Updated dependencies [5134b3b]
855
+ - Updated dependencies [dd444c1]
856
+ - Updated dependencies [1f57e7f]
857
+ - Updated dependencies [833afe1]
858
+ - Updated dependencies [d6b9404]
859
+ - Updated dependencies [7863b16]
860
+ - Updated dependencies [fde558e]
861
+ - Updated dependencies [db5c439]
862
+ - Updated dependencies [4a01a87]
863
+ - Updated dependencies [cbee54e]
864
+ - Updated dependencies [9814afc]
865
+ - Updated dependencies [242a644]
866
+ - @robota-sdk/agent-interface-execution@3.0.0-beta.80
867
+ - @robota-sdk/agent-interface-command@3.0.0-beta.80
868
+ - @robota-sdk/agent-framework@3.0.0-beta.80
869
+ - @robota-sdk/agent-core@3.0.0-beta.80
870
+ - @robota-sdk/agent-interface-session@3.0.0-beta.80
871
+ - @robota-sdk/agent-preset@3.0.0-beta.80
872
+
873
+ ## 3.0.0-beta.79
874
+
875
+ ### Patch Changes
876
+
877
+ - @robota-sdk/agent-core@3.0.0-beta.79
878
+ - @robota-sdk/agent-framework@3.0.0-beta.79
879
+ - @robota-sdk/agent-interface-transport@3.0.0-beta.79
880
+ - @robota-sdk/agent-preset@3.0.0-beta.79
881
+
882
+ ## 3.0.0-beta.78
883
+
884
+ ### Patch Changes
885
+
886
+ - Updated dependencies [6f308d1]
887
+ - @robota-sdk/agent-core@3.0.0-beta.78
888
+ - @robota-sdk/agent-framework@3.0.0-beta.78
889
+ - @robota-sdk/agent-interface-transport@3.0.0-beta.78
890
+ - @robota-sdk/agent-preset@3.0.0-beta.78
891
+
892
+ ## 3.0.0-beta.77
893
+
894
+ ### Patch Changes
895
+
896
+ - Updated dependencies
897
+ - @robota-sdk/agent-core@3.0.0-beta.77
898
+ - @robota-sdk/agent-framework@3.0.0-beta.77
899
+ - @robota-sdk/agent-interface-transport@3.0.0-beta.77
900
+ - @robota-sdk/agent-preset@3.0.0-beta.77
901
+
902
+ ## 3.0.0-beta.76
903
+
904
+ ### Patch Changes
905
+
906
+ - DQ-AUDIT-002 — consolidate duplicated domain data onto single owners: one model-pricing SSOT in agent-core (`MODEL_PRICES`/`lookupModelPrice`/`calculateModelCost`/`estimateBlendedCostPer1000`) consumed by agent-command and agent-plugin (drops two embedded/stale price tables); the `len/4` token estimator replaced by core `CONTEXT_ESTIMATE_CHARS_PER_TOKEN`; TUI `TContextState` derived from core `IContextWindowState`; dead pass-through re-exports removed from agent-session.
907
+ - 576af62: Fix `ConfigurationError: Agent must be fully initialized before changing model configuration` when running `/preset` (or any live model re-apply) on a fresh interactive session before the first message. The Robota agent initialized lazily on the first `run()`, but `setModel` requires full initialization. `Session.applyModelOptions` now awaits the new idempotent `Robota.ensureReady()` before `setModel`, and the preset live-switch path (`applyPresetToSession` → `executePresetCommand`) is async end-to-end. Adds a real cold-session regression test (no mocked Robota).
908
+ - Updated dependencies
909
+ - Updated dependencies [c0a6287]
910
+ - Updated dependencies [9df3a88]
911
+ - Updated dependencies
912
+ - Updated dependencies
913
+ - Updated dependencies [576af62]
914
+ - @robota-sdk/agent-core@3.0.0-beta.76
915
+ - @robota-sdk/agent-framework@3.0.0-beta.76
916
+ - @robota-sdk/agent-interface-transport@3.0.0-beta.76
917
+ - @robota-sdk/agent-preset@3.0.0-beta.76
918
+
919
+ ## 3.0.0-beta.75
920
+
921
+ ### Patch Changes
922
+
923
+ - Agent preset system + live preset switching + context/history correctness fixes.
924
+
925
+ - **Preset system (PRESET-001~017):** new `@robota-sdk/agent-preset` package layering framework
926
+ assembly options into named, selectable profiles (`default`, `autonomous-builder`, `careful-reviewer`,
927
+ `neutral-executor`) plus user-authored external presets loaded from `~/.robota/presets/*.json`.
928
+ - **Live preset switching:** `/preset` command (list + active marker + switch) and a TUI active-preset
929
+ display. Switching live re-applies permission posture, model/effort, persona, command-module
930
+ selection, parallel-subagents gating, and a self-verification system-prompt section via the single
931
+ `applyPresetToSession` engine.
932
+ - **CTX-001:** the TUI Context display + session auto-compact now use the accurate provider-based token
933
+ estimate (system prompt + tool schemas included) instead of a crude history-only char heuristic.
934
+ - **HIST-001:** conversation history is now append-only — removed the silent 100-message count cap that
935
+ could drop early context; context size is managed solely by size-based compaction.
936
+
937
+ - Updated dependencies
938
+ - @robota-sdk/agent-preset@3.0.0-beta.75
939
+ - @robota-sdk/agent-framework@3.0.0-beta.75
940
+ - @robota-sdk/agent-core@3.0.0-beta.75
941
+ - @robota-sdk/agent-interface-transport@3.0.0-beta.75
942
+
943
+ ## 3.0.0-beta.74
944
+
945
+ ### Patch Changes
946
+
947
+ - Updated dependencies
948
+ - @robota-sdk/agent-framework@3.0.0-beta.74
949
+ - @robota-sdk/agent-core@3.0.0-beta.74
950
+ - @robota-sdk/agent-interface-transport@3.0.0-beta.74
951
+
952
+ ## 3.0.0-beta.73
953
+
954
+ ### Patch Changes
955
+
956
+ - @robota-sdk/agent-core@3.0.0-beta.73
957
+ - @robota-sdk/agent-framework@3.0.0-beta.73
958
+
959
+ ## 3.0.0-beta.72
960
+
961
+ ### Patch Changes
962
+
963
+ - Updated dependencies
964
+ - @robota-sdk/agent-framework@3.0.0-beta.72
965
+ - @robota-sdk/agent-core@3.0.0-beta.72
966
+
967
+ ## 3.0.0-beta.71
968
+
969
+ ### Patch Changes
970
+
971
+ - fix(context): unify token estimation to single SSOT — status bar and /context list now use the same serialized JSON estimate
972
+ - Updated dependencies
973
+ - @robota-sdk/agent-core@3.0.0-beta.71
974
+ - @robota-sdk/agent-framework@3.0.0-beta.71
975
+
976
+ ## 3.0.0-beta.70
977
+
978
+ ### Patch Changes
979
+
980
+ - CLI UX fixes: /context list full LLM context breakdown (CLI-B10), logo resize fix (CLI-B03), token estimates in context list (CLI-B04)
981
+ - Updated dependencies
982
+ - @robota-sdk/agent-framework@3.0.0-beta.70
983
+ - @robota-sdk/agent-core@3.0.0-beta.70
984
+
985
+ ## 3.0.0-beta.69
986
+
987
+ ### Patch Changes
988
+
989
+ - Updated dependencies
990
+ - @robota-sdk/agent-framework@3.0.0-beta.69
991
+ - @robota-sdk/agent-core@3.0.0-beta.69
992
+
993
+ ## 3.0.0-beta.68
994
+
995
+ ### Patch Changes
996
+
997
+ - @robota-sdk/agent-core@3.0.0-beta.68
998
+ - @robota-sdk/agent-framework@3.0.0-beta.68
999
+
1000
+ ## 3.0.0-beta.67
1001
+
1002
+ ### Patch Changes
1003
+
1004
+ - Updated dependencies
1005
+ - @robota-sdk/agent-framework@3.0.0-beta.67
1006
+ - @robota-sdk/agent-core@3.0.0-beta.67
1007
+
1008
+ ## 3.0.0-beta.66
1009
+
1010
+ ### Patch Changes
1011
+
1012
+ - refactor: CLI-001/002 — agent-cli layer separation and monorepo-wide readability lint rules
1013
+ - CLI-001: Extract startup phases into focused modules; enforce agent-cli layer separation
1014
+ - CLI-002: Apply import/order, consistent-type-imports, explicit-function-return-type, prefer-const, object-shorthand across all packages
1015
+ - Fix stale child-process-subagent-worker entry in agent-cli tsdown.config.ts (build fix)
1016
+
1017
+ - Updated dependencies
1018
+ - @robota-sdk/agent-framework@3.0.0-beta.66
1019
+ - @robota-sdk/agent-core@3.0.0-beta.66
1020
+
1021
+ ## 3.0.0-beta.65
1022
+
1023
+ ### Patch Changes
1024
+
1025
+ - @robota-sdk/agent-core@3.0.0-beta.65
1026
+ - @robota-sdk/agent-framework@3.0.0-beta.65
1027
+
1028
+ ## 3.0.0-beta.64
1029
+
1030
+ ### Minor Changes
1031
+
1032
+ - feat: add displayName and requiresPermission to command interfaces
1033
+ - `ICommand`, `ISystemCommand`, `ICommandListEntry`: add optional `displayName` field for user-friendly labels
1034
+ - `ISystemCommand`: add optional `requiresPermission` field for per-command permission policy declaration
1035
+ - `SystemCommandExecutor`: add `resolveRequiresPermission()` — derives from `safety` when field is undefined
1036
+ - All 24 built-in commands declare explicit `displayName` and `requiresPermission`
1037
+ - TUI autocomplete renders `displayName ?? name`; Tab completion still inserts the technical command ID
1038
+ - `/help` output shows `Display Name (/command-id)` format
1039
+
1040
+ ### Patch Changes
1041
+
1042
+ - Updated dependencies
1043
+ - @robota-sdk/agent-framework@3.0.0-beta.64
1044
+ - @robota-sdk/agent-core@3.0.0-beta.64