@robota-sdk/agent-cli 3.0.0-beta.1 → 3.0.0-beta.100

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 (156) hide show
  1. package/CHANGELOG.md +2998 -0
  2. package/LICENSE +661 -21
  3. package/README.md +527 -170
  4. package/bin/agent.cjs +39 -0
  5. package/dist/node/artifact-DrPhOb_v.d.ts +749 -0
  6. package/dist/node/artifact-DrPhOb_v.d.ts.map +1 -0
  7. package/dist/node/bin.js +35 -9
  8. package/dist/node/bounded-output-BdWv9SX3-28em7iHq.js +2 -0
  9. package/dist/node/bounded-output-BdWv9SX3-28em7iHq.js.map +1 -0
  10. package/dist/node/bounded-output-BdWv9SX3-C4ucXwLR.js +2 -0
  11. package/dist/node/cli-core-QK7sBRp8.js +2920 -0
  12. package/dist/node/command-executor-D_UBZHxh-CVZipoOV.js +2 -0
  13. package/dist/node/command-executor-D_UBZHxh-CVZipoOV.js.map +1 -0
  14. package/dist/node/command-executor-D_UBZHxh-D7UrdAlN.js +2 -0
  15. package/dist/node/desktop-host.d.ts +53 -0
  16. package/dist/node/desktop-host.d.ts.map +1 -0
  17. package/dist/node/desktop-host.js +2 -0
  18. package/dist/node/desktop-host.js.map +1 -0
  19. package/dist/node/headless.js +4 -0
  20. package/dist/node/host.d.ts +3 -0
  21. package/dist/node/host.js +1 -0
  22. package/dist/node/http-executor-ADn4locH-D-8crzF8.js +2 -0
  23. package/dist/node/http-executor-ADn4locH-D-8crzF8.js.map +1 -0
  24. package/dist/node/http-executor-ADn4locH-uz5KV6N-.js +2 -0
  25. package/dist/node/index.d.ts +790 -49
  26. package/dist/node/index.d.ts.map +1 -0
  27. package/dist/node/index.js +6 -13
  28. package/dist/node/index.js.map +1 -0
  29. package/dist/node/node-BRvqhy29.js +165 -0
  30. package/dist/node/node-BRvqhy29.js.map +1 -0
  31. package/dist/node/node-BTKWbzr0.js +15 -0
  32. package/dist/node/node-BZfTvzcV.js +15 -0
  33. package/dist/node/node-BZfTvzcV.js.map +1 -0
  34. package/dist/node/node-CdgadDFa.js +2 -0
  35. package/dist/node/node-CdgadDFa.js.map +1 -0
  36. package/dist/node/node-aM3G__lp.js +2 -0
  37. package/dist/node/node-zABT8WBL.js +165 -0
  38. package/dist/node/product-host-CwqVX6Xg.js +2954 -0
  39. package/dist/node/product-host-CwqVX6Xg.js.map +1 -0
  40. package/dist/node/product-host-DYR8lHfh.d.ts +4691 -0
  41. package/dist/node/product-host-DYR8lHfh.d.ts.map +1 -0
  42. package/dist/node/rolldown-runtime-C9xYM0wc.js +1 -0
  43. package/dist/node/rolldown-runtime-DK3Fl9T5-BeX3Bzvn.js +2 -0
  44. package/dist/node/rolldown-runtime-DK3Fl9T5-DK3Fl9T5.js +2 -0
  45. package/dist/node/rolldown-runtime-DK3Fl9T5-DK3Fl9T5.js.map +1 -0
  46. package/dist/node/rolldown-runtime-rs7quPpn.js +2 -0
  47. package/dist/node/verdict-decoder-Dz_rjoIZ-DA8BKB83.js +2 -0
  48. package/dist/node/verdict-decoder-Dz_rjoIZ-DA8BKB83.js.map +1 -0
  49. package/dist/node/verdict-decoder-Dz_rjoIZ-qM0jJVsB.js +2 -0
  50. package/dist/web/app-sandbox.html +145 -0
  51. package/dist/web/assets/PretendardVariable.subset.0-BHUkWNFR.woff2 +0 -0
  52. package/dist/web/assets/PretendardVariable.subset.1-C-__qv6_.woff2 +0 -0
  53. package/dist/web/assets/PretendardVariable.subset.10-DzSWztS8.woff2 +0 -0
  54. package/dist/web/assets/PretendardVariable.subset.11-CqVmlKJn.woff2 +0 -0
  55. package/dist/web/assets/PretendardVariable.subset.12-BHuZSgT0.woff2 +0 -0
  56. package/dist/web/assets/PretendardVariable.subset.13-C42mj_j2.woff2 +0 -0
  57. package/dist/web/assets/PretendardVariable.subset.14-Bl512uUX.woff2 +0 -0
  58. package/dist/web/assets/PretendardVariable.subset.15-D04iXIE3.woff2 +0 -0
  59. package/dist/web/assets/PretendardVariable.subset.16-BQUnS2GX.woff2 +0 -0
  60. package/dist/web/assets/PretendardVariable.subset.17-BfZSA-Xc.woff2 +0 -0
  61. package/dist/web/assets/PretendardVariable.subset.18-CwAxMC3C.woff2 +0 -0
  62. package/dist/web/assets/PretendardVariable.subset.19-CJu4Zcdo.woff2 +0 -0
  63. package/dist/web/assets/PretendardVariable.subset.2-dCZkyKLw.woff2 +0 -0
  64. package/dist/web/assets/PretendardVariable.subset.20-Ig1-z3n5.woff2 +0 -0
  65. package/dist/web/assets/PretendardVariable.subset.21-yKPEdLXC.woff2 +0 -0
  66. package/dist/web/assets/PretendardVariable.subset.22-CSqxKoOs.woff2 +0 -0
  67. package/dist/web/assets/PretendardVariable.subset.23-DK80wi0t.woff2 +0 -0
  68. package/dist/web/assets/PretendardVariable.subset.24-CmkE8Q8D.woff2 +0 -0
  69. package/dist/web/assets/PretendardVariable.subset.25-CsoWBIZB.woff2 +0 -0
  70. package/dist/web/assets/PretendardVariable.subset.26-Sozl8dw8.woff2 +0 -0
  71. package/dist/web/assets/PretendardVariable.subset.27-CT6nuW9L.woff2 +0 -0
  72. package/dist/web/assets/PretendardVariable.subset.28-CpO0Y96p.woff2 +0 -0
  73. package/dist/web/assets/PretendardVariable.subset.29-D6hjrUWm.woff2 +0 -0
  74. package/dist/web/assets/PretendardVariable.subset.3-Dqw33sf4.woff2 +0 -0
  75. package/dist/web/assets/PretendardVariable.subset.30-CWDM1c0J.woff2 +0 -0
  76. package/dist/web/assets/PretendardVariable.subset.31-CdmyZ5mm.woff2 +0 -0
  77. package/dist/web/assets/PretendardVariable.subset.32-CGnFWD2i.woff2 +0 -0
  78. package/dist/web/assets/PretendardVariable.subset.33--0OT__YQ.woff2 +0 -0
  79. package/dist/web/assets/PretendardVariable.subset.34-CaCS33Md.woff2 +0 -0
  80. package/dist/web/assets/PretendardVariable.subset.35-DWFYRGLp.woff2 +0 -0
  81. package/dist/web/assets/PretendardVariable.subset.36-Dn5IBRQB.woff2 +0 -0
  82. package/dist/web/assets/PretendardVariable.subset.37-BD6FyOtY.woff2 +0 -0
  83. package/dist/web/assets/PretendardVariable.subset.38-D4hu443z.woff2 +0 -0
  84. package/dist/web/assets/PretendardVariable.subset.39-B_7wfth9.woff2 +0 -0
  85. package/dist/web/assets/PretendardVariable.subset.4-Bvh2YGoc.woff2 +0 -0
  86. package/dist/web/assets/PretendardVariable.subset.40-BDaOfdUe.woff2 +0 -0
  87. package/dist/web/assets/PretendardVariable.subset.41-BUACvzZC.woff2 +0 -0
  88. package/dist/web/assets/PretendardVariable.subset.42-Dp-5mnyL.woff2 +0 -0
  89. package/dist/web/assets/PretendardVariable.subset.43-DHdpry7N.woff2 +0 -0
  90. package/dist/web/assets/PretendardVariable.subset.44-qHopVhdd.woff2 +0 -0
  91. package/dist/web/assets/PretendardVariable.subset.45-BniyRFfm.woff2 +0 -0
  92. package/dist/web/assets/PretendardVariable.subset.46-BMRq7xC-.woff2 +0 -0
  93. package/dist/web/assets/PretendardVariable.subset.47-B-cWO2pw.woff2 +0 -0
  94. package/dist/web/assets/PretendardVariable.subset.48-Ct-fWrPO.woff2 +0 -0
  95. package/dist/web/assets/PretendardVariable.subset.49-BblQVys9.woff2 +0 -0
  96. package/dist/web/assets/PretendardVariable.subset.5-K_MNGNCe.woff2 +0 -0
  97. package/dist/web/assets/PretendardVariable.subset.50-C8IyFH7L.woff2 +0 -0
  98. package/dist/web/assets/PretendardVariable.subset.51-Bxd0gTAs.woff2 +0 -0
  99. package/dist/web/assets/PretendardVariable.subset.52-CNgqKOOJ.woff2 +0 -0
  100. package/dist/web/assets/PretendardVariable.subset.53-BSRnyb-u.woff2 +0 -0
  101. package/dist/web/assets/PretendardVariable.subset.54-Dt2-cQkx.woff2 +0 -0
  102. package/dist/web/assets/PretendardVariable.subset.55-jFgflYjX.woff2 +0 -0
  103. package/dist/web/assets/PretendardVariable.subset.56-BwZdvJZQ.woff2 +0 -0
  104. package/dist/web/assets/PretendardVariable.subset.57-BwFDg-Fs.woff2 +0 -0
  105. package/dist/web/assets/PretendardVariable.subset.58-DlucQts_.woff2 +0 -0
  106. package/dist/web/assets/PretendardVariable.subset.59-CMkWjhdo.woff2 +0 -0
  107. package/dist/web/assets/PretendardVariable.subset.6-Bxhohlcm.woff2 +0 -0
  108. package/dist/web/assets/PretendardVariable.subset.60-CeHezjjf.woff2 +0 -0
  109. package/dist/web/assets/PretendardVariable.subset.61-PUuTnod4.woff2 +0 -0
  110. package/dist/web/assets/PretendardVariable.subset.62-DGSAWCfb.woff2 +0 -0
  111. package/dist/web/assets/PretendardVariable.subset.63-B35xsm4O.woff2 +0 -0
  112. package/dist/web/assets/PretendardVariable.subset.64-CTbrgYF9.woff2 +0 -0
  113. package/dist/web/assets/PretendardVariable.subset.65-B66rjuyf.woff2 +0 -0
  114. package/dist/web/assets/PretendardVariable.subset.66-C3HqaDeY.woff2 +0 -0
  115. package/dist/web/assets/PretendardVariable.subset.67-BmuXdlDy.woff2 +0 -0
  116. package/dist/web/assets/PretendardVariable.subset.68-DS9B48d0.woff2 +0 -0
  117. package/dist/web/assets/PretendardVariable.subset.69-YT16ymcp.woff2 +0 -0
  118. package/dist/web/assets/PretendardVariable.subset.7-E2HaA55t.woff2 +0 -0
  119. package/dist/web/assets/PretendardVariable.subset.70-BUXiAGMT.woff2 +0 -0
  120. package/dist/web/assets/PretendardVariable.subset.71-DuPZj8us.woff2 +0 -0
  121. package/dist/web/assets/PretendardVariable.subset.72-pYYGrEQR.woff2 +0 -0
  122. package/dist/web/assets/PretendardVariable.subset.73-DMrK970F.woff2 +0 -0
  123. package/dist/web/assets/PretendardVariable.subset.74-D4tQnymK.woff2 +0 -0
  124. package/dist/web/assets/PretendardVariable.subset.75-CxKdrRNf.woff2 +0 -0
  125. package/dist/web/assets/PretendardVariable.subset.76-DhPm2b_q.woff2 +0 -0
  126. package/dist/web/assets/PretendardVariable.subset.77-DwaxqOC8.woff2 +0 -0
  127. package/dist/web/assets/PretendardVariable.subset.78-DhqRbBzT.woff2 +0 -0
  128. package/dist/web/assets/PretendardVariable.subset.79-XpoyPP38.woff2 +0 -0
  129. package/dist/web/assets/PretendardVariable.subset.8-CRbJhhyA.woff2 +0 -0
  130. package/dist/web/assets/PretendardVariable.subset.80-DsV9Qp_h.woff2 +0 -0
  131. package/dist/web/assets/PretendardVariable.subset.81-BZzF9Hb3.woff2 +0 -0
  132. package/dist/web/assets/PretendardVariable.subset.82-BgAHe30u.woff2 +0 -0
  133. package/dist/web/assets/PretendardVariable.subset.83-DF-zBLLe.woff2 +0 -0
  134. package/dist/web/assets/PretendardVariable.subset.84-Brb8EsYQ.woff2 +0 -0
  135. package/dist/web/assets/PretendardVariable.subset.85-Byo_x2hf.woff2 +0 -0
  136. package/dist/web/assets/PretendardVariable.subset.86-XG7lTN_6.woff2 +0 -0
  137. package/dist/web/assets/PretendardVariable.subset.87-Lzui2vbK.woff2 +0 -0
  138. package/dist/web/assets/PretendardVariable.subset.88-CqX6JSgh.woff2 +0 -0
  139. package/dist/web/assets/PretendardVariable.subset.89-DOzqWPpX.woff2 +0 -0
  140. package/dist/web/assets/PretendardVariable.subset.9-Btb3bmS6.woff2 +0 -0
  141. package/dist/web/assets/PretendardVariable.subset.90-BF7RiZjm.woff2 +0 -0
  142. package/dist/web/assets/PretendardVariable.subset.91-Csm0YNoH.woff2 +0 -0
  143. package/dist/web/assets/index-2GhVGsHN.js +314 -0
  144. package/dist/web/assets/index-BSxTg2Py.css +1 -0
  145. package/dist/web/index.html +15 -0
  146. package/package.json +203 -34
  147. package/scripts/koffi-bun-plugin.mjs +116 -0
  148. package/scripts/native-build.d.mts +19 -0
  149. package/scripts/native-build.mjs +72 -0
  150. package/scripts/qualify-koffi-gc.mjs +28 -0
  151. package/dist/node/bin.cjs +0 -1185
  152. package/dist/node/bin.d.cts +0 -1
  153. package/dist/node/bin.d.ts +0 -1
  154. package/dist/node/chunk-C56WFH5S.js +0 -1163
  155. package/dist/node/index.cjs +0 -1202
  156. package/dist/node/index.d.cts +0 -49
package/CHANGELOG.md ADDED
@@ -0,0 +1,2998 @@
1
+ # @robota-sdk/agent-cli
2
+
3
+ ## 3.0.0-beta.100
4
+
5
+ ## 3.0.0-beta.99
6
+
7
+ ## 3.0.0-beta.98
8
+
9
+ ## 3.0.0-beta.97
10
+ ### Minor Changes
11
+
12
+ - 0d43f37: **An editor can reopen an earlier conversation over ACP and offer the agent's slash commands.**
13
+
14
+ `@robota-sdk/agent-transport-acp` sends `available_commands_update` once the editor holds the session
15
+ id — the runtime's commands and the user-invocable skills, each with its argument hint — and again
16
+ after a prompt that changed the list (a plugin reload, a preset). A host can supply `loadSession`;
17
+ the agent then offers `session/load`, checks it exactly like `session/new` (the served directory, no
18
+ editor MCP servers or extra roots, workspace trust), refuses it while a prompt runs, and replays the
19
+ loaded conversation — what was typed, what the agent answered, and each finished tool call, a reused
20
+ call id getting a row of its own — before it answers. The port makes the stored session current in
21
+ the object the transport was attached to, so a host attaches a forwarder such as a session slot.
22
+ The published narrow `IAcpTransportSession` contract remains supported: command listing and history
23
+ display reads are optional capabilities. Loading is offered only with both the host port and the
24
+ history read, so a session cannot be replaced without replaying its conversation.
25
+
26
+ `robota acp` supplies the load: the editor may load a stored session of the folder it serves that
27
+ this version can read, and the agent replaces its session with it and shuts the old one down. A load
28
+ is refused while the current session has work in progress, and for a stored session with an active
29
+ goal, a live loop or a schedule that restore would re-arm to fire, which would otherwise run unseen
30
+ by the editor. With `--no-session-persistence` nothing is stored, so loading is not offered.
31
+
32
+ ### Patch Changes
33
+
34
+ - e5a4254: **An editor that offers its file system carries the file tools' reads and writes.**
35
+
36
+ `Read`, `Write` and `Edit` take an optional `editorFiles` port (`IEditorFileAccess`), asked at each
37
+ call. On the host, after the tool's containment check (the permission gate has already decided),
38
+ `Read` returns the editor's text, unsaved changes included, and `Write` hands the content to the
39
+ editor; `Edit` reads where `Read` reads and writes where `Write` writes. The editor's text keeps
40
+ `Read`'s byte budget, binary refusal, formatting and cancellation, and an editor failure becomes the
41
+ tool's error result. `Glob` and `Grep` still search the disk and end their result with a note saying
42
+ so while an editor is attached. A sandbox with its own filesystem still carries every file tool, and
43
+ commands never use the editor. `createDefaultTools` and `createCodingPack` pass `editorFiles` through
44
+ to those tools only.
45
+
46
+ The ACP agent transport records the editor's `clientCapabilities.fs` and, once `session/new` binds the
47
+ session, hands the host an access through the new `onEditorFileAccess` option that sends
48
+ `fs/read_text_file` and `fs/write_text_file` for that session, for what the editor offered; stopping
49
+ the transport withdraws it. `robota acp` connects the two, so Read, Write and Edit go through the
50
+ editor in an editor that offers `fs`; `terminal/*` is not used.
51
+
52
+ ## 3.0.0-beta.96
53
+ ### Minor Changes
54
+
55
+ - 68dda38: MCP Apps: a tool's declared view can ride its result to a surface that can host it.
56
+
57
+ `agent-mcp` declares the `io.modelcontextprotocol/ui` extension only when the host opts in
58
+ (`IMCPOpenSessionOptions.apps`), on both protocol eras. A tool's `_meta.ui` reference (a `ui://`
59
+ resource and its audience) is kept in discovery and the catalog only when the server declared the
60
+ extension and resources too; a malformed declaration is reported by a fixed reason and the tool is
61
+ still offered without a view, and a tool the server hides from the model is rejected with a reason
62
+ rather than offered. `IMCPSession` gains `readResource` (a general `resources/read`, also on
63
+ `MCPConnectionSupervisor`) and `apps`; a custom `IMCPSession` implementation must add `readResource`.
64
+ After a successful call, `DiscoveredMCPTool` reads the declared resource and, when it is one
65
+ `text/html;profile=mcp-app` document within the size bound, attaches `IToolResult.view`; otherwise the
66
+ text result is unchanged and the omission is reported. The view never enters model context.
67
+
68
+ `agent-cli` opts in only for the plain `--serve` host that the desktop app and web GUI drive, not the
69
+ TUI, print runs, `mcp serve` or the ACP agent.
70
+
71
+ ### Patch Changes
72
+
73
+ - fbb287d: Show a finished MCP tool call's app in an opaque frame with a source-bound JSON-RPC bridge. App callbacks use the session's view authority and permission prompt. Session changes and frame disposal reject pending local requests.
74
+
75
+ Hosts can provide a sandbox URL through `SessionSurface`. The web GUI serves a separate sandbox document with content restrictions and `Connection-Allowlist` enforcement. App execution requires Chromium 158 or newer and native network isolation; other browsers and hosts without the response policy show an unavailable message and retain the text result. A surface without a sandbox URL never reads the app document.
76
+ - Keep E2B out of the base Node installation and defer SDK loading until stock hosted execution is selected. Install `e2b@2.51.0` alongside the CLI in the same project or global prefix to enable that capability. A missing SDK produces installation guidance before admission requests and never falls back to host execution. Public E2B exports and injected executors remain compatible; stock native binaries retain the embedded SDK, and consumer native builds require it as explicit build tooling.
77
+
78
+ ## 3.0.0-beta.95
79
+ ### Minor Changes
80
+
81
+ - c967c57: `robota acp` runs the agent as an Agent Client Protocol (v1) agent over stdio, so an editor that launches it (Zed, JetBrains and other ACP clients) can create a session, send prompts, see streamed replies and tool calls, approve or reject permission requests, and cancel a turn. Workspace trust, permission rules and the sandbox behave as in the terminal: the editor answers prompts and widens nothing. A folder that is not trusted yet is refused at `session/new` with the `trust` command to run, a working directory other than the one the agent was started in and any editor-supplied MCP servers or extra roots are refused, and a prompt that starts with `/` runs that command.
82
+
83
+ New package `@robota-sdk/agent-transport-acp` provides `createAcpTransport`, the ACP `ITransportAdapter` the command uses.
84
+
85
+ `@robota-sdk/agent-transport/node` gains `attachStdioCarrier`, the process-input bridge and close observation a stdio protocol carrier shares. `agent-transport-mcp` now uses it for its stdio carrier; its behavior is unchanged.
86
+ - 04a6de4: **Reasoning text a provider streams now reaches the editor as an ACP `agent_thought_chunk`, through a new `thinking_delta` session event.**
87
+
88
+ `IChatOptions.onThinkingDelta`, `IRunOptions.onThinkingDelta` and `IResumeExecutionOptions.onThinkingDelta`
89
+ (typed `TThinkingDeltaCallback`) carry reasoning text from a provider through the run, and the session
90
+ options (`ISessionOptions.onThinkingDelta`, `ICreateSessionOptions.onThinkingDelta`) hand it to the
91
+ session's owner. `InteractiveSession` emits it as `thinking_delta(delta)`, beside `text_delta`. The
92
+ text is never part of the reply: it is not appended to the conversation, not streamed through
93
+ `onTextDelta`, and the core adds no record of its own for it (no text-delta log entry, no
94
+ `provider_stream_raw_delta`); a provider's own native payload capture and response metadata keep
95
+ what the provider sent, as before. A reasoning delta counts as provider activity for the idle
96
+ timeout whether or not anyone listens to it, so a model that is only thinking is not cut off. The
97
+ existing `thinking(isThinking)` event is unchanged and remains the busy indicator. Where a provider
98
+ sends no reasoning text, nothing is emitted and nothing new is switched on.
99
+
100
+ `@robota-sdk/agent-interface-session` is a major change because a new session event name makes an
101
+ exhaustive map over the event names stop compiling until it classifies the event.
102
+
103
+ `@robota-sdk/agent-transport-acp` sends each non-empty `thinking_delta` to the editor as an
104
+ `agent_thought_chunk`, in order with the message chunks, so `robota acp` shows the model's reasoning
105
+ in the editor. A turn that streamed only thoughts still shows its result text, because only reply
106
+ text marks a turn as streamed. The WebSocket wire, the HTTP stream, headless stream-json, `query()`
107
+ and the terminal UI do not carry the new event in this change (`@robota-sdk/agent-transport` and
108
+ `@robota-sdk/agent-ui-terminal` classify it as non-surface). A forked skill and an in-process subagent
109
+ pass their reply text up but not their reasoning.
110
+
111
+ Two providers already receive reasoning text and now report it. `@robota-sdk/agent-provider-openai`
112
+ passes `response.reasoning_summary_text.delta` events of a streamed Responses call to
113
+ `onThinkingDelta`; the API sends them only when the provider profile asks for a reasoning summary, and
114
+ no request option changed; a summary streamed in several parts, and the summary of a later reasoning
115
+ item, each start on a new paragraph (`\n\n`) so the parts do not run together.
116
+ `@robota-sdk/agent-provider-openai-compatible` reports the text of Gemma's thought channel, which the
117
+ provider already removed from the reply, as it streams across chunk boundaries, with each later
118
+ channel of one reply on a new paragraph; the reply text is unchanged. No other provider reports
119
+ reasoning, and Anthropic thinking is not enabled. A model fallback chain still moves to the next model when only reasoning had streamed.
120
+
121
+ The scripted test provider (`@robota-sdk/agent-core/testing`) accepts an optional `thinking` string on
122
+ a turn and delivers it through `onThinkingDelta` before the turn's text; the scripted session harness
123
+ collects `thinking_delta` and now keeps a scripted tool-call turn's `usage`, which it used to drop.
124
+
125
+ ### Patch Changes
126
+
127
+ - 09e7e21: **An MCP server's form-mode elicitation during a tool call becomes a Robota ask prompt.**
128
+
129
+ `@robota-sdk/agent-mcp` opens a session with `elicitation: true` to declare form-mode elicitation and
130
+ answer it: a request the server raises while a tool call is in flight (a server→client
131
+ `elicitation/create` on the legacy era, an `input_required` round on 2026-07-28) is put to a person
132
+ through that call's ask port (`IMCPToolCallOptions.ask`, which the discovered tool fills from
133
+ `IToolExecutionContext.ask`) as one question titled with the configured server id and carrying the
134
+ server's message as one bounded line, with Respond and Decline, then one question per field
135
+ (choices, multi-select, yes/no, or free text validated against the field and re-asked once). The
136
+ answer continues the same call; the re-sent `tools/call` that carries it is the protocol's
137
+ continuation, not a replay, and a prompt is withdrawn when its call ends (an abort, or the call's
138
+ own timeout) — when the last in-flight call to that server ends, if there are several, since a
139
+ request names none of them. A request outside an in-flight call, or during a call that has no ask port, is
140
+ declined; a dismissal, a failing port or a second rejected answer cancels; a field named like a
141
+ credential is shown unmasked with a warning. URL mode is not offered. Without the opt-in nothing
142
+ changes: no capability is declared and an `input_required` result still closes the carrier. An
143
+ embedded request of another kind (sampling, roots) or more rounds than the client allows closes the
144
+ carrier as `input_required` does; an embedded elicitation the client refuses on its face (URL mode,
145
+ a malformed form) fails the call and leaves the carrier open.
146
+
147
+ `@robota-sdk/agent-cli` turns the opt-in on for the interactive terminal and the serve host driven by
148
+ the desktop app or the web GUI, which show the prompt, and leaves it off for print mode and for the
149
+ two serve-runtime hosts with no prompt surface, `mcp serve` and the ACP editor agent.
150
+
151
+ `@robota-sdk/agent-ui-web` renders an ask with `maxSelect` above 1 as a multi-select: options toggle
152
+ (click or 1–9), Submit answers with the ticked values once `minSelect` is met, and a toggle beyond
153
+ `maxSelect` is ignored, as in the terminal.
154
+ - 60f3965: **`robota mcp serve --ask-client` puts a caller's permission and ask prompts to the calling MCP client through elicitation instead of failing them.**
155
+
156
+ `@robota-sdk/agent-transport-mcp`: `createMcpTransport`, `createMcpHttpHost`,
157
+ `createMcpRemoteHttpHost` and `createAgentMcpServer` take `askClient`. Off by default, every prompt
158
+ is a tool error as before. On, and when the client declared form-mode elicitation, a permission or
159
+ ask prompt raised by that client's own call is put to it: a permission offers allow once or deny
160
+ only, never a remembered grant; an ask becomes a form (choice, multiple choice, free text); a masked
161
+ ask is never sent; deny, decline, cancel, a client error or an answer naming nothing offered keeps
162
+ today's outcome. stdio asks during the call. The HTTP hosts keep no session state, so they ask only
163
+ a client on the 2026-07-28 revision, through input-required rounds whose `requestState` is a
164
+ single-use random token naming one parked call; a 2025-era HTTP client keeps the tool error.
165
+ A call listens for prompts only while its own work drives the session, so a submission waiting
166
+ in the queue never holds another caller's prompt that it cannot answer.
167
+ Breaking for hand-written sessions: `IMcpTransportSession` now also requires `ISessionEvents`
168
+ (`on`/`off`), `ISessionPromptResolution` (`resolvePermission`/`resolveAsk`) and
169
+ `ISessionDriverAttribution` (`getActiveDriverId`), which `InteractiveSession` and the published
170
+ test double already implement.
171
+
172
+ `@robota-sdk/agent-interface-session` / `@robota-sdk/agent-session` / `@robota-sdk/agent-framework`:
173
+ `ISessionRuntimeTools.invokeRuntimeTool` (and `Session.invokeRuntimeTool`) accept
174
+ `options.permissionInteraction` and `options.driverId`. A direct call still denies a decision that
175
+ would need a person, and finds no one to ask a question, unless its caller passes
176
+ `permissionInteraction: 'interactive'`; then it asks the session's permission handler and its
177
+ tools get the session's ask port. `InteractiveSession` makes `driverId` the active driver while
178
+ the direct call runs, so a prompt the call raises carries it as `requesterDriverId`. With
179
+ `askClient` on, an MCP call runs under its own `mcp:<n>` driver id; a submission made that way is
180
+ attributed to it rather than to the owner.
181
+
182
+ `@robota-sdk/agent-transport`: exports `permissionSummaryArgument`, the argument a person recognises
183
+ a tool call by (`filePath` for the built-in file tools), for every transport that puts a permission
184
+ request to a person in its own wire form.
185
+
186
+ `@robota-sdk/agent-cli`: `mcp serve --ask-client` turns this on for whichever carrier the run serves.
187
+ - 26baaa9: **`@robota-sdk/agent-mcp` opens every MCP session through one `@modelcontextprotocol/client` 2.x client, for both protocol eras.**
188
+
189
+ Legacy negotiation stays the default; `protocolVersion: '2026-07-28'` pins the same client to the
190
+ modern era and never falls back to the legacy handshake. The hand-written stateless framing is gone,
191
+ and so is the package's dependency on the `@modelcontextprotocol/sdk` 1.x runtime: the client and its
192
+ schema package (`@modelcontextprotocol/client`, `@modelcontextprotocol/core`) are now the package's
193
+ dependencies, and the 1.x SDK remains only as a test dependency for legacy-era fixture servers. The
194
+ guarantees are unchanged and still tested: an unsupported result (`input_required`, an unknown result
195
+ type) closes the carrier and is never replayed (no client input is offered, so the SDK's automatic
196
+ input round stays off), and the receive bound, redirect refusal, pinned sockets, authenticated retry,
197
+ trace propagation and stdio authority live in the package's own transport and fetch wrappers as before.
198
+
199
+ Two behaviors change on the pinned 2026-07-28 era, both following the official client:
200
+
201
+ - A result that omits `resultType` is refused (`invalid-tool-result` for a tool call) instead of being
202
+ treated as complete. The tolerance served peers that predate the discriminator, which legacy
203
+ negotiation serves.
204
+ - An invalid freshness hint on `server/discover` (`ttlMs` negative or fractional, `cacheScope` unknown)
205
+ is read as the safest value (`ttlMs` 0, `cacheScope` private) instead of rejecting the server. List
206
+ pages and resource reads with an invalid hint are still refused.
207
+ - An `input_required` result that carries neither `inputRequests` nor `requestState` is refused as
208
+ `invalid-tool-result` rather than reported as `input_required`; either way the carrier closes and
209
+ nothing is replayed.
210
+
211
+ Against a real 2026-07-28 host the pinned path now also sends the body-derived request headers the
212
+ revision requires (`Mcp-Method`, `Mcp-Name`), which the hand-written framing did not; a headers helper
213
+ may no longer set either, as it already could not set the other headers the transport owns. A pinned
214
+ server that answers the discovery probe with a non-protocol HTTP error (a 404 page) is reported as a
215
+ failed startup carrying that status, not as a server lacking the revision. On the legacy era
216
+ `structuredContent` is still checked against the tool's declared `outputSchema`: the schema the latest
217
+ discovery saw travels with each call, since the SDK's own source for it is the aggregating `listTools`
218
+ cache that per-page discovery never fills.
219
+
220
+ `@robota-sdk/agent-cli` adds `@modelcontextprotocol/client` and `@modelcontextprotocol/core` to its
221
+ external `dependencies`, and with them `@modelcontextprotocol/server` and `@modelcontextprotocol/node`,
222
+ which its bundled MCP serve path already imported but the bundle inlined: with `core` external, the
223
+ server must resolve the one `core` it pins. It keeps the 1.x SDK, which
224
+ `@robota-sdk/agent-transport-mcp`'s stdio server still uses, and its import-boundary test now names
225
+ every `@modelcontextprotocol` package.
226
+ - b4c2572: **`@robota-sdk/agent-transport-mcp` serves stdio from `@modelcontextprotocol/server` 2.x, and the 1.x `@modelcontextprotocol/sdk` leaves the repository's runtime dependencies.**
227
+
228
+ The stdio server (`createAgentMcpServer`, `createMcpTransport`) now builds on the same v2 server
229
+ package the HTTP hosts already use, through its `/stdio` carrier; the catalog is validated with the
230
+ SDK's own `Tool` validator, so the HTTP hosts no longer re-serialize it at the SDK boundary. Client-
231
+ visible behavior is unchanged: the same tools, submission extension, admission, turn correlation and
232
+ error results, negotiated through the legacy `initialize` handshake as before. `createAgentMcpServer`
233
+ returns the v2 `Server` type; a consumer wiring its own carrier imports `StdioServerTransport` from
234
+ `@modelcontextprotocol/server/stdio`. The stdio connection is stateful, so the server can send
235
+ server-to-client requests such as `elicitInput` to a client that declares the capability (tested).
236
+ Two carrier details follow the v2 stdio transport: a single frame over 10 MiB now fails the stdio
237
+ carrier instead of being buffered without bound (the HTTP carriers already cap a body at 1 MiB), and
238
+ a line that is not JSON is skipped instead of failing the carrier. A malformed `tools/call` request
239
+ or result is answered with the SDK's invalid-params error, and an invalid catalog entry is reported
240
+ as `Invalid MCP tool: <path>: <reason>` instead of the 1.x validator's own text.
241
+
242
+ `@robota-sdk/agent-cli` drops `@modelcontextprotocol/sdk` from its external `dependencies`; nothing
243
+ in its bundle imports it any more. The 1.x SDK remains only as a test dependency of
244
+ `@robota-sdk/agent-mcp`, for its legacy-era fixture host.
245
+ - 115e104: **A command's caller can cancel what a skill command starts, including while the skill is prepared.**
246
+
247
+ `ISessionCommands.executeCommand` takes an optional trailing `{ signal }`. For a skill command it
248
+ reaches the turn the skill submits or the fork it runs, and the work before them: a cancel stops the
249
+ skill's activation before or after its shell expressions (once they start, all of them run to the
250
+ end), refuses a turn not yet started and interrupts one that is, and stops a fork. A command cancelled this way
251
+ rejects with the signal's reason whichever step the cancel reached, and a cancelled fork is not
252
+ recorded as an error. Before, a cancel that landed before the skill submitted its turn found nothing
253
+ for `abort()` to stop, and the turn then ran; a fork skill run as a command ignored the cancel. A
254
+ command that starts neither a turn nor a fork ignores the signal; every existing call keeps its shape.
255
+
256
+ A fork skill now receives the signal of whatever runs it, so a fork the model starts inside a turn
257
+ stops when that turn is aborted instead of running on.
258
+
259
+ `robota acp` hands each `/command` prompt's signal to the command, so `session/cancel` stops a skill
260
+ command's turn or fork even when it lands before the turn has started.
261
+
262
+ ## 3.0.0-beta.94
263
+ ### Minor Changes
264
+
265
+ - e706b43: Live telemetry can name its spans with the OpenTelemetry GenAI semantic conventions. Set
266
+ `PRODUCT_TELEMETRY_SEMCONV` to `gen_ai` to export `invoke_agent`, `chat {model}` (client) and
267
+ `execute_tool {tool}` spans with `gen_ai.*` attributes, keeping Robota-only facts as `agent.*`; or to
268
+ `gen_ai/dup` to emit both attribute sets while dashboards move. The default, `agent`, exports exactly
269
+ the spans it did before. An unknown value, or the setting while neither traces nor metrics are exported,
270
+ refuses startup. Console traces under a GenAI mode print the same projected spans.
271
+
272
+ The live prompt trace now carries each tool body's name (the session's tool-body observation includes it
273
+ and the framework's live projection passes a label-safe name through); it reaches spans only under a
274
+ GenAI mode and is never written to session history.
275
+ - ccff111: Live telemetry can name its metrics with the OpenTelemetry GenAI semantic conventions. With
276
+ `PRODUCT_TELEMETRY_SEMCONV` set to `gen_ai`, the metrics export the `gen_ai.client.inference.*`
277
+ duration, token-usage and per-call token histograms, `gen_ai.execute_tool.duration` and
278
+ `gen_ai.invoke_agent.*` duration and call counts, and drop only `agent.provider.input_tokens` and
279
+ `agent.provider.output_tokens`, which the GenAI usage counters replace exactly. `gen_ai/dup` exports
280
+ every `agent.*` metric and the GenAI ones. The default, `agent`, exports exactly the metrics it did
281
+ before. Provider- and tool-derived GenAI metrics are exported only when no such child was omitted, and
282
+ usage that was not received is recorded as 0 under the `unknown` modality, as the conventions specify.
283
+ The GenAI metrics carry the requested model as those conventions require, and their inference points
284
+ also take the `agent.provider.id` and `agent.model.id` labels under the same
285
+ `PRODUCT_TELEMETRY_METRIC_ATTRIBUTES` opt-ins as the `agent.*` metrics (the model only in `gen_ai/dup`),
286
+ so a provider split of token totals survives the dropped counters. `PRODUCT_TELEMETRY_SEMCONV` now applies to
287
+ traces and metrics, and is refused only when neither is exported over otlp or console. Console metrics
288
+ print the same projection.
289
+
290
+ `@robota-sdk/agent-telemetry-otel` adds `projectLivePromptMetricSamples`, which maps a live prompt trace
291
+ batch onto those GenAI metrics as plain, SDK-free samples with the recommended bucket boundaries, so
292
+ every host names and measures the same facts the same way. Its inference samples keep the label-safe
293
+ `agent.provider.id`, and `agent.model.id` in `gen_ai/dup`, as the provider span does.
294
+
295
+ ### Patch Changes
296
+
297
+ - The E2B hosted executor passes `--` before the mode when it sets private permissions on restored conversation files and their directory, so the commands also run on BSD/macOS `chmod`.
298
+
299
+ ## 3.0.0-beta.93
300
+ ### Minor Changes
301
+
302
+ - c402d98: Expose a separate product-bound CLI entry and desktop/native host helpers for npm-only consumer applications. Publish the product configuration and browser client contracts needed by those applications.
303
+
304
+ ### Patch Changes
305
+
306
+ - 8ee9265: Publish the generic organization canonical JSON and resource budget contracts. Move the operator-owned authority implementation behind a private host package; the CLI continues to bundle its hosted behavior.
307
+ - 0f15760: Withhold runtime transport credentials and telemetry exporter settings from commands started with a captured environment, including skill preprocessing. Preserve runtime authentication, telemetry export, product identity and explicit provider credential opt-ins.
308
+
309
+ ## 3.0.0-beta.92
310
+ ### Patch Changes
311
+
312
+ - Local OpenAI-compatible servers can be set up without saving a placeholder API key. Explicit credentials and environment references remain effective for authenticated servers and connection tests. Optional credentials are managed through the user-only provider edit command.
313
+
314
+ ## 3.0.0-beta.91
315
+ ### Patch Changes
316
+
317
+ - dfde075: Generated products can select a CLI package binary, version and build label, native host entry and artifact name, and desktop identity. Artifact metadata records the source version and provenance. Clean-tree generation copies only the committed source tree, while the built-in desktop remains packageable without generation.
318
+ - cf4fff8: Generated products can declare home-relative operational defaults and choose shared user/project settings sources. Installed artifacts scope ambient canonical variables to their product identity before file selection and child execution. Use matching PRODUCT_ID with canonical overrides or product-prefixed aliases.
319
+
320
+ The public startCliEntry function installs the complete CLI diagnostics and crash policy while preserving startCli worker dispatch. Diagnostics use the configured CLI name; headless Restricted runs report ignored project settings and trust refusals name the full product command.
321
+ - bad976f: Persist turn and tool intent before effects, restore interrupted calls with unknown outcomes, reap owned foreground shell groups after owner death, and prune proven dead peer announcements.
322
+
323
+ ## 3.0.0-beta.90
324
+ ### Minor Changes
325
+
326
+ - 7116367: Credentials the runtime holds for itself no longer reach the commands it runs.
327
+
328
+ - `agent-cli` — once a workspace's settings are admitted, every variable a provider profile or
329
+ definition names as its credential (`$ENV:` references, and defaults such as `OPENAI_API_KEY`)
330
+ leaves the process environment, so tools, hooks, background shells, a skill's `!` commands and
331
+ the environment an MCP header helper inherits no longer carry it, a hosted worker's broker token
332
+ included (a variable an MCP definition references explicitly still resolves); the provider keeps
333
+ authenticating from the startup snapshot. A user or managed settings layer can let
334
+ commands see one with `"commandEnvAllow": ["NAME"]`; project settings cannot. With the OS sandbox
335
+ on, confined commands can no longer read `settings.json`, `credentials/`, `mcp-credentials/` or
336
+ `remote-host-identity.json` under the user state directory. **Behavior change:** a command that
337
+ relied on inheriting a provider key now needs the opt-in, and a host embedding `startCli` finds
338
+ those variables gone from its own `process.env` afterwards — a later in-process call must pass them
339
+ in its `environment`.
340
+ - `agent-framework` — the settings schema gains `commandEnvAllow`; `checkSettingsDocument` and
341
+ `buildProviderProfilesSnapshot` take an optional environment map to resolve `$ENV:` references
342
+ against instead of the live process environment.
343
+ - `agent-command` — provider startup checks credentials against the host's environment snapshot.
344
+ - `agent-tools` — `OsSandboxClient` takes `hiddenPaths`, always hidden from confined commands and
345
+ not changeable by sandbox settings.
346
+ - `agent-subagent-runner` — a child-process subagent removes its provider credential variable from
347
+ its own environment once its provider is built, unless the start payload says the owner opted it in
348
+ (`keepProviderCredential`).
349
+ - `dag-node-instant-node` — prompt nodes accept an env resolver (`createPromptBackedNodeDefinition`'s
350
+ third argument, `rehydrateInstantNode`'s `resolveEnv`) for their `$ENV:` credential default.
351
+ - `agent-command-workflows` — `/workflows create` and `/workflows run` resolve prompt-node credentials
352
+ from the host's environment snapshot.
353
+
354
+ ### Patch Changes
355
+
356
+ - 5bb675a: Provider commands check a profile's `$ENV:` or default credential against the host's environment snapshot, so `/provider switch`, `/provider test`, `/provider add`, `/provider edit` and a cross-profile `/model` keep working while the runtime withholds its own credentials from `process.env`.
357
+
358
+ - `IProviderCommandModuleOptions` takes an optional `env`, and `createDefaultCommandModules` takes `providerEnvironment`. The CLI passes its startup snapshot.
359
+ - The git that isolated subagents run, and so the repository's hooks, now get the command environment instead of the full snapshot.
360
+
361
+ ## 3.0.0-beta.89
362
+ ### Minor Changes
363
+
364
+ - 3427887: `--serve --http-port` can serve the agent HTTP API as an OAuth resource server: with `--http-public-url`, `--oauth-issuer`, `--oauth-scopes` and `--oauth-allowed-subjects` (plus optional `--http-host` and `--trusted-proxy`) it may bind a non-loopback address and admits access tokens through the same shared resource-server gate as `mcp serve`. Without them it stays loopback with the `PRODUCT_HTTP_TOKEN` bearer.
365
+
366
+ The refusal answer shared by both resource-server carriers (503 for unavailable issuer keys, uncounted; counted challenge otherwise) is now `refuseAccessToken` in `@robota-sdk/agent-transport/node`.
367
+
368
+ ### Patch Changes
369
+
370
+ - ca44ac6: HTTP clients can now receive and answer permission and ask prompts. `POST /submit` with `receivePrompts: true` streams `permission_request`, `ask_request` and `prompt_resolved`. `GET /prompts` lists the prompts still open, so a client that attaches later can see them, and `POST /prompts/:id` answers one; an id that is not open returns 404, and an answer the session did not take returns 409. Clients that omit `receivePrompts` see no change. `IHttpTransportSession` now also requires `resolvePermission` and `resolveAsk`, which `InteractiveSession` already implements. The `--serve --http-port` API gains these routes as well.
371
+
372
+ ## 3.0.0-beta.88
373
+
374
+ ### Minor Changes
375
+
376
+ - b96b81a: `--serve --http-port <port>` also serves the agent HTTP API on `127.0.0.1:<port>`, so other
377
+ applications and services can submit a prompt and stream its result over SSE, run a slash command,
378
+ abort and read the conversation without a Robota client library. Requests are admitted by the
379
+ bearer in `PRODUCT_HTTP_TOKEN` (at least 32 characters), which the runtime removes from its
380
+ environment before any command runs; the API binds loopback only and is reached from elsewhere
381
+ through the operator's own proxy. `--http-port` keeps its `mcp serve` meaning.
382
+
383
+ ### Patch Changes
384
+
385
+ - 853e585: Print mode (`-p`, `--goal`) waits for stdout and stderr to flush before exiting, so a result larger than the pipe buffer is no longer cut off when stdout is a pipe on macOS (for example `-p /help --output-format json`).
386
+
387
+ ## 3.0.0-beta.87
388
+
389
+ ### Minor Changes
390
+
391
+ - 7d9cc66: Preserve an explicitly supplied host tool scheduling policy through session assembly and CLI presentation modes, retaining permission admission and conservative handling of undeclared resources.
392
+
393
+ ### Patch Changes
394
+
395
+ - 66af868: Restore portable default startup for generated Robota artifacts while preserving explicit product selection and immutable identity. Keep shared product configuration internal because existing SDK and CLI artifacts bundle its implementation and types.
396
+ - Release the migrated Robota product with its public SDK namespace, robota command, compatible SDK exports, internal product configuration, and verified CLI startup and delivery corrections.
397
+ - 7d9cc66: Expose declarative contribution descriptors for installed bundles and resolved MCP sources,
398
+ including source generations, host capability requirements and compatibility diagnostics.
399
+ Revalidate MCP approval, source identity and workspace trust before new dispatch, preserving
400
+ outcomes of calls already started and refusing retained tools after shutdown.
401
+ - 7d9cc66: Resume hosted conversations in freshly provisioned workers using verified checkpoint records while rebuilding current prompts, policy and execution state.
402
+ - 7d9cc66: Load declared JSON and inline bundle MCP configurations with selected-source containment, ordered whole-server replacement and file provenance. Route packaged servers through normal CLI/app MCP startup with namespaced identities, existing trust/approval/authority gates, disable overlays and value-free compatibility diagnostics.
403
+ - 7d9cc66: Add owner-side clean E2B worker provisioning and verified filesystem recovery with fresh identity and epoch, explicit failure reconciliation, and no paused-memory execution fallback.
404
+ - 7d9cc66: Add owner-hosted organization control and OpenAI-wire gateway composition with anchored authority, mandatory verified audit, durable effect reservations, provider inventory cleanup and private content receipts. OpenAI profiles can opt into operation identities preserved across SDK retries.
405
+ - 7d9cc66: Load external name-only plugin manifests without inventing optional metadata. Select the recorded
406
+ installed source instead of the lexically last cached directory, refusing invalid, missing or
407
+ ambiguous revisions. Check the installed identity as well as the manifest name for disablement.
408
+ - 7d9cc66: Pass the child execution root and restored sandbox to hook composition. The stock CLI explicitly refuses command and HTTP hooks for separate task workers whose hook capabilities are unavailable, instead of executing those hooks on the host.
409
+ - 7d9cc66: Connect explicitly selected stateless MCP servers through product startup. Preserve and expose the selected protocol, bind activation approval to it, refuse unsupported selections, and report unavailable protocol capabilities without widening process or network authority.
410
+ - 7d9cc66: Add explicit MCP Skills selection, bounded supervisor access and persistent host content consent.
411
+ Users can inspect verified instructions, approve the exact content fingerprint and withdraw consent;
412
+ models can discover metadata only. Consent stays separate from server admission and model-turn activation.
413
+ - 7d9cc66: Route discovered MCP Skills through the shared skill command runtime with lazy verified content, source-bound invocation names, current user consent and execution-owned activation cleanup. Keep remote shell expressions inert and defer queued user skill reads until their own turn executes.
414
+ - 7d9cc66: Connect the desktop to an owner-bound remote task through scoped TLS credentials, durable replay protection, current company authority and a separate native operator approval channel. Keep remote credentials and local filesystem capabilities outside the renderer's remote task connection.
415
+ - 7d9cc66: Reconstruct visible session history from verified hosted conversation checkpoints without importing old system messages or execution events.
416
+ - 7d9cc66: Retain an owner shutdown signal received while a served runtime initializes, and release its signal handlers when the run settles.
417
+ - 7d9cc66: Require hosted admission protocol v2 with signed root-task ownership. Add a pinned E2B SDK connector that checks admitted worker ownership and network configuration before connecting, keeps management credentials outside task operations, and retains provider teardown failures.
418
+ - 7d9cc66: Expose verified skill supporting resources only within their owning turn or fork, using the current host consent and manifest.
419
+ - 7d9cc66: Run the stock hosted CLI inside an admitted E2B task worker with broker-signed scoped access, pinned worker artifacts, runtime-owned input/output and provider cleanup.
420
+ - 7d9cc66: Observe scheduler queue intervals separately from pre-dispatch admission, permission and tool work.
421
+ Preserve valid duration totals when live trace detail is bounded, reporting invalid evidence explicitly
422
+ through content-free console and OTLP traces without altering dispatch, recovery or settlement.
423
+ - 7d9cc66: Require version-3 hosted admission with broker-signed cumulative usage and owner-selected runtime ceilings. Collect accounting before dispatch, on refresh and at completion; stop and release owned execution when accounting or limits fail. Operator deployments and issuers must migrate from version 2.
424
+
425
+ Connect authenticated runtime loopback clients to the stock hosted worker daemon through owned provider command streams. Hosted serve requires a private owner-selected ingress configuration and uses a separate worker launch token; stop closes clients and the listener before cleanup settles.
426
+
427
+ ## 3.0.0-beta.86
428
+
429
+ ### Patch Changes
430
+
431
+ - 44bd221: Allow the desktop CLI to start and reconnect to its workspace daemon on Windows using private storage and a native local control pipe that verifies process ownership.
432
+
433
+ ## 3.0.0-beta.85
434
+
435
+ ### Minor Changes
436
+
437
+ - db35dcd: MCP approvals now outlive the process, and an approval takes effect at once. The `robota` CLI keeps
438
+ activation decisions in `$PRODUCT_USER_STATE_DIR/mcp-approvals.json` (owner-only) unless the host passes its own
439
+ `mcpApprovalStore`, so an approved server connects at later starts until its definition changes.
440
+ `/mcp approve <server>` now also connects the server in the running session, the way `/mcp reload`
441
+ does, instead of waiting for a restart. A remote server without OAuth therefore connects once
442
+ approved; stdio servers still need a host-supplied authority.
443
+
444
+ ### Patch Changes
445
+
446
+ - 193a0bc: `${CLAUDE_SKILL_DIR}` in a skill expands to the absolute folder its `SKILL.md` is in; it used to expand to an empty string. A skill's `` !`command` `` gets `CLAUDE_SKILL_DIR` and `CLAUDE_SESSION_ID` in its environment, so the shell expands them there too (`TShellExecFn` takes an optional `env`). Skills from host folders and a trusted project carry the folder, and bundle-plugin skills and commands record theirs. `ICommand` gains `skillDirectory`, and `IContributionSource` an optional `locate()` that names the absolute path of a root-relative one.
447
+ - 3cfd5b9: Words after `--` are the prompt, never a subcommand or a flag. `robota -p -- init` used to run `robota init` (and `eval`, `user-local` or `mcp` likewise ran or refused as those commands), and a prompt spelling `--attach`, `--safe-mode` or `--restricted-workspace` acted as that flag; now each is sent to the model as text. A subcommand or flag given before `--` works as before.
448
+ - 4cee6fb: Report unsupported MCP transports at startup and reload instead of silently skipping them,
449
+ and distinguish plugin MCP declaration inspection from a runtime connection.
450
+ - 3ab2eca: - External presets accept every permission mode a session does, `auto` included; `auto` used to fail validation.
451
+ - `createDagFramework({ ports: { costMeta } })` wires cost-metadata management; without it cost operations still report that they are unsupported.
452
+ - `startCli()` runs the subagent worker when a subagent starts the embedder's entry script again, as the `robota` executable already did; an embedded CLI used to start a second CLI there.
453
+ - The provider `executor` option docs no longer import a `RemoteExecutor` that does not exist, and `IRemoteExecutorConfig` is marked deprecated: nothing implements a remote executor.
454
+
455
+ ## 3.0.0-beta.84
456
+
457
+ ### Minor Changes
458
+
459
+ - e8d70ac: A first run finds a provider, instead of a dead end. `robota --serve` (and the daemon it starts) no
460
+ longer refuses when no provider is configured: it starts in setup mode, and the GUI's conversation area
461
+ shows a "Connect a model provider to start" panel with a "Set up provider" button in place of the
462
+ composer. Answering it configures and swaps in the first provider live, with no restart. A startup
463
+ failure now says why — `robota daemon start --json` and the desktop app's fatal screen report the
464
+ child's own reason instead of a generic "readiness channel closed", and the fatal screen gets a Try
465
+ again button. `trust status --json` and the desktop trust dialog list only sources whose state trust
466
+ would actually change, instead of naming one this platform could not determine; the dialog shows one
467
+ sentence and a collapsed Details section. `robota --serve --open` in an untrusted folder now asks at
468
+ the terminal (trust it, start Restricted, or quit) when someone is there to ask, instead of refusing
469
+ outright.
470
+
471
+ ### Patch Changes
472
+
473
+ - 7e87bb2: A front end with a person in front can ask about an untrusted folder instead of being refused.
474
+ `robota trust status --json` reports the folder's trust state, whether a person can be asked, and
475
+ what trust would load. `robota daemon start --restricted-workspace` starts the daemon Restricted in a
476
+ folder that is not trusted yet, the choice that person made. A daemon reports whether it runs
477
+ Restricted: a Restricted start never reuses one with the project's configuration, and a plain start
478
+ in a folder trusted since never reuses a Restricted one; both refuse and name `robota daemon stop`. `pnpm gui:dev` asks at its terminal: trust the folder, start
479
+ Restricted, or quit.
480
+ - 4f35721: Starting a session from `robota session view` in a folder that is not trusted asks what to do
481
+ instead of failing. The question names the folder and lists what trust would load. The choices are
482
+ `y` to trust the folder and start, `r` to start it Restricted, and `n` to cancel. A Restricted answer
483
+ holds even if the folder was trusted meanwhile. Before, the view showed only "Start failed".
484
+
485
+ - `agent-cli` (patch):
486
+ - A background session started Restricted runs with `--restricted-workspace`.
487
+ - A headless start that asked to run Restricted is no longer refused for want of trust, the same
488
+ as `--safe-mode`.
489
+ - `agent-ui-terminal` (minor): the session view takes `startTrustQuestion`, which returns the
490
+ folder and what trust would load (`ISupervisedStartTrustQuestion`). `onStart` receives the person's
491
+ choice (`TSupervisedStartTrustChoice`).
492
+
493
+ - 4ee9596: Starting a new TUI session in an untrusted repository asks whether to trust the folder. Before, the
494
+ TUI started Restricted without saying so, and the project's settings, hooks, skills and MCP servers
495
+ were silently missing. The question comes before anything from the project is loaded and lists what
496
+ trust would load. A yes records the grant and starts normally. A no starts Restricted and asks again
497
+ next time. If the grant cannot be recorded, it says so and starts Restricted.
498
+
499
+ It is not asked:
500
+
501
+ - when resuming or continuing a session, which stays in the store it was saved in;
502
+ - for setup commands (`init`, `--configure`, `--configure-provider`, `--set-current`);
503
+ - with `--safe-mode`, or after a `/cd` into a Restricted folder;
504
+ - without a terminal;
505
+ - in a directory outside Git.
506
+
507
+ - 12a5b15: CLI fixes from the docs-refresh findings (#3275):
508
+
509
+ - `robota trust revoke` exits 0 when the revoke succeeds.
510
+ - `Robota <subcommand> --help` prints that subcommand's help instead of running it, refusing an unknown option, or starting the terminal UI; commands with fuller help of their own (`usage`, `session list|view|attach`) keep printing it, and `robota mcp serve --help` prints to stdout.
511
+ - `robota --help` lists every option the parser accepts (`--goal`, `--goal-max-iterations`, `--provider`, `--set-current`, `--type`, `--base-url`, `--api-key`, `--api-key-env`, `--settings-scope`, `--session-log`, `--serve`, `--restricted-workspace`, `--disable-update-check`), `--effort` lists `none` and `minimal`, and `--bare` is described as what it does: print mode without instruction files or plugins.
512
+ - `robota session start --background --restricted-workspace` starts a background session Restricted, as the headless trust refusal already suggested.
513
+ - `Robota "prompt"` opens the terminal UI with the prompt typed in, not yet sent.
514
+
515
+ - 29486da: A lost connection no longer drops a typed message or takes the person out of their session. While the
516
+ transport is not `connected`, the composer keeps its draft and Send explains why it cannot submit
517
+ ("Not connected"). A banner above the conversation — never a full-screen replacement — says "Connection
518
+ lost. Reconnecting…" while retries continue, and once they give up either offers a working Reconnect
519
+ (a host that can restart the runtime) or says how to reopen the page (a browser served by
520
+ `robota --serve --open`). A desktop Reconnect remembers the session the person was in and returns to it
521
+ once the restart's reload reconnects.
522
+
523
+ ## 3.0.0-beta.83
524
+
525
+ ### Minor Changes
526
+
527
+ - 3c81769: A supervised session accepts attach connections on its control socket.
528
+
529
+ - A terminal of the same user sends `{"command":"attach","id","generation","mode":"drive"|"observe","protocol":1}`.
530
+ The session compares the generation with its own. It refuses another generation, an unknown mode or
531
+ protocol, or a fifth concurrent attach. Otherwise it answers `{"status":"attached","driverId":"attach:<n>"}`
532
+ and carries the ordinary session protocol as newline-delimited JSON on the same connection.
533
+ - The driver id is assigned by the session and a client-sent one is ignored. Turns submitted from an
534
+ attached terminal are attributed to it and counted under the new `attach` usage surface.
535
+ - `drive` sends prompts and answers the session's questions under the usual co-drive rules. `observe`
536
+ is read-only and never counts as a surface that can answer, so an unattended session still denies
537
+ its prompts at once.
538
+ - Detaching or crashing ends only that connection: a turn in progress keeps running, a prompt no other
539
+ surface can answer is denied, and the session returns to its unattached posture. A reader that falls
540
+ 1 MiB behind is disconnected, and an oversize frame closes only its own connection. Stopping the
541
+ session ends attached connections with it.
542
+ - Commands from an attached terminal carry the remote origin, so pairing, revoking and reading the
543
+ pairing link stay refused. An attached terminal is never an operator approver; supervised sessions
544
+ keep refusing mesh connections that need one.
545
+ - A client may send its first frames in the same write as the handshake; only the handshake line
546
+ itself is held to the control endpoint's line limit.
547
+ - `agent-framework`: the surface a turn was submitted on now reaches its usage observation. It was
548
+ dropped before, so remote-control turns were counted as `unknown`.
549
+ - The terminal client (`robota session attach`) and the view keys come separately.
550
+
551
+ - 617b795: `robota session attach <id> [--observe]` puts this terminal on a live supervised session.
552
+
553
+ - It asks first, on the controlling terminal, naming the session and the role: drive (send prompts,
554
+ answer its questions) or observe (read only). The yes holds only for the process start it named: if
555
+ the session restarted meanwhile, the attach is refused.
556
+ - It needs an interactive terminal. Without one (a script, or an agent running shell commands) it is
557
+ refused and prints the command for the user to run. It is not available as a slash command or a
558
+ model tool.
559
+ - The terminal view shows the conversation so far, then follows the session live: streaming replies,
560
+ tools, other terminals' prompts, queued input and questions. In drive mode it sends prompts and
561
+ `/commands` and answers permission prompts and questions; a question answered on another surface is
562
+ dismissed. Observe mode is labelled read-only and sends nothing but reads.
563
+ - `/exit`, `/quit`, Ctrl-C and Ctrl-] detach. Nothing is sent to the session, so its turn continues and
564
+ it keeps running; stop it with `robota session stop` or from `robota session view`.
565
+ - The attached terminal is the full terminal UI (`renderAttachedApp` in `agent-ui-terminal`).
566
+ - A handshake from a connection that already closed no longer holds one of the session's attach slots.
567
+
568
+ - 724fabb: External-event grants are given at start, carried exactly to a background session, listed without their
569
+ principal, and revoked by the owner.
570
+
571
+ - `agent-framework` (breaking) — `openExternalEventSource` and `ExternalEventIngress.open` take only
572
+ `{ grant, audit? }`: the session builds each grant's verifier from `grant.verifier` with the
573
+ `externalEventVerifierFactory` its host passed when the session was built (without one, no grant opens), and an
574
+ open that supplies a verifier or a factory is refused. `IExternalEventSource.revoke()` stops the grant's queued and running turns,
575
+ refuses its later events as `grant-revoked`, and keeps the label from being opened again. A submission the
576
+ session refuses is `shutting-down` only while it shuts down, and `session-unavailable` otherwise. New command
577
+ host adapter `externalEvents` (`ICommandExternalEventsAdapter`).
578
+ - `agent-ui-terminal` — forwards `externalEventVerifierFactory` from the render options to the session.
579
+ - `agent-interface-transport` — `TExternalEventRefusal` gains `session-unavailable`.
580
+ - `agent-command` — `/events` lists the session's grants (label, principal kind, state, counts) and
581
+ `/events revoke <grant-id>` withdraws one. User-only.
582
+ - `agent-cli` — a grant file (`grantId`, `issuer`, `resource` ending in `/events/<grantId>`, exactly one of
583
+ `subject` or `client`, `scopes`, optional `algorithms` and `rate`) is validated before anything starts, with a
584
+ reason that names the grant and no configured value. `robota --external-event-grant <file>` (TUI) and
585
+ `robota session start --background --external-event-grant <file>` open every grant or fail the start; a
586
+ background session receives its grants through a private file, opens them before it reports ready, and the
587
+ launcher refuses a readiness that names other grants. `robota session events list <id> [--json]` and
588
+ `robota session events revoke <id> <grant-id>` work over the generation-bound control socket, and
589
+ `robota session list --format json` shows each grant's counts. The retired `--external-event-allow` now points
590
+ at `--external-event-grant`.
591
+
592
+ - d877de2: External events arrive over HTTP: `POST <public-url>/events/<grant-id>`, on a loopback port behind the owner's own
593
+ HTTPS proxy or tunnel.
594
+
595
+ - `agent-transport` — `./node` exports the resource-server gate every token-admitted HTTP carrier shares:
596
+ `createBearerResourceServer` (Host and Origin checked against the public URL, trusted-proxy `X-Forwarded-For`,
597
+ a failure-only throttle per address, coarse address classes), `describeProtectedResource` (RFC 9728 metadata
598
+ path and body, RFC 6750 challenges), `serveProtectedResourceMetadata`, `refuseBearerToken` and
599
+ `parsePublicHttpsUrl`.
600
+ - `agent-transport-mcp` — the remote MCP gate is composed from that shared gate; its answers are unchanged.
601
+ - `agent-cli` — each grant is its own endpoint and audience (its resource is `<public-url>/events/<grant-id>`).
602
+ An admitted event answers `202 {"turnId"}`; refusals have an empty body: 401 with the `invalid_token` or
603
+ missing-token challenge, 403 `insufficient_scope` or a revoked grant, 400 a malformed event, 404 an unknown
604
+ grant, 413 a body over 16 KiB, 429 over the grant's rate or the address's failure budget, 503 when the issuer
605
+ or the session cannot take it. `--external-event-port <port>` (required with grants) and
606
+ `--external-event-trusted-proxy <ip>` work on the TUI and on `robota session start --background`. A background
607
+ session writes an owner-only, bounded JSONL trail of refusals and settlements under its supervised directory;
608
+ the TUI reports them on one line each.
609
+
610
+ - 0d78a17: An MCP server is no longer a carrier for external events: a server connection proves nothing about who wrote a
611
+ message, and external events are now admitted only by a verified access token.
612
+
613
+ - `agent-mcp` (breaking) — `MCP_EXTERNAL_EVENT_CAPABILITY`, `MCP_EXTERNAL_EVENT_METHOD`, `IMCPExternalEvent` and
614
+ `TMCPExternalEventListener` are removed, as are `IMCPSession.externalEventsDeclared`,
615
+ `IMCPSession.onExternalEvent`, `IMCPSession.onClose` and `MCPConnectionSupervisor.onExternalEvent`. A server
616
+ that declares `com.Robota.external-event` or sends its notification is served as any other server and the
617
+ notification is ignored. The `zod` dependency is dropped.
618
+ - `agent-cli` (breaking) — `IMcpClientComposition.subscribeExternalEvent` and
619
+ `IMcpServerConnection.onExternalEvent` are removed.
620
+
621
+ - bfe8ed5: An external event is admitted only by a bearer access token the session verifies itself, and its sender is the
622
+ grant that token matched, never a name in the event.
623
+
624
+ - `agent-interface-transport` — `IExternalEventGrant` (a label, an access-token verifier configuration that pins
625
+ exactly one subject or client, the `message` kind, optional turn-rate windows), `IExternalEventDelivery` (the
626
+ token and the event as a carrier received them), the closed `TExternalEventRefusal` set, `TExternalEventAdmission`
627
+ and the content-free `TExternalEventAuditRecord`.
628
+ - `agent-framework` (breaking) — `ExternalEventIngress.open` and `InteractiveSession.openExternalEventSource` take
629
+ `{ grant, verifier, audit? }` instead of `{ id, allowedSenders, authenticate }`, and `receive` takes
630
+ `{ token, event }`. A delivery is refused with a stable word when the token is missing or the verifier refuses
631
+ it, when the token was already spent on an event (`jti`), when the event is malformed or oversize, or when the
632
+ grant is over its rate; nothing refused reaches the queue. An admitted event is attributed
633
+ `external:<grant>:<conversation>`, a payload display name appears in the envelope only as `claimed-name`, and the
634
+ receipt (`TExternalEventReceipt`) answers at acceptance with the turn id. Every refusal and settlement is
635
+ reported to the `audit` sink without content, conversation, name or token. `IAuthenticatedExternalEvent` and
636
+ `IExternalEventReceipt` are removed.
637
+ - `agent-cli` (breaking) — `--external-event-allow` is refused with the reason: a sender name relayed by an MCP
638
+ server does not prove who sent an event.
639
+
640
+ - 1706d29: `robota session view` attaches to and peeks at sessions.
641
+
642
+ - `a` attaches to the selected session to drive it, and `p` peeks at it read-only. Both are offered, in
643
+ the footer and in help, only on a row that is alive, controllable and verified. The view asks first,
644
+ naming the role. The yes is held to the process start the row showed: if the session restarted or
645
+ lost control meanwhile, the attach is refused. Detaching returns to the view, on the same row and
646
+ grouping, without printing the screen-reader line again.
647
+ - **Breaking:** opening a row's linked PR moves from `p` to `o`.
648
+ - The attached view shows the keys for what is on screen: sending, answering a permission, or
649
+ answering a question. With a screen reader it words every line ("Prompt from attach:2: …",
650
+ "Tool started: …", "Permission needed: …") and uses no symbols.
651
+ - Questions wait their turn: a second permission request or question no longer replaces the one on
652
+ screen, and one settled elsewhere leaves the queue.
653
+ - Line breaks inside pasted text become spaces instead of sending the text. In a masked answer they are
654
+ dropped, so a copied key that ends with a line break is kept exactly.
655
+ - `robota session attach` accepts `--screen-reader` and `--no-screen-reader`.
656
+ - The attach client closes a connection that sends more frames than it can hold before the view reads
657
+ them.
658
+
659
+ - 28fa8a7: `/devices add` and `/devices join` enrol a new device into your devices.
660
+
661
+ - On a device that holds the signing key, `/devices add` shows a one-time code on the terminal. It
662
+ works once, for five minutes.
663
+ - On the new device, `/devices join [name]` asks for the code on the terminal, creates the device's
664
+ keys, and meets the other device through the signaling relay in
665
+ `transports.webrtc.options.relayUrl`. Both devices need that setting.
666
+ - The new device proves the code over the WebRTC connection's DTLS fingerprints before anything else
667
+ crosses, so a relay in the middle cannot enrol anyone.
668
+ - Both devices then show the same six digits, and both operators compare them and confirm. Someone
669
+ who saw the code cannot choose the digits: the new device commits to its part before it sees the
670
+ existing device's.
671
+ - Only when both operators say yes does the existing device certify the new one and issue a new
672
+ roster. The new device keeps its identity only once the chain it receives verifies.
673
+ - A wrong, expired or already used code is refused. So is a code after a few failed attempts. When
674
+ either operator declines, nothing is issued or kept.
675
+ - The code appears only on the two terminals. It never reaches history, transcripts or the model. A
676
+ code typed as a command argument is refused. Both commands stay user-only and refuse remote
677
+ surfaces.
678
+ - `agent-remote-pairing`: enrollment codes, the enrollment proof, the signed request with its
679
+ commitment, the short authentication string and the frame decoder.
680
+ - `agent-transport-webrtc`: `dialEnrollment` and `listenForEnrollment` provide a data channel bound to
681
+ the negotiated fingerprints.
682
+
683
+ - 4f49d14: The device mesh can be turned on, and `/peers`, `/handoff` and `/devices` reach the user's other devices.
684
+
685
+ - New user setting `transports.mesh.enabled` (default `false`). When it is `true`, an interactive
686
+ session opens the device mesh at startup and closes it on exit. Only the user settings count; a
687
+ project's settings cannot turn it on. Print and serve runs never open it, and a device without an
688
+ identity is told to run `/devices init`. Only one session of a device opens it at a time; another
689
+ session on the same device says so in `/devices` and links nothing.
690
+ - By default a linked device may send messages, files and sessions. Each file and each session still
691
+ waits for the operator's yes on this machine's terminal; with no terminal the answer is no.
692
+ Delegating, observing and driving stay off unless `transports.mesh.options.capabilities` lists
693
+ them.
694
+ - A message from a linked device arrives like one from a session on this host: a peer turn with no
695
+ authority, attributed to the device the handshake proved, under the same rate limit and
696
+ conversation limits.
697
+ - `/peers` lists linked devices beside the sessions on this host; `/peers send` and
698
+ `/peers send-file` take a device id. `/handoff` lists linked devices and pushes the session to one.
699
+ Files arrive in the usual place aside, and sessions are saved without starting.
700
+ - `/devices` shows whether the mesh is on, how this device finds the others, and which are linked.
701
+ - The mesh uses the relay at `transports.webrtc.options.relayUrl` when one is set. Without one it
702
+ finds devices on the local network and over the public ways in `transports.mesh.options`.
703
+ - `/peers`, `/handoff` and `/devices` stay user-only. Their descriptions tell the model they cover
704
+ linked devices.
705
+ - `agent-framework`: the `/peers` port can list linked devices (`listDevices`, `ILinkedDeviceSummary`).
706
+ - `agent-command`: the `/devices` port can report the mesh (`meshStatus`, `IDevicesMeshStatus`).
707
+
708
+ - 963a4e0: Where no direct path joins two of one user's devices, a TURN relay on one of the user's own devices carries the
709
+ connection.
710
+
711
+ - `agent-transport-webrtc` — `TurnServer`, a pure-JavaScript TURN server over UDP (Allocate, Refresh,
712
+ CreatePermission, ChannelBind, Send/Data indications, ChannelData, long-term credentials) with quotas for
713
+ allocations per owner and in all, relayed bytes per second per owner, and allocation lifetime, and an optional
714
+ relayed-port range for a relay behind a NAT. Only what MESSAGE-INTEGRITY covers is read. Requests nobody has
715
+ authenticated are answered at a limited rate (per source and in all) and never with more bytes than they
716
+ carried, and forwarding into private ranges can be turned off (`allowPrivatePeers`). `MeshTurnRelay` runs it
717
+ for the devices of the roster: each pair derives a short-lived credential of its own (`meshRelayCredential`), a
718
+ device the lists drop or revoke can no longer allocate and loses its allocations. `DeviceMeshNode` takes
719
+ `relays` (the relays paired devices advertise, then configured TURN servers, and `relayOnly`) and
720
+ `relayServer`; a connection that needs a relay and has none is refused with `MeshRelayNeededError`, which says
721
+ why a relay was needed and carries the direct attempt's failure. `MeshDht` publishes this device's relay
722
+ endpoints in the sealed hints records (`relayEndpoints`) and reads the peers' (`relayAdverts`). DTLS stays end
723
+ to end; the relay only forwards it.
724
+ - `agent-remote-pairing` — the pair rendezvous derives a `relay-user` tag and `relayPassword`, the relay
725
+ credential's password for one direction and username.
726
+ - `agent-cli` — `transports.mesh.options` takes `relay` (`serve`, `port`, `host`, `publicAddress`, `relayPorts`,
727
+ `allowPrivatePeers`), `turnServers` and `relayOnly`. When the mesh is on, the session's mesh runs the relay,
728
+ advertises it to paired devices only, and uses the fallback order; `/devices` names the relays, and a device
729
+ that needs a relay is reported once with why. Running a relay, or relay-only without TURN servers, needs the
730
+ DHT or pkarr relays, which carry a relay's address to the other devices; a setting that could not work is
731
+ refused, naming it.
732
+
733
+ - 7b72344: Every client can offer the session's commands and skills and show its status. The GUI uses them as a
734
+ desktop-style composer.
735
+
736
+ - `agent-interface-session` is **`major` because `IInteractiveSession` gains required members**:
737
+ `ISessionCommands.listSkills()` and a `statusRead` capability, `getStatusSnapshot()`. The snapshot
738
+ holds session, model, permission mode, effort, context and goal. An external implementation stops
739
+ compiling until it adds both.
740
+ - `agent-transport` is **`major` for the same reason on `IProtocolSession`**. It also gains the wire
741
+ messages `get-commands` → `commands` and `get-status` → `session_status`. Both are reads that the
742
+ observe role may send.
743
+ - `agent-interface-command` gains `ICommandSkillListEntry`, moved from `agent-framework`, which
744
+ re-exports it unchanged.
745
+ - `agent-ui-web` is **`major` because its state changes shape**:
746
+ - A command's outcome and a finished turn's tool calls are now conversation entries: `messages` is
747
+ `TConversationEntry[]`.
748
+ - The `uiIntentNotices` list, `dismissUiIntentNotice`, `applyUiIntentEvent`,
749
+ `removeUiIntentNotice` and `IUiIntentNotice` are removed. An intent now answers with an info
750
+ line in the conversation.
751
+ - Session notices keep only `session-error` and `protocol-error`.
752
+ - The state gains `commandCatalog` and `sessionStatus`.
753
+ - `agent-command` registers `/theme` and `/keybindings` even without a terminal. They then answer
754
+ that they belong to the Robota terminal, instead of being unknown.
755
+ - `agent-framework`:
756
+ - `InteractiveSession.getStatusSnapshot()`.
757
+ - The main-thread row previews the last chat message instead of the last record's type.
758
+ - `agent-cli` serves the full GUI web app, renamed from `agent-cli-web` to `agent-gui-web`, on
759
+ `robota --serve --open`.
760
+
761
+ - 9ecffed: `robota daemon start | status | stop | unlock` runs one long-lived runtime per workspace for clients to attach to.
762
+
763
+ - **What a daemon is.** A supervised session marked as the workspace's daemon.
764
+ - Its control socket answers `connect` with the loopback WebSocket address, and only to a caller that names the daemon's current start.
765
+ - The token never touches disk or argv. The daemon removes it from its own environment, so its tools do not inherit it.
766
+ - **Starting.** `daemon start --json` prints one line, `{"id","url"}`, for a client host to read.
767
+ - Starts in one workspace take turns through a lock.
768
+ - A lock left behind by a start that is gone is never removed automatically. The start refuses and names `robota daemon unlock`.
769
+ - A daemon that cannot hand over its address fails its start and is not left running.
770
+ - **The desktop app attaches** to the workspace daemon and starts one only when none is running.
771
+ - Closing the window leaves the daemon running.
772
+ - If the daemon stops while the window is open, the window says so and offers Reconnect.
773
+ - **`agent-ui-web`.** The WebSocket session client reports when its retries are exhausted (`onGiveUp`, and `onConnectionLost` in `useWsSession`).
774
+
775
+ - 6ae3f28: A served runtime can list its workspace's sessions, start a new one and switch to another without the
776
+ process or any client connection restarting. The GUI shows them in a sessions sidebar.
777
+
778
+ - `agent-interface-session` is **`major`** for two reasons:
779
+ - It adds `ISessionListing`, `ISessionDirectory` and `ISessionSwitchedEvent`.
780
+ - `IInteractiveSessionEvents` gains a required `session_switched` event. An exhaustive map over the
781
+ event names stops compiling until it classifies the new event.
782
+ - `agent-transport` is **`major`** for the same kind of reason:
783
+ - It adds the wire messages `list-sessions` → `sessions`/`sessions_error`, `new-session`,
784
+ `switch-session`, and the broadcast `session_switched`.
785
+ - It adds a `sessionDirectory` handler option. A refusal to switch comes back as a
786
+ `protocol_error` carrying the reason.
787
+ - An exhaustive map over message types must add the new variants.
788
+ - `agent-transport-ws` passes a configured `sessionDirectory` to every connection.
789
+ - `agent-framework` is **`major` because `IRuntimeHostHandle.session` is now a `SessionSlot`**, not the
790
+ `InteractiveSession`, and `bindTransports` receives the slot.
791
+ - The slot is an `IInteractiveSession` that forwards to the current session. Members outside that
792
+ interface are read through `host.session.current`, which changes on a switch.
793
+ - Adds `InteractiveSession.whenInitialized()`.
794
+ - Exports `listUnreadableSessions`.
795
+ - `agent-ui-web` is **`major`**:
796
+ - `IWsSessionState` gains session listing state and actions.
797
+ - It adds a `SessionSidebar`.
798
+ - `/resume` opens the sidebar.
799
+ - `agent-cli`: `robota --serve` provides the session directory, and refuses a switch that would lose
800
+ work in progress. External-event grants belong to the run: a switch reopens them on the new session,
801
+ as the TUI already does.
802
+ - External-event grant history (spent tokens, rate windows, revocations) belongs to the run. The new
803
+ option `externalEventGrantHistory` (from `createExternalEventGrantHistory()`) is shared by every
804
+ session that a served runtime or the TUI builds. A session switch therefore replays no spent token
805
+ and resets no rate limit. The TUI previously had this gap when it switched sessions.
806
+
807
+ - 57f57f5: A daemon keeps several sessions live, and each client is bound to its own: one client's switch moves
808
+ only that client.
809
+
810
+ - `agent-interface-session`: `ISessionBinder`/`ISessionBinding`, the session-change refusal codes with
811
+ `isSessionChangeRefusal`, and listing rows that may say `live` and `clients`.
812
+ - `agent-framework`: `SessionPool` and `SessionChangeRefusal`.
813
+ - `agent-transport` (**major**): a new server frame, `session_change_failed`, which an exhaustive
814
+ consumer must handle. A refused `new-session` or `switch-session` now answers with it, carrying a
815
+ `code`, instead of `protocol_error`; both requests take an optional `requestId` that it echoes.
816
+ - `agent-transport-ws`: the `sessionBinder` option binds each connection to its own session and
817
+ releases the binding when the connection closes.
818
+ - `agent-cli`: `robota --serve` and the daemon keep up to four sessions live. Each WebSocket client and
819
+ attached terminal is bound to its own session; leaving a busy session is no longer refused, and only
820
+ the last driver of a session with a pending prompt is kept from leaving it. Grants and the supervised
821
+ name stay on the runtime's first session, and its reported activity covers every live session.
822
+ - `agent-ui-web`: a refused new or switch shows the host's reason as a notice (an older host's
823
+ `protocol_error` still does); the session sidebar marks live sessions and counts the other clients
824
+ on each; after a reconnect to a host that keeps sessions live, the GUI returns to the session it was on.
825
+ - `agent-ui-terminal`: an attached terminal shows why a switch was refused, and the picker's switch
826
+ stays pending until the host answers; the session picker marks live sessions and their clients.
827
+
828
+ - ba822c1: An attached client reads a workspace entry's detail, stops a waiting self-paced loop, completes
829
+ subcommands, and sees status changes another client made.
830
+
831
+ - `agent-interface-session` (major): `IInteractiveSession` gains the required roles
832
+ `ISessionExecutionDetail` and `ISessionSelfPacedLoopControl` (`TWaitingLoopStopOutcome`), and the
833
+ exhaustively mapped event map gains `status_changed`.
834
+ - `agent-transport` (major): `IProtocolSession` gains both roles; the wire unions gain
835
+ `read-execution-detail`, `stop-waiting-loop`, `execution_detail`, `execution_detail_error` and
836
+ `waiting_loop_stop`; `status_changed` is pushed as `session_status`; `isObserverMessageType` is
837
+ exported from the root and `./client`.
838
+ - `agent-interface-command` (minor): `ICommandListEntry` gains optional `argumentHint` and
839
+ `subcommands` (`ICommandSubcommandEntry`).
840
+ - `agent-cli` (minor):
841
+ - `robota session attach` and an attach from `robota session view` open the full terminal UI,
842
+ the one `robota --attach` opens, in drive mode or, with `--observe`, read-only. The reduced
843
+ attached view is gone.
844
+ - A served runtime names each of its sessions after the session's first real turn.
845
+ - `agent-framework` (minor):
846
+ - `InteractiveSession` gains `stopWaitingSelfPacedLoop(reason?)`: it stops the one waiting loop,
847
+ or stops none and names `/loop stop` when several wait. `SessionSlot` forwards it and
848
+ `readExecutionWorkspaceDetail`.
849
+ - The session emits `status_changed` when its mode, model, effort, goal or name changes, after a
850
+ command, a turn, a rename or a goal or plan transition.
851
+ - A new option `autoName: true` makes the session name itself once, after its first turn, with its
852
+ current provider; it keeps a name it already has and lets a rename made meanwhile win. Off by
853
+ default.
854
+ - The command catalog carries each command's `argumentHint` and `subcommands`.
855
+ - `agent-ui-terminal` (major):
856
+ - The full terminal UI attached to a host's session reads a workspace entry's detail, sends input
857
+ to a background task, stops a waiting self-paced loop on Esc, and completes subcommands.
858
+ - `renderAttachedApp` takes `mode: 'drive' | 'observe'` (default `'drive'`) and `announce`. In
859
+ observe mode the terminal sends only what an observer may send, refuses prompts, host commands,
860
+ abort and loop stop with a read-only notice, still runs `/exit` and its own commands, and its
861
+ status bar says it is observing. `ITuiChannelSnapshot` gains `readOnly`.
862
+ - The in-process terminal no longer names sessions; it builds its session with `autoName: true`.
863
+ `ITuiInteractionChannelOptions.onAutoNamed` is removed.
864
+
865
+ - 6e6b06b: `robota --attach` puts the full terminal UI on this workspace's running daemon.
866
+
867
+ - **`agent-ui-terminal`:**
868
+ - `renderAttachedApp` renders the same App over a wire connection, through `WireTuiChannel`, a second implementation of the TUI's channel port.
869
+ - Leaving detaches; the daemon keeps running.
870
+ - The session picker lists and switches the daemon's sessions. It opens on the daemon's answer to `/resume`; a daemon that cannot list its sessions says why instead.
871
+ - With no saved sessions to resume, `/resume` says so and keeps the prompt, instead of opening an empty picker that blocks input. This applies to the in-process TUI too.
872
+ - The session picker lists an unnamed session by the part of its id that differs, not by the `session_` prefix every id shares. This applies to the in-process TUI too.
873
+ - The status bar names the daemon's current session: its name, or else its short id.
874
+ - A question already open when the terminal attaches is shown, and a long session's history reaches the terminal without the daemon cutting it off.
875
+ - Features that belong to the runtime process say they are unavailable while attached: the plugin manager, background task details and sending to an agent job.
876
+ - **`agent-cli`:**
877
+ - `robota --attach` finds the workspace daemon and asks the user to confirm on the terminal, as `robota session attach` does, then attaches.
878
+ - Options that shape a session are refused, because the daemon shapes its session.
879
+ - A terminal attached over the supervised socket can now list, start and switch sessions.
880
+ - A client's command no longer stops or restarts the workspace daemon. `/language`, `/reset`, `/exit` and provider setup or switch keep their saved change, and the client is told to run `robota daemon stop`, then `robota daemon start`.
881
+ - `robota --attach` installs the plain TUI's process guards, so an error the plain TUI survives no longer ends the attached terminal.
882
+ - A client's command no longer stops or restarts any supervised session either; the client is told to run `robota session stop <id>`, and the saved change applies to a new session from `robota session start --background`.
883
+
884
+ ### Patch Changes
885
+
886
+ - d61e159: The session protocol gains a read-only `observe` role.
887
+
888
+ - `createSessionMessageHandler({ role: 'observe' })` follows the session's conversation, streaming,
889
+ tool, background, usage and workspace events and answers reads of this session. The default role
890
+ stays `drive`, which is unchanged.
891
+ - An observer never subscribes to `permission_request` or `ask_request`. A session with only observers
892
+ attached therefore still denies a permission and cancels a question at once instead of waiting for a
893
+ surface that may not answer. Observers still see `prompt_resolved`.
894
+ - An observer cannot submit, run commands, abort, cancel the queue, control background tasks, answer
895
+ prompts, or read personal or other sessions' usage reports. Each such message is answered with a
896
+ `protocol_error` and changes nothing. Only an explicit list of reads is accepted, so a message type
897
+ added later is refused to observers until it is classified.
898
+ - `ATTACHED_SURFACE_MAX_PENDING_BYTES` (1 MiB) is the backpressure budget a carrier passes to
899
+ `createOutboundDelivery` for a terminal attached on the same host. A reader that falls that far
900
+ behind is cut off and reported to the carrier, and other surfaces keep streaming. No carrier uses it
901
+ yet.
902
+ - `agent-cli`: a supervised session's control endpoint no longer includes its generation in a
903
+ refusal to a caller that did not present it.
904
+
905
+ - 8625a14: Supervised session control is bound to one process start.
906
+
907
+ - Each supervised session start writes a fresh random generation into its private registration. Its
908
+ control endpoint echoes that generation in every reply and refuses any request that carries a
909
+ different one, or none.
910
+ - `session view` keeps the generation each row was verified with. Stop and linked-PR opening send it,
911
+ so a session that restarted under the same id since the row was shown is refused instead of acted
912
+ on. A stop confirmation also refuses when the row's generation changed while it was open.
913
+ - `session stop`, `rename`, `link-pr` and `unlink-pr` read the generation at request time and the
914
+ session itself checks it, closing the window between the liveness check and the action.
915
+ - A registration without a generation (written by an earlier version) is listed but never
916
+ controllable; restart that session to control it again.
917
+ - The generation is never printed by `session list` in text or JSON.
918
+
919
+ - 997f2fb: A revoked or closed external-event grant checks the token before it answers, so only a caller holding a valid
920
+ token for that grant learns it was revoked; anyone else gets exactly the refusal a live grant gives. The TUI keeps
921
+ revoked grants open-and-revoked on each session it binds so their tokens are still checked. The per-grant counters
922
+ now also count the refusals the HTTP endpoint decides itself (a missing token, an oversize body).
923
+ - e689c8e: Follow-ups to connection approval and `/handoff`.
924
+
925
+ - `agent-transport-webrtc-web` — the browser remote client says `Waiting for the host to approve this connection…`
926
+ (`awaiting-approval`) after pairing, and `Connected` only once the host's session answers. A host that closes the
927
+ channel instead is shown as `refused` and is not retried, since a retry would only ask the operator again; a
928
+ first connection lost before the host answered is `failed`. Reconnect attempts count until the host admits a
929
+ connection, so a link that keeps dropping while approval is pending gives up instead of asking again and again.
930
+ - `agent-core`, `agent-session`, `agent-framework` — a turn that did not come from the operator stores its
931
+ `turnSource` (`peer`, `external`, `agent-wakeup`) beside `driverId` on the user message and in the display
932
+ history (`IRunOptions.turnSource`), so a session handed off keeps where each turn came from.
933
+ - `agent-cli` — a `/handoff` resent after a lost confirmation reports what stays behind as it is now, not as it was
934
+ at the first attempt, and after refusing to resend a session that changed, the hand-off status shows that refusal
935
+ instead of the earlier lost confirmation.
936
+
937
+ - 6e4c6ee: Device mesh discovery holds up better against endpoints and peers that misbehave.
938
+
939
+ - A direct path's admission deadline starts only from a `hello`, `offer` or `answer`, so ICE candidates
940
+ that trail an admitted connection no longer set a working path aside.
941
+ - An endpoint or signaling carrier that keeps failing is set aside for longer each time it fails
942
+ again, until a connection over it is admitted.
943
+ - An mDNS lookup keeps listening briefly after the first matching answer, so a faster answer from
944
+ another host cannot hide the peer's own.
945
+ - The device lists read from public records are bounded per paired device, each device's newest
946
+ before any device's next, and the chunks of one list carry a shared version, so a read that finds
947
+ chunks of two versions yields no list. Records written before this change are not read as lists.
948
+ - CLI processes that share `$PRODUCT_USER_STATE_DIR/devices/address-cache.json` apply each change to the file as it
949
+ is on disk, so one process no longer overwrites what another learned.
950
+
951
+ - 4241fc5: Device mesh follow-ups.
952
+
953
+ - A device with no identity is pointed to `/devices add` on one of the user's devices and `/devices join`
954
+ here, as well as to `/devices init`. The message says that `init` is for the first device only,
955
+ because it creates a separate identity that can never link to the user's other devices. The `/devices`
956
+ description and the `init` subcommand say the same.
957
+ - An identity created mid-session (`/devices init`, or a successful `/devices join`) opens the mesh
958
+ without a restart when `transports.mesh.enabled` is on.
959
+ - If a session stalls for longer than the mesh lock's stale window (for example while the machine
960
+ sleeps), another session can take the mesh over. The stalled session now notices this on its next
961
+ lock refresh, closes its own mesh, and says why. Two sessions no longer run it together.
962
+ `holdExclusiveFileLock` has a new `onLost` option for this.
963
+ - `/peers` and `/handoff` still list and reach linked mesh devices when local same-host peer discovery
964
+ fails. `/peers` says why sessions on this host are not listed. `ICommandLocalPeersAdapter` has a new
965
+ optional `localDiscoveryOff` field for this.
966
+ - `agent-transport-webrtc`: when lists become newer on a node (reissued, revoked, or adopted from a
967
+ peer), the node sends them over every admitted connection instead of waiting for the next handshake.
968
+ Reissues, revocations and enrolments in the CLI take effect this way at once. A receiver adopts a
969
+ pushed list only if it is newer, issued by this user's signing key, and verifies. The push does not
970
+ depend on the peer's capabilities, and it never reaches the application's message handlers.
971
+
972
+ - 05d5391: The embedded TURN relay says why it cannot listen — the port is taken, the host is not an address of
973
+ this machine, or the port needs privileges — and keeps the bind error as `cause`; the CLI names the
974
+ relay setting that cause asks you to change. Mesh options are no longer checked while
975
+ `transports.mesh.enabled` is off, so a mistake in them does not report that the mesh could not start.
976
+ - bbe9689: On a host that cannot prove a project write stays inside the project (every platform but Linux), a
977
+ trusted workspace no longer composes project memory, whose every save would be refused there. Memory
978
+ is shared through the repository, so it is not moved to a per-user store: turning it on prints once
979
+ that it stays off and why, and the run continues instead of failing on the first capture. Linux is
980
+ unchanged.
981
+ - 9721162: A trusted workspace saves its sessions on macOS and other hosts without Linux's project-write guarantee.
982
+
983
+ - **`agent-framework`:** `supportsWorkspaceProjectMutation(platform?)` tells a host in advance whether project writes can be proven to stay under the trusted root. The project writer uses the same answer, so the two cannot disagree.
984
+ - **`agent-cli`:**
985
+ - Where project writes cannot be proven safe, a trusted workspace keeps its sessions in the user session store (`$PRODUCT_USER_STATE_DIR/sessions`). They are listed for that workspace by their working directory, so `/resume`, the sessions sidebar, `--continue` and `robota session list` find them. Nothing is written under the project root.
986
+ - Before this, every save in a trusted git workspace on macOS was refused, and those surfaces listed nothing.
987
+ - Linux keeps trusted sessions in the project, as before.
988
+ - `robota session list`, `robota usage` and `robota session analyze` report such sessions as user sessions, never as project sessions.
989
+
990
+ - 57280bf: Every published package now declares `"engines": { "node": ">=22.12.0" }`. Before, 27 of the 38
991
+ packages declared no floor (`agent-core`, `agent-tools` and every provider among them),
992
+ `agent-session` and `agent-file-authority` declared `>=20.19.0`, and the other nine declared
993
+ `>=22.0.0`, so a consumer on Node 20 saw at most a warning from a transitive dependency.
994
+
995
+ Why 22.12: `agent-cli` and `agent-ui-terminal` need Node 22 through `ink` 7, and the CommonJS entries
996
+ of `agent-tools` and its dependents, `agent-transport`/`node` and its dependents, and
997
+ `agent-ui-terminal` `require()` ESM-only dependencies (`p-limit`, `jose`, `chalk`), which Node 22
998
+ supports unflagged only from 22.12. `engines` is advisory unless the consumer enables `engine-strict`.
999
+
1000
+ No code changes: `tsdown` now reads `node22.12.0` as its build target from the field.
1001
+
1002
+ - 1efb124: `@robota-sdk/agent-cli` and `@robota-sdk/agent-ui-terminal` are now ESM-only. Their CommonJS entry
1003
+ could never load: it pulls in Ink, whose `yoga-layout` dependency starts with a top-level `await`, so
1004
+ `require()` failed on every Node version with `ERR_REQUIRE_ASYNC_MODULE`. The packages no longer
1005
+ declare a `require` condition or ship `index.cjs`/`index.d.cts`, so `require()` now fails at resolution
1006
+ with `ERR_PACKAGE_PATH_NOT_EXPORTED`. Load them with `import` or `import()`, which is unchanged. The
1007
+ `robota` executable is unaffected.
1008
+ - 18c0d5c: Every published package now exports `./package.json`, so `require('<package>/package.json')` and
1009
+ `import('<package>/package.json', { with: { type: 'json' } })` work instead of failing with
1010
+ `ERR_PACKAGE_PATH_NOT_EXPORTED`, and each tarball now ships the package's `CHANGELOG.md`.
1011
+ - caaab20: `/rewind` works again. Every session the CLI builds for a trusted workspace captures edit
1012
+ checkpoints; before, none did, and `/rewind` failed everywhere with "Edit checkpoints require
1013
+ project authority."
1014
+
1015
+ - `agent-cli` (patch): a trusted workspace composes an edit checkpoint store for the terminal UI, a
1016
+ print run, a served runtime and each session a daemon keeps live. Every session gets its own
1017
+ 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
1018
+ stays inside the project (every platform but Linux), none is composed: a checkpoint could be
1019
+ neither saved nor restored there.
1020
+ - `agent-command` (patch): where a session has no checkpoints, `/rewind` says why: a restricted
1021
+ workspace is told to run `robota trust --yes` and restart Robota, and a host that cannot write
1022
+ the project safely says so. `/rewind list` reports this as a failed command instead of throwing.
1023
+ - `agent-framework` (minor): a checkpoint operation on a session without a store throws
1024
+ `EditCheckpointsUnavailableError`, whose `reason` is `host-cannot-write-project`,
1025
+ `restricted-workspace` or `no-checkpoint-store`. It is a `WorkspaceAuthorityRequiredError`, as
1026
+ before, named `EditCheckpointsUnavailableError`. `HeadlessInteractionChannel` takes an
1027
+ `editCheckpointStore` option.
1028
+ - `agent-ui-terminal` (minor): `renderApp` takes `createEditCheckpointStore` in place of
1029
+ `editCheckpointStore`, and builds each session its own store, since a session switch can build the
1030
+ next session before the old one's turn ends. `TuiInteractionChannel` keeps `editCheckpointStore`:
1031
+ a channel runs one session.
1032
+
1033
+ - 227ff3a: The GUI session surface (desktop app, `robota --serve --open`) is redesigned as a calm desktop app: readable
1034
+ type in a bundled Pretendard, monospace only for code and paths, surfaces separated by tone instead of
1035
+ outlines, and light and dark themes that follow the system. Agent replies read as prose, your messages sit
1036
+ in bubbles, tool calls are single quiet lines, and the composer carries the model, mode and effort. The
1037
+ permission prompt now shows what the tool was asked to run, and the title bar names the current session.
1038
+ - f01868f: `sandbox.autoAllowBashIfSandboxed` takes effect. The CLI confined shell commands in the OS sandbox,
1039
+ but no session learned about the sandbox, so a confined command still asked for approval in the
1040
+ terminal UI, a served runtime and MCP serve, and a print run with no one to approve it refused it.
1041
+
1042
+ - `agent-cli` (patch): a served session, and each session a daemon keeps live, receives the sandbox
1043
+ the shell tools run under.
1044
+ - `agent-framework` (minor): `HeadlessInteractionChannel` takes a `sandboxClient` option and hands it
1045
+ to the session.
1046
+ - `agent-ui-terminal` (minor): `renderApp` and `TuiInteractionChannel` take a `sandboxClient` option
1047
+ and hand it to each session they build.
1048
+
1049
+ - e8779c9: A subagent consults the sandbox its shell tools run under, as its parent does. With
1050
+ `autoAllowBashIfSandboxed` on, a confined command the subagent's gate leaves to the mode now runs
1051
+ without a prompt. Before, a `context: fork` skill asked for approval, and a print run refused it.
1052
+ An Agent-tool subagent in `auto` mode sent a command allowed by a broad rule like `Bash(npm *)` to
1053
+ its classifier instead. A background policy's ceiling is still checked first: nothing outside it
1054
+ runs.
1055
+
1056
+ - `agent-framework` (minor): `createSubagentSession` takes a `commandSandbox` option. The fork and the
1057
+ in-process runner derive it from the parent's sandbox, the instance their inherited tools run
1058
+ under. `sandboxApprovalFor` is exported.
1059
+ - `agent-subagent-runner` (minor): `ISubagentWorkerComposition` takes an optional `createSandbox`. The
1060
+ worker builds that sandbox once and hands the same instance to `createTools` and to the session.
1061
+ - `agent-cli` (patch): Robota's worker composition builds the OS sandbox through `createSandbox`, so
1062
+ a child-process subagent approves what its parent approves.
1063
+
1064
+ - f8a8a4d: The GUI surface's design now applies inside a `agent-ui` scope that each of its root components opens,
1065
+ so an app with design tokens of its own can embed the surface without either overriding the other. Such
1066
+ an app imports `@robota-sdk/agent-ui-web/styles/surface.css` into its Tailwind entry; a page that is only
1067
+ the surface keeps importing `styles/theme.css` and puts `agent-ui` on its `<html>`. `ProductMark` and
1068
+ `ProductWordmark` are exported. The browser remote client (`RemoteClient`) follows the same design, with
1069
+ its pairing states centred on the page.
1070
+ - 122e82b: A child-process subagent confines and approves commands with its parent's sandbox settings as they
1071
+ stand when it starts. Before, it read its root's settings files, so a `/sandbox` change made in the
1072
+ session did not reach it. `/sandbox` changes the live sandbox and only the user settings file, which
1073
+ project settings outrank. A worktree child also missed the parent's untracked local settings.
1074
+
1075
+ - `agent-subagent-runner` (minor): the runner factory takes `parentSandboxSettings`, read at each
1076
+ spawn. The start payload carries it, the IPC guard checks it is a record, and the worker hands it to
1077
+ `createSandbox` as `parentSettings`.
1078
+ - `agent-cli` (patch): Robota sends its live sandbox's settings, and a child builds its sandbox from
1079
+ them. It refuses settings it cannot read rather than falling back to the files.
1080
+
1081
+ - 0368058: A running child-process subagent follows a `/sandbox` change made in its parent session. Before, it
1082
+ kept the settings it started with until it ended, so a long-running `auto`-mode subagent went on
1083
+ auto-approving confined commands after the user turned auto-allow off.
1084
+
1085
+ - `agent-tools` (minor): `OsSandboxClient.watchSettings(watcher)` reports each `configure`, until
1086
+ its returned function is called.
1087
+ - `agent-subagent-runner` (minor): the runner factory takes `watchParentSandboxSettings`, and
1088
+ forwards each change to every running child as a `sandbox_settings` message. It watches before
1089
+ reading the start payload, so no change is lost while a child starts, and stops when the child
1090
+ exits or fails to start. A composed sandbox may define `applyParentSettings`. The worker applies
1091
+ each change through it. On settings it cannot take, it aborts the run, lets a running command
1092
+ finish, and ends the run with that error.
1093
+ - `agent-cli` (patch): Robota watches its live sandbox, and a child applies each change to the
1094
+ instance its tools and approval read.
1095
+
1096
+ - 5a0ee96: Workspace trust no longer drops on its own. A grant was keyed by the repository config file and the
1097
+ volume's device number, so `git push -u`, a branch rename or delete, `git remote add`, or macOS
1098
+ renumbering a volume left a trusted workspace untrusted. Where the filesystem records a birth time
1099
+ (APFS, ext4, NTFS, …), the key is now the git directory's own inode and birth time, which hold through
1100
+ all of these while a repository recreated at the same path still does not inherit the grant; elsewhere
1101
+ the stricter config-based key stays. Granting or revoking also retires a trusted record left for the
1102
+ same worktree under an earlier key, so it no longer counts as a second trusted repository; it is
1103
+ marked revoked rather than deleted, so a repository that returns to that path never regains a
1104
+ generation an approval (such as a project MCP server's) was recorded against.
1105
+
1106
+ Grants made before this change are keyed the old way: run `robota trust --yes` once more in each
1107
+ workspace. A workspace revoked before shows as untrusted until it is trusted or revoked again.
1108
+
1109
+ ## 3.0.0-beta.82
1110
+
1111
+ ### Minor Changes
1112
+
1113
+ - e15e22b: Two of one user's devices can connect to each other over WebRTC, admitted by the device handshake.
1114
+
1115
+ - `agent-transport-webrtc` — `DeviceMeshNode` keeps one connection per device pair, in either role: the
1116
+ device with the lower id offers and the other answers, so concurrent attempts resolve by rule. Both
1117
+ roles bind the handshake to the certificate the DTLS layer verified and take one remote description
1118
+ with exactly one fingerprint; before admission only handshake frames cross, and a connection counts
1119
+ as admitted only once both sides admitted each other. Each connection gets its own DTLS certificate
1120
+ (`createDtlsKeys`). A new attempt never displaces an admitted connection until it is admitted
1121
+ itself, and attempts per pair are paced. Lists adopted in a handshake, or handed over through
1122
+ `refresh`, apply at once, and a device they revoke loses its connection. Signaling runs through
1123
+ `IMeshRelay`: `WsMeshRelayClient` for the self-hosted relay's `presence` / `message` frames
1124
+ (topics capped per source and relay-wide), `createInMemoryMeshRelayHub` for tests.
1125
+ - `agent-remote-pairing` — the pair's two relay inbox topics, one per direction, come from their
1126
+ pairwise secret (`derivePairRendezvous(...).relayInbox()`).
1127
+ - `agent-cli` — a device holding the signing key reissues its roster and revocation list before they
1128
+ expire while an interactive session runs. The host can open this device's mesh endpoint from the
1129
+ identity under `$PRODUCT_USER_STATE_DIR/devices`, saving newer lists a peer hands over; no command starts it yet.
1130
+ - `agent-command` — `/devices add|join` still reports that enrolment is not available yet.
1131
+
1132
+ - e15e22b: The Node WebRTC transport runs on `node-datachannel` (libdatachannel, DTLS by OpenSSL). The DTLS stack
1133
+ verifies handshake signatures, so a channel binding names the party that holds the certificate's key.
1134
+ It is an optional dependency with a prebuilt binary per platform; where it cannot load, the WebRTC
1135
+ transport reports itself unavailable instead of falling back to another implementation.
1136
+
1137
+ - `agent-transport-webrtc` — `RtcPeer` / `RtcChannel` wrap one connection; `loadDataChannel` replaces
1138
+ `loadWerift` (and the `loadWerift` option becomes `loadDataChannel`). Every connection has its own DTLS
1139
+ certificate, no ICE server is contacted unless configured, and `werift` is no longer a peer dependency.
1140
+ A pairing peer that finishes the handshake first and speaks at once no longer has that frame dropped: it
1141
+ is held until this side accepts, and discarded if it does not.
1142
+ - `agent-cli` — depends on `node-datachannel` (optional) instead of `werift`.
1143
+
1144
+ - c7f9203: Connected sessions can send each other files.
1145
+
1146
+ - `/peers send-file <session-id> <path>` sends a copy of any file the operator can read to another
1147
+ live session on this host.
1148
+ - The model sends a file only through the `peer_send_file` tool. Every call asks the user, showing
1149
+ the path, size, hash and destination; no permission mode, rule or remembered consent answers it.
1150
+ The tool reaches only files inside the workspace whose path does not look like it holds secrets
1151
+ (`.env*`, `~/.ssh`, keys and credentials), and it does not exist in a turn a peer's message started.
1152
+ - The receiving operator approves every file. A received file is kept as an inert copy (mode 0600)
1153
+ under `$PRODUCT_USER_STATE_DIR/peer-files/<sender>/`. It is never run and never placed in the model's context.
1154
+ The conversation is told only its name, size and sha256. A name that leaves that directory is
1155
+ refused, a symbolic link is never written through, and nothing is overwritten.
1156
+ - Transfers travel on a channel of their own (a separate connection on this host, a separate data
1157
+ channel between devices), in chunks the receiver paces, up to 32 MiB, and are kept only when the
1158
+ whole content matches the offered sha256. A transfer that ends early is discarded; there is no
1159
+ resume.
1160
+
1161
+ **API**
1162
+
1163
+ - `agent-interface-session-mobility`: the `file` capability, which asks the operator for every
1164
+ request; `ConnectionAuthority.authorizeFile`; `IFileOffer` and `IFileFrameChannel`.
1165
+ - `agent-transport/node`: `sendFileOverChannel` and `receiveFileOverChannel`, the carrier over any
1166
+ `IFileFrameChannel`; `DEFAULT_MAX_FILE_BYTES`.
1167
+ - `agent-transport-webrtc`: `IDeviceMeshLink.openFileChannel` and `onFileChannel`.
1168
+ - `agent-remote-pairing`: `file` joins `DEVICE_CAPABILITIES`. A device certificate that names it is
1169
+ refused as malformed by an earlier version.
1170
+ - `agent-core`: `IToolPermissionProfile.notInPeerTurn` withholds a tool from a turn a peer's message
1171
+ started.
1172
+ - `agent-framework`: `ICommandLocalPeersAdapter.prepareFile`.
1173
+
1174
+ - 004fe7f: `/handoff` moves a session over a real connection.
1175
+
1176
+ - `/handoff <session-id>` pushes this conversation to another Robota session on this machine. The
1177
+ same carrier also runs between two of the user's devices over their mesh connection; no command
1178
+ opens that connection yet.
1179
+ - A hand-off is push-only: only the operator of the session that holds it starts one. A session or
1180
+ device that asks another for its session is refused.
1181
+ - The source signs a grant for that one transfer over that one channel with its device key, so a
1182
+ hand-off needs the device identity from `/devices init`. The receiving side checks it against the
1183
+ sender's certificate, then asks its own operator; without a yes nothing is sent.
1184
+ - The session travels on the file-transfer carrier, is kept aside until it matches the manifest, and
1185
+ is saved without being started. The operator there resumes it with `robota --resume <id>`.
1186
+ - The source gives the session up, and ends, only once the receiving side confirms it saved it.
1187
+ Every other outcome leaves the session where it was; if the confirmation is lost, `/handoff` to the
1188
+ same session again resends the same transfer, which the receiver settles without saving it twice.
1189
+ Peer attribution (`driverId`, `turnSource`)
1190
+ travels with it.
1191
+ - `/handoff` stays user-only. Its description tells the model to suggest the command to the user.
1192
+ - `agent-transport-webrtc`: an admitted mesh link exposes the DTLS fingerprints it is bound to, and
1193
+ `judgeHandoffGrant` is exported.
1194
+ - `agent-interface-session-mobility`: a hand-off carrier may move the sealed payload whole
1195
+ (`sendPayload`), the destination verifies it with `receivePayload`, and the source can report its
1196
+ open transfer (`status`) and abandon it for any refusal the destination names.
1197
+
1198
+ - 189c21e: The device mesh can find and signal a peer device beyond the local network, through public
1199
+ infrastructure that sees only signed ciphertext.
1200
+
1201
+ - **Rendezvous records (BEP 44):** each device publishes, per peer, its connection hints and the newest
1202
+ device revocation lists it holds as mutable items on the BitTorrent Mainline DHT (`bittorrent-dht`,
1203
+ directly over UDP), or through pkarr relays (`createPkarrRelayStore`, for clients that cannot reach
1204
+ the DHT). Every record is signed by a one-time key of the pair, direction, epoch and purpose, stored
1205
+ under a rotating salt, AEAD-sealed and padded to a fixed size; publish times are jittered per pair.
1206
+ Lookups read the peer's records at the current and adjacent epochs and reject anything that does not
1207
+ verify or open for the pair. Lists too large for one record are split into fixed-size chunks.
1208
+ `MeshDht` is the candidate source and publisher, and its `latestLists` feeds the freshness lookup
1209
+ before a remote admission with every list candidate found, newest first.
1210
+ - **Nostr signaling:** `NostrMeshRelay` carries live SDP/ICE as ephemeral events on several relays,
1211
+ under per-epoch, per-direction keys and kinds, with our own AEAD over the payload and no tags.
1212
+ - **Order:** `DiscoveringMeshRelay` tries the address cache, mDNS, then DHT records for candidates,
1213
+ and when none answers the signaling carriers in order — Nostr, then the self-hosted relay. A
1214
+ carrier that carries no admission in time is set aside for the next one.
1215
+ - **Defaults:** `DEFAULT_NOSTR_RELAYS` and `DEFAULT_PKARR_RELAYS` list well-known relays of several
1216
+ operators; `transports.mesh.options` (`dht`, `pkarrRelays`, `nostrRelays`) replaces them, parsed by
1217
+ the CLI's `parseMeshInternetSettings`, and `openDeviceMesh` takes them as `internet`. No command
1218
+ starts the mesh yet.
1219
+ - `agent-remote-pairing`: `signingSeed`, `sealRecord` and `openRecord` take a record purpose
1220
+ (`hints`, the default and unchanged; `revocation`; `signal`), and two tag purposes are added. The
1221
+ device handshake's `fetchLatestLists` may return several candidates per list kind; each is verified
1222
+ and the newest that verifies counts, so a forged "newer" list cannot hide a real one.
1223
+ - `agent-cli` declares the mesh's runtime dependencies (`bittorrent-dht`, `nostr-tools`,
1224
+ `multicast-dns`), which its bundle leaves external.
1225
+
1226
+ **Breaking (pre-release, hence minor):** `IDiscoveringMeshRelayOptions.advertiser` becomes
1227
+ `advertisers` (a list).
1228
+
1229
+ - 5e924ee: Two of one user's devices can find each other on the local network before the relay.
1230
+
1231
+ - `agent-remote-pairing` — `derivePairRendezvous` derives every place a device pair meets from their
1232
+ pairwise secret, separated by direction: rotating tags (hourly epochs, looked up one epoch either
1233
+ way), the seed of each epoch's one-time signing key, sealed connection-hint records, and the relay
1234
+ inbox topics. It takes the lists in force and refuses a device they do not name with the same
1235
+ key-agreement key, or revoke, so a rotated or revoked key stops deriving.
1236
+ - `agent-transport-webrtc` — `startLanMeshRelay` / `DiscoveringMeshRelay` look for a peer in the
1237
+ address cache, then with mDNS (`MeshMdns`, over `multicast-dns`), then on the self-hosted relay, and
1238
+ carry signals to the peer's direct endpoint (`startMeshLanListener`) on rotating pairwise topics,
1239
+ sent only as hashes. An endpoint must prove it holds the pair's topic before it carries signals,
1240
+ and is set aside when no admission follows. The mDNS announcement names no product, device or
1241
+ host, pads its instance count with names that hold for the epoch, and answers queries at a bounded
1242
+ rate. Discovery yields candidates only: admission is still the device handshake, and an address is
1243
+ remembered only after an admission it carried.
1244
+ - `agent-cli` — the device mesh endpoint can look on the local network (`lan` option), remembering
1245
+ the addresses that worked in an owner-only `$PRODUCT_USER_STATE_DIR/devices/address-cache.json`; no command starts
1246
+ it yet.
1247
+
1248
+ ## 3.0.0-beta.81
1249
+
1250
+ ### Minor Changes
1251
+
1252
+ - 3038eb7: A message from another session is instant messaging: text from an untrusted third party that
1253
+ carries no authority. What the model does with it is decided by the session's ordinary permissions —
1254
+ rules, permission mode and remembered consent — exactly like the session's own work. The per-origin
1255
+ peer policy is gone.
1256
+
1257
+ **BREAKING**
1258
+
1259
+ - `agent-core`: `IPermissionEvaluationContext.peerTurn` is now a boolean. It decides only whether a
1260
+ `repliesToPeer` tool exists; every other call in a peer turn is decided as in any turn. Removed:
1261
+ `isToolAvailableInPeerTurn`, `isSecretPath`, `IPeerTurnAuthority`, `TPeerReach` (now exported by
1262
+ `agent-interface-session-mobility`) and `IToolPermissionProfile.workspacePaths`.
1263
+ - `agent-tools`: `Read` and `Glob` no longer declare `workspacePaths`.
1264
+ - `agent-session`: `ISessionRunOptions.peerReach` is replaced by `peerTurn?: boolean`, and
1265
+ `ISessionOptions.allowPeerChanges` is removed. An ask in a peer turn is answered like any other:
1266
+ a consent the operator remembered answers it, and an "always allow" given there is remembered.
1267
+ - `agent-interface-session`: `IPeerTurnContext` no longer has `reach`; it carries only the reply
1268
+ route.
1269
+ - `agent-interface-session-mobility`: `peerReachOf` is removed; `TPeerReach` moves here. A delegated
1270
+ turn carries no reach.
1271
+ - `agent-framework`: the `peers.allowChanges` setting is removed (an existing value is ignored). A
1272
+ peer turn is offered the ordinary tools, plus `peer_reply`.
1273
+
1274
+ **Changes**
1275
+
1276
+ - `agent-framework`: the per-turn statement tells the model the message is an opinion from an
1277
+ untrusted third party, not its owner's instruction, and that it decides for itself whether and how
1278
+ to act. A message still expands no `@path` and attaches no context reference, and its requests
1279
+ still carry no provider-hosted tool, since no permission step can decide one. The session takes
1280
+ at most 6 messages a minute and 30 an hour from each sender for a turn; a message over the limit
1281
+ is refused with a reason the sender receives. A prompt answer given in the name of a `peer:` or `external:`
1282
+ driver is ignored. External-event turns keep their tool-less baseline.
1283
+ - `agent-cli`: incoming peer turns carry only their reply route.
1284
+
1285
+ - 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 `$PRODUCT_USER_STATE_DIR/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.
1286
+
1287
+ The remote-control host identity key moves into the store. The old `$PRODUCT_USER_STATE_DIR/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.
1288
+
1289
+ - 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.
1290
+
1291
+ 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 `$PRODUCT_USER_STATE_DIR/devices`.
1292
+
1293
+ `agent-remote-pairing` adds `isRecoveryPhraseWord`, so a phrase can be checked one word at a time.
1294
+
1295
+ - 007fd90: Authority per connection: pairing stays with the local operator, and driving needs the operator's yes.
1296
+
1297
+ - `agent-interface-session-mobility` — `ConnectionAuthority` decides what one connection may do.
1298
+ `presence` and `message` are allowed; `delegate` and `handoff` ask the receiving operator for every
1299
+ request; `observe` and `drive` ask once per connection. With no `IOperatorApprover` the answer is
1300
+ no. `authorizeDelegation` turns an approved task into a peer turn from where admission placed the
1301
+ peer, ignoring anything else on the request, so the receiver's policy decides what it may do.
1302
+ - `agent-transport-webrtc` — `connectionApproval` asks the operator before a connection reaches the
1303
+ session, after every proof has run. Frames the peer sends meanwhile are held (bounded) and delivered
1304
+ only after a yes. A channel that closes first withdraws the question (the approval context carries
1305
+ an `AbortSignal`), and a later answer admits nothing. A first-pairing device is pinned for
1306
+ reconnect only once it is admitted.
1307
+ - `agent-command` — `/remote-control enable` and `revoke` from a connected surface are refused, and
1308
+ `status` never shows a connected surface the pairing link.
1309
+ - `agent-framework` — the `remote-control-enable` host action runs only for the operator's own
1310
+ command, whichever command asked for it.
1311
+ - `agent-cli` — every remote-control connection, a returning trusted device included, is put to the
1312
+ operator on the host terminal; without an interactive terminal it is refused.
1313
+
1314
+ - 18b52cc: Built-in commands are offered to the model deliberately, each described for the model, and never
1315
+ with a trust, credential or permission-widening action.
1316
+
1317
+ - `agent-interface-command` — `ICommand` gains `modelDescription?` (what the model is told, beside the
1318
+ short `/help` line), and `modelInvocable` on a subcommand entry now narrows what the model may run.
1319
+ - `agent-framework` — `ISystemCommand` gains `modelDescription?` and `modelRequiresPermission?`.
1320
+ Once any subcommand of a model-invocable command declares `modelInvocable`, the model may run only
1321
+ the bare command and the subcommands declared `true`; everything else, including an alias or an
1322
+ undeclared subcommand, is refused before the command runs. The model-facing descriptor lists only
1323
+ that subset. A model-requested monitor's command is now decided by the shell tool's gate (its
1324
+ Bash/Shell rules, the mode and the prompt) and a refusal rejects with the new
1325
+ `MonitorCommandRefusedError`. The `scriptedSession` test harness accepts `permissions` patterns.
1326
+ - `agent-session` — `Session.checkToolPermission(toolName, toolArgs)` decides an action that has a
1327
+ tool's effect by another route: that tool's PreToolUse hooks, then the gate's rules, mode,
1328
+ remembered consent and prompt — never the command sandbox's auto-approval, since the action does
1329
+ not run inside the sandbox.
1330
+ - `agent-framework` also: `ICommandMCPActivationAdapter.userActionSurface?` tells `/mcp` whether the
1331
+ user can type a session command, so the model's status names the terminal sign-in otherwise.
1332
+ - `agent-command` — `/context` (bare and `list`), `/cost` (the report, not `budget`) and `/mcp`
1333
+ (`status` only, without a prompt) are now model-invocable; `/memory approve` and `/memory reject`
1334
+ are now user-only; the model's `/monitor` is decided by the shell gate rather than by consent to
1335
+ the command's name. Every model-invocable command carries a model-facing description. The model's
1336
+ `/mcp status` shows only safe names, states and the command to suggest, and a caller that does not
1337
+ identify itself gets that view. `/context list` accounts only for turns still in context. New
1338
+ `mcpUserActionNotice`, `mcpUserActionCommand` and `mcpUnavailableServersNotice` build the fixed
1339
+ notices.
1340
+ - `agent-command-workflows` — `/workflows` carries a model-facing description.
1341
+ - `agent-mcp` — `createDiscoveredTool` accepts `authFailureNotice`: a call the server refuses for
1342
+ authentication returns that host text instead of the generic failure.
1343
+ - `agent-cli` — an MCP server that did not start because the user must approve it, trust the
1344
+ workspace or sign in is named to the model at the start of an interactive session with the
1345
+ command to suggest, and a
1346
+ signed-in OAuth server that refuses a call tells the model to suggest `/mcp login <server>` — or, in print and serve runs, the terminal
1347
+ `robota mcp login <server>`.
1348
+
1349
+ A command whose bare form is a complete action declares `runsBare`, so choosing `/cost` or `/mcp`
1350
+ from the autocomplete menu still runs it even though they now declare subcommands.
1351
+
1352
+ - 9843fe6: Sign in to a remote MCP server from inside a session, and use its tools without restarting.
1353
+
1354
+ - **`/mcp login <server> [--no-browser]`** runs the same per-server OAuth sign-in as
1355
+ `robota mcp login` (discovery checks, PKCE, `state`, RFC 9207 `iss`, RFC 8707 resource, the
1356
+ loopback listener, the lock-guarded store). It opens the browser through the argv opener; with
1357
+ `--no-browser`, or when no browser can be opened, it shows the authorization URL and asks for the
1358
+ redirect URL in the session's own prompt (masked), held to the same rules as a pasted redirect in
1359
+ the terminal. A failed, refused, timed-out or cancelled sign-in changes nothing and is reported by
1360
+ a fixed reason only. `/mcp login <server> --client-secret` is refused: a secret is never typed into
1361
+ a session, and `robota mcp login <server> --client-secret` is named instead (also after a failed
1362
+ token exchange for a pre-registered client). `/mcp` stays user-only (`modelInvocable: false`).
1363
+ - **Connected in the same session:** after a sign-in, a server that could not connect for want of
1364
+ one goes through the normal admission (approval, fingerprint, trust) and connects, and its tools
1365
+ are offered from the next message; a server that was already connected is admitted again and
1366
+ reconnects, and its authenticator drops what it held and reads the new credential. A tool left out
1367
+ because the session already has its name is reported, and only tools actually added get
1368
+ provenance. In browser mode the authorization URL is shown in the session prompt before the
1369
+ browser opens, where the user can also choose to paste the redirect instead or cancel.
1370
+ - **`runMCPOAuthLogin`** takes `readRedirectWhenBrowserFails`: when `openBrowser` rejects, the
1371
+ loopback listener stops and the pasted redirect is read instead, for the same redirect URI. The
1372
+ loopback listener's time limit now runs from its first `wait()` — once the authorization page is
1373
+ handed over — so time spent at a prompt before the browser opens does not count against it.
1374
+ - **`Session.addTools`** (and `ICommandSessionTools.addTools`) offers tools that became usable
1375
+ mid-session, through the same permission gate and — via `ISessionOptions.wrapAddedTools`, which
1376
+ `createSession` sets — the same edit-checkpoint and reversible-execution wraps as the assembled
1377
+ tools. A name the session already has is left out, never replaced. Calls are serialized, and tools
1378
+ added while a turn runs are applied when the next turn starts, so a turn's rounds all see one
1379
+ tool list and the prompt cache misses once, at that boundary.
1380
+ - **Breaking (`agent-framework`, major):** `addTools` is a new required member of the
1381
+ `ICommandSessionTools` role port (and so of `ICommandSessionRuntime`). A host that implements the
1382
+ port itself must add it; one that passes an `agent-session` `Session` already has it.
1383
+ - **`ICommandMCPActivationAdapter.oauthLogin`** (`ICommandMCPOAuthLoginRequest`,
1384
+ `ICommandMCPOAuthLoginResult`, `ICommandMCPOAuthRedirectPrompt`) is the port behind it.
1385
+ - The sign-in notice and `/mcp status` now suggest `/mcp login <server>` in a session and
1386
+ `robota mcp login <server>` in a terminal; the server's name is shown only when it is safe to paste
1387
+ into any shell, otherwise `<server>`.
1388
+
1389
+ ### Patch Changes
1390
+
1391
+ - 02b7452: A reply to a peer session is decided by the permission system like any call that sends something
1392
+ off this machine, and it goes only to the sender that was admitted.
1393
+
1394
+ - `agent-core` — a tool declaring `repliesToPeer` is still refused outside a peer turn; inside one it
1395
+ is decided by the ordinary steps: deny, ask and allow rules, then the mode. It declares no risk
1396
+ class, so it asks by default, is refused in plan mode and proceeds under bypass. `IPeerTurnAuthority`
1397
+ no longer has `toolUsed`.
1398
+ - `agent-session` — an "always allow" answer to the reply is remembered and answers later replies,
1399
+ as for any tool; other asks in a peer turn still need a fresh approval each time.
1400
+ - `agent-framework` — `peer_reply` asks the operator by default, showing the full text and the peer
1401
+ it goes to; `permissions.allow`/`deny` rules naming `peer_reply` apply. `PeerMessageIngress`
1402
+ refuses a message whose origin names a sender other than the admitted one, or whose admission names
1403
+ no sender, and submits the turn with the admitted identity.
1404
+ - `agent-cli` — a local peer message is taken as coming from the session it names only when that
1405
+ session confirms, at its own socket, that it is sending exactly that message to this receiver.
1406
+ The reply target, the driver id and the operator's notices come from the confirmed sender. A
1407
+ session on an earlier version cannot confirm, so its messages are refused; both sessions need this
1408
+ version to message each other.
1409
+
1410
+ ## 3.0.0-beta.80
1411
+
1412
+ ### Major Changes
1413
+
1414
+ - 64ba748: **BREAKING — ARCH-042: project filesystem access is now an explicit, host-issued authority instead of an ambient consequence of `cwd`.**
1415
+
1416
+ `@robota-sdk/agent-framework` adds `WorkspaceTrustService` and the opaque
1417
+ `IWorkspaceProjectAuthority`, plus bounded reader, settings-writer, state-storage, and mutation
1418
+ facets. Public session, settings, context, checkpoint, memory, contribution, query, and replay
1419
+ contracts consume those facets. A caller that does not supply `projectAccess` is deliberately
1420
+ restricted to user-owned host state and receives no project filesystem capability.
1421
+
1422
+ The framework removes or renames ambient Node/project exports. Migrate `checkSettingsFile` to
1423
+ `checkNodeHostSettingsFile`, `readMergedProviderSettingsFromPaths` to
1424
+ `readMergedProviderSettingsFromSources`, `resolveProviderSettingsWriteTargetPath` to
1425
+ `resolveProviderSettingsWriteTarget`, `FileSystemMemoryStore` / `createFileSystemMemoryStore` to
1426
+ `WorkspaceMemoryStore` / `createWorkspaceMemoryStore`, and `PluginSettingsStore` to
1427
+ `NodeHostPluginSettingsStore`. Host-only git helpers now carry the `FromNodeHost` suffix.
1428
+ `projectPaths`, `resolveSettingsPathForScope`, and `getProviderSettingsPaths` are removed; project
1429
+ consumers must use the authority facets rather than recover absolute paths.
1430
+
1431
+ `@robota-sdk/agent-session` renames the Node filesystem implementation `SessionStore` to
1432
+ `NodeSessionStore` and adds explicit session-log/external-payload source and sink ports. Session
1433
+ replay no longer resolves external payload files from an ambient directory.
1434
+
1435
+ `@robota-sdk/agent-interface-transport` changes `ISkillExecutionPort.loadCommands(cwd, home?)` to
1436
+ the authority-bound `loadCommands()` and removes the optional absolute-path leak
1437
+ `IInteractiveSessionStore.getFilePath`. `@robota-sdk/agent-command` consequently replaces the
1438
+ `cwd` option of `createSkillsCommandModule` with required `contributionSources`; default command
1439
+ composition accepts explicit contribution sources and discovers no project skills when none are
1440
+ provided.
1441
+
1442
+ `@robota-sdk/agent-cli`, `@robota-sdk/agent-transport`, and
1443
+ `@robota-sdk/agent-transport-tui` thread the trusted-or-restricted project decision through every
1444
+ session surface. Embedded callers that need project settings, state, context, skills, checkpoints,
1445
+ or mutation must mint access through `WorkspaceTrustService` and pass the returned
1446
+ `projectAccess` (and a separately approved mutation/settings facet where required). Omitting it is
1447
+ still type-compatible but is behaviorally breaking: the surface now fails closed instead of
1448
+ reading or writing the current directory.
1449
+
1450
+ ### Minor Changes
1451
+
1452
+ - 9c19c50: `/fork [name] [--same-dir]` copies the live conversation into a background session.
1453
+
1454
+ The session writes a copy of its own record under a fresh id — messages, system prompt, tool schemas
1455
+ and full history, and deliberately not the sandbox snapshot, goal, plan or branch — then spawns a
1456
+ background job carrying only `resumeSessionId`. The child restores that record itself, so no
1457
+ conversation crosses the child-process boundary and the worker start payload's key set is unchanged.
1458
+ The background panel gains an `attach` control that switches the terminal onto the forked session; it
1459
+ is a view switch, never a merge, and a missing record or a terminal task is refused with the task's
1460
+ own status.
1461
+
1462
+ **`@robota-sdk/agent-framework` is `major` for one reason: `ICommandHostSessionAccess` gains a
1463
+ required member.** `forkSession({ name? })` is not optional, so an external implementation of that
1464
+ role port stops compiling until it adds the method. Every other change in this set is additive:
1465
+
1466
+ - `agent-interface-execution` — `TExecutionControl` gains `'attach'`.
1467
+ - `agent-interface-command` — `TCommandUiIntent` gains `{ type: 'switch-session'; sessionId }`.
1468
+ - `agent-subagent-runner` — `ISubagentWorkerComposition` gains the optional `openSessionStore`, and
1469
+ the worker resumes a record when the job names one. A composition that registers no store and
1470
+ receives no `resumeSessionId` behaves exactly as before.
1471
+ - `agent-framework` also gains the fork-record builder, `SessionTurnMemory`, and
1472
+ `IAgentBackgroundTaskRequest.resumeSessionId?`; `loadSessionRecord` now returns
1473
+ `restoredSystemPrompt`, which makes a record field that was written and never read take effect on
1474
+ restore.
1475
+ - `agent-executor`, `agent-command`, `agent-transport-tui`, `agent-cli` — the spawn input, the
1476
+ `/fork` module and the attach control that carry it to the surface.
1477
+
1478
+ - 3ac6c48: Remote MCP servers get a client authentication port. OAuth and a dynamic header helper will plug
1479
+ into it without the transport changing.
1480
+
1481
+ - **`IMCPClientAuthenticator`:**
1482
+ - A host registers one for one server identity: `authenticatorFor` on the CLI's MCP client
1483
+ composition, or `authentication` on `IMCPHttpEndpoint`.
1484
+ - The Streamable HTTP transport asks it for headers on every request to that server, after
1485
+ admission. Its headers override a static header of the same name.
1486
+ - **Failures:**
1487
+ - A 401/403 is retried at most once, with fresh authorization, when the authenticator answers
1488
+ `retry`.
1489
+ - Otherwise the connection is refused with `MCPAuthenticationError`, which is classified as
1490
+ `auth`. Neither the credential nor the authenticator's text appears in it.
1491
+ - There is never an unauthenticated attempt.
1492
+ - **`oauth` and `headersHelper` in a definition** were silently ignored.
1493
+ - On a remote server they are now decoded as `unsupportedAuthentication`: the server stays
1494
+ listed, and admission refuses it by name (`unsupported-authentication`) instead of connecting
1495
+ without the credential.
1496
+ - On a stdio server they are a definition problem.
1497
+
1498
+ - 9368d00: Advisor escalation: the main model can consult a second model at the decision points it chooses.
1499
+
1500
+ With an advisor configured (`--advisor <profile>[:<model>]`, or the `advisorModel` setting that
1501
+ `/advisor` saves; the flag wins), the session gets an `Advisor({ question? })` tool. The advisor reads
1502
+ the whole conversation — system prompt, messages, tool calls and results — serialized into one prompt
1503
+ and sent with `toolChoice: 'none'`, truncated from the front to fit its window with the system prompt
1504
+ kept, and declines when even that does not fit. Its answer comes back framed as guidance to verify;
1505
+ an empty or refusing answer reads as declined. Calls are limited to two per turn and a fixed number
1506
+ per session, parallel calls in one round share those limits, and a repeated question in the same
1507
+ turn returns the earlier answer. A request that was sent counts even when the provider failed (the
1508
+ decline is reported by class, never by its text); only a call declined before sending gives its slot
1509
+ back.
1510
+
1511
+ Advisor usage, including in-process subagents', is recorded where each turn's usage is recorded —
1512
+ the persisted session history, under the advisor's own provider and model — so `/cost`, usage reports
1513
+ and resumed sessions include it. `/cost` now totals that history and prices each part on its own
1514
+ model, showing "mixed" when more than one model was priced.
1515
+
1516
+ `/advisor <model>` and `/advisor off` change only where calls go, never the tool list, so the main
1517
+ model's prompt cache is not invalidated mid-session; the tool is added only when a session starts
1518
+ with an advisor. Sending history to a destination (provider type and endpoint host) the main model
1519
+ does not already use needs a one-time consent per destination, kept in the user settings file; a
1520
+ refusal is remembered for the session. The organization's `allowedProviders` applies, and
1521
+ `PRODUCT_DISABLE_ADVISOR=1` turns it off completely. In-process subagents inherit the advisor, bound
1522
+ to their own conversation; child-process subagents do not get it.
1523
+
1524
+ **`@robota-sdk/agent-framework` is `major` for one reason: `ICommandHostSessionAccess` gains a
1525
+ required member,** `getSessionUsage()`, the session's persisted usage records. An external
1526
+ implementation of that role port stops compiling until it adds the method. The rest is additive.
1527
+
1528
+ - `agent-session` — `formatConversationEntries`, the one text rendering of a conversation, now used
1529
+ by compaction too. It keeps tool calls and results, marks a user message a peer session sent
1530
+ (`user [from "peer:<id>"]`), and JSON-encodes every message onto one line so no content can forge
1531
+ another entry; compaction previously flattened all of this. `Session.getProvider()` returns the
1532
+ provider the session currently uses.
1533
+ - `agent-framework` — `AdvisorController`, `createAdvisorTool`, the advisor spec helpers, provider
1534
+ destinations (`describeProviderDestination`, `rememberProviderDestination`), `getSessionUsage` and
1535
+ `ISessionUsageRecord`, the `onUsageRecorded` session option, and the optional `advisor` command
1536
+ host adapter. Session assembly binds a host-supplied Advisor tool to the session holding it.
1537
+ - `agent-command` — the `/advisor` command module; `/cost` reads the session's persisted usage.
1538
+ - `agent-cli` — the `--advisor` flag, `advisorModel` setting, per-destination consent store and kill
1539
+ switch.
1540
+ - `agent-ui-terminal` — a usage line from another source (the advisor, a background task) names that
1541
+ source and leaves out the context window it does not have.
1542
+ - `agent-session-analytics` — personal usage counts an advisor call's tokens and cost toward its
1543
+ turn without counting it as a turn.
1544
+
1545
+ - 0d369ce: Remote MCP servers can take their request headers from a header helper.
1546
+
1547
+ - **Declaring one:** `"headersHelper": { "command": "/absolute/path", "args": [...] }` on a remote
1548
+ server definition.
1549
+ - `${}` templates are refused, as they are for stdio.
1550
+ - The shell-string form other clients accept is refused, with a message showing the argv form.
1551
+ - `headersHelper` is no longer reported as unsupported authentication; only `oauth` is.
1552
+ - **Allowing one:** a helper runs only when all of these hold:
1553
+ - its exact command and arguments are listed in `mcpHeaderHelpers` in the user's own settings.
1554
+ A project, local, managed or plugin source cannot add to that list.
1555
+ - the server's activation is approved. The helper is part of the definition fingerprint, and the
1556
+ activation endpoint shows it beside the URL, with arguments quoted and control characters
1557
+ escaped.
1558
+ - for a `project` or `local` definition, the workspace is trusted.
1559
+
1560
+ Otherwise the server is refused with `headers-helper-not-allowed` or `headers-helper-untrusted`.
1561
+ It is never connected with its static headers alone.
1562
+
1563
+ - **Running it** (`agent-cli`):
1564
+ - No shell. It runs in the project directory for a `project`/`local` definition, and in
1565
+ `$PRODUCT_USER_STATE_DIR` otherwise.
1566
+ - Its environment is the host's without runtime-loading variables (`NODE_OPTIONS`, `LD_*`,
1567
+ `BASH_ENV`, …). A `project`/`local` helper also gets no credential-shaped variables.
1568
+ `PRODUCT_MCP_SERVER_NAME` and `PRODUCT_MCP_SERVER_URL` are set. For a `project`/`local` helper
1569
+ the URL keeps its `${VAR}` references instead of what the environment expanded them to.
1570
+ - Limits: 10 s and 64 KiB of stdout. Stderr is discarded. The process tree is killed on timeout
1571
+ or cancel.
1572
+ - **Its output** (`agent-mcp`, `parseHeadersHelperOutput`):
1573
+ - Exactly one JSON object of string values.
1574
+ - Refused: invalid or duplicate names (case-insensitive), more than 32 headers, and headers the
1575
+ transport owns (`Host`, `Content-Length`, `Transfer-Encoding`, `Connection`, `Content-Type`,
1576
+ `Accept`, `Mcp-Session-Id`, `Mcp-Protocol-Version`, `traceparent`).
1577
+ - **When it runs:** once per connection, one run at a time, and once more after a 401/403.
1578
+ - Requests refused together cost one new run: the reusable `MCPSingleFlightCache` tags each value
1579
+ with a generation and drops it only when the refused request used the current one.
1580
+ - `IMCPAuthorizationRejection` now carries `authorization`, the object `authorize` returned for
1581
+ the refused request, so an authenticator can tell which credential was refused.
1582
+ - A reconnect runs the helper afresh. A helper still running when the CLI exits is killed.
1583
+ - Failures are `MCPHeadersHelperError` with a fixed reason. Nothing the helper printed reaches an
1584
+ error or a diagnostic.
1585
+ - `IMCPHttpEndpoint.authenticationRequired` refuses admission (`authentication-unavailable`) when no
1586
+ authenticator is registered.
1587
+
1588
+ - d4189b9: The rest of the per-server MCP OAuth lifecycle: signing in to a server without a local browser,
1589
+ signing out of it with token revocation, and each server's sign-in state in `/mcp`.
1590
+
1591
+ - **`robota mcp login <name> --no-browser`** prints the authorization URL and reads the redirect URL
1592
+ the user pastes back (not echoed). `runMCPOAuthLogin` takes `readRedirect` for this; nothing
1593
+ listens on the redirect URI then. The pasted URL is held to the loopback listener's rules through
1594
+ `createPastedRedirectAcceptor`: it must be the registered redirect URI (same origin and path, no
1595
+ user info), carry the sign-in's `state` (compared in constant time), and is accepted once; an
1596
+ error redirect is `authorization-denied` without its `error_description`, and the RFC 9207 `iss`
1597
+ check applies as before. `openBrowser` now also receives the redirect URI. `readRedirect` gets a
1598
+ signal that aborts on cancel or at `callbackTimeoutMs` (5 minutes by default, as for the loopback
1599
+ listener); a paste longer than the prompt accepts fails as `redirect-too-long`.
1600
+ - **Signing out** (`runMCPOAuthLogout`; `robota mcp logout <name>`; `/mcp logout <serverId>`):
1601
+ deletes the stored credential under the refresh lock, then revokes the refresh token and the
1602
+ access token (RFC 7009) when the stored issuer advertises `revocation_endpoint`. Revocation is a
1603
+ POST through the egress policy that never follows a redirect and is byte-bounded; it authenticates
1604
+ the client by `revocation_endpoint_auth_methods_supported` when advertised, otherwise the way its
1605
+ refresh does. The local delete always happens. Each token's outcome is reported in `tokens`, and
1606
+ overall as `revoked`, `partial`, `unsupported`, `failed` or `not-attempted`, with fixed reasons
1607
+ only (new: `revocation-failed`, and `token-type-not-revocable` for RFC 7009
1608
+ `unsupported_token_type`). `/mcp logout` also makes the session's authenticator drop the token it
1609
+ holds (`IMCPOAuthAuthenticator.forget`).
1610
+ - **Sign-in state:** `readMCPOAuthCredentialState` answers `signed-in`, `expired-refreshable`,
1611
+ `sign-in-required` or `signed-out` — never anything token-derived. `/mcp` shows it for every
1612
+ OAuth server; a server this session was told needs a sign-in reads `sign-in-required`.
1613
+ `ICommandMCPActivationAdapter` gains optional `oauthStatus` and `oauthLogout`
1614
+ (`ICommandMCPOAuthStatus`, `ICommandMCPOAuthLogoutResult`).
1615
+ - **No in-session sign-in:** signing in stays `robota mcp login <server>` in a terminal, since it
1616
+ needs the terminal (browser, pasted redirect, hidden secret prompt) a running session owns. `/mcp`
1617
+ names that command for a server that needs a sign-in, as does the session's sign-in notice; the
1618
+ server name is shown there only when it is safe to paste into any shell — a plain token not
1619
+ starting with `-` or `=` — and is otherwise replaced by `<server>` (`shellArgumentForDisplay` in
1620
+ `agent-core`; nothing is quoted, since quoting rules differ between shells). Server names in OAuth
1621
+ notices have control and format characters escaped.
1622
+ - **Refresh hardening:** on `invalid_grant`, the store is read again under the lock; a refresh token
1623
+ another holder rotated in meanwhile is kept (and used when still valid) instead of being deleted.
1624
+ The expiry skew is capped at half the token's lifetime, so a short-lived token is not refreshed on
1625
+ every request; credentials now record `issuedAt` for this.
1626
+
1627
+ - 9edae52: Remote MCP servers can authenticate with OAuth: `robota mcp login <name>` signs in, and sessions
1628
+ send the stored token.
1629
+
1630
+ - **Declaring it:** `"oauth": { "clientId"?, "callbackPort"?, "authServerMetadataUrl"?, "scopes"? }`
1631
+ on a remote server definition, decoded into `IMCPOAuthConfig`.
1632
+ - `authServerMetadataUrl` must be `https`. `scopes` wins over the server's metadata and a 401's
1633
+ `scope`.
1634
+ - A `clientId` needs a `callbackPort`: a pre-registered client's redirect URI is fixed.
1635
+ - `clientSecret`, unknown keys and `${}` templates are refused; `oauth` beside `headersHelper`,
1636
+ or on a stdio server, is refused.
1637
+ - `oauth` is no longer reported as unsupported authentication, and it is part of the definition
1638
+ fingerprint.
1639
+ - **Signing in** (`runMCPOAuthLogin`; `robota mcp login <name> [--client-secret]`):
1640
+ - Discovery is done here, not by the SDK's `discoverOAuthServerInfo`: the protected-resource
1641
+ metadata `resource` must be the canonical server URL, the authorization server's `issuer` must
1642
+ be the server it was fetched for, the server, authorization, token and registration
1643
+ endpoints must be `https`, and the authorization server must advertise PKCE `S256`
1644
+ (`pkce-unsupported` otherwise). A 401's `resource_metadata` is never followed to another origin,
1645
+ and a server without resource metadata is not guessed at.
1646
+ `authServerMetadataUrl` skips resource discovery.
1647
+ - Dynamic client registration when there is no `clientId`; PKCE, a random `state` and the
1648
+ RFC 8707 resource indicator on the authorization request; the RFC 9207 `iss` checked on the
1649
+ redirect when sent, and required when the server promises it.
1650
+ - The loopback callback listens on `127.0.0.1`, checks `Host`, answers only `GET /callback` with
1651
+ the sign-in's `state` (compared in constant time), once, within 5 minutes, and serves a static
1652
+ page that never shows `error_description`.
1653
+ - The browser opens by argv (`open`, `xdg-open`, `rundll32 url.dll,FileProtocolHandler`) and only
1654
+ for an `https` URL. `--client-secret` asks for the secret without echo (or reads stdin's first
1655
+ line) and only for a pre-registered client.
1656
+ - **Network:** every OAuth request goes through the egress policy and is capped at 256 KiB.
1657
+ `agent-core` adds `postWithEgressPolicy`, which refuses a redirect (`redirect_refused`) instead
1658
+ of following it; registration, token and refresh requests use it.
1659
+ - **Storage:** `IMCPOAuthCredentialStore` (`get`/`set`/`delete`), keyed by the server's security
1660
+ identity and canonical URL together. `createFileOAuthCredentialStore` keeps 0600 files in a 0700
1661
+ `$PRODUCT_USER_STATE_DIR/mcp-credentials/`. The issuer, token endpoint and client (with its secret, if any, and
1662
+ the client authentication method dynamic registration returned) are stored with the tokens. A
1663
+ sign-in stores its credential under the refresh lock, so a refresh in flight cannot overwrite it.
1664
+ - **Sessions** (`createOAuthAuthenticator`, wired for every `oauth` definition):
1665
+ - Sends the stored token as `Authorization: Bearer`, refreshing it first when expired — including
1666
+ a token cached earlier in the session — and only at the stored token endpoint.
1667
+ - One refresh at a time per credential: in a process through `MCPSingleFlightCache`, across
1668
+ processes under `createFileOAuthRefreshLock`, re-reading the store once the lock is held. A
1669
+ stale lock is taken over, and a lock released, only after it is renamed aside and proven to be
1670
+ the one judged — never another process's fresh lock.
1671
+ - A 401 rediscovers, then refreshes once and retries; an authorization server that changed clears
1672
+ the tokens — compared, under the lock, with what is stored then, so a newer sign-in is kept. `invalid_grant`, or nothing stored, asks the user to run `robota mcp login <name>`. A 403
1673
+ `insufficient_scope` fails and names the scope.
1674
+ - An `oauth` definition is never connected without the authenticator (`oauth-unavailable`).
1675
+ - Failures are `MCPOAuthError` with a fixed reason; no code, verifier, token, secret or
1676
+ authorization-server text reaches an error, a notice or a log.
1677
+
1678
+ - 4078a72: Model fallback chain. `--fallback-model a,b` (or the `fallbackModel` settings array; the flag wins) names up to three models a turn moves to when its model is overloaded, unavailable or failing on the server. An entry is a provider profile, `profile:model`, a bare model on the primary's provider, or `default`. The move happens only before any output has streamed, lasts for the current turn, and is shown as a system note; entries that cannot be built are passed over and entries outside the organization's `allowedProviders` are dropped with a notice. `FallbackProvider` in agent-framework implements it over the session's provider; `IChatOptions` gains `executionId`, `onModelFallback` and `preserveContextWindow`, `IAIProvider` gains optional `resolveModelRoute`, and the execution loop emits a `provider_fallback` event and attributes requests, call observations, committed replies, the response cache and usage to the model that answered. A turn that ran on more than one model records per-model `modelShares` on its usage observation, which personal usage reports split by model and provider. A `/provider` switch keeps the chain, read again for the new primary.
1679
+ - 3131209: `robota mcp serve` can serve a remote MCP client as an OAuth resource server.
1680
+
1681
+ `agent-transport-mcp` gains `createMcpRemoteHttpHost`, which admits requests only by an OAuth access
1682
+ token checked by an injected `IAccessTokenVerifier`. Its endpoint path and its RFC 9728
1683
+ protected-resource metadata path are derived from the `https` public URL, so a proxy prefix works,
1684
+ and Host and Origin are checked against that public origin. A refusal has an empty body: `401` with
1685
+ `WWW-Authenticate: Bearer resource_metadata="…"` (plus `error="invalid_token"` when a token was
1686
+ presented), or `403` with `error="insufficient_scope"`. The token is verified before anything is
1687
+ counted, and only failures are counted, per client address, so a valid token is never throttled;
1688
+ `X-Forwarded-For` is believed only from a configured trusted proxy. Each refusal goes to an injected
1689
+ audit sink as a reason and an address class, never token text. The host stays stateless and issues
1690
+ no `Mcp-Session-Id`. The loopback `createMcpHttpHost` is unchanged.
1691
+
1692
+ `agent-cli` adds `--http-public-url`, `--http-host`, `--oauth-issuer`, `--oauth-scopes`,
1693
+ `--oauth-allowed-subjects` and `--trusted-proxy` to `robota mcp serve`. It binds an address other than
1694
+ `127.0.0.1` only with the public URL and the OAuth settings, and never with `--http-token-file`.
1695
+ Refusals are logged on stderr without token text.
1696
+
1697
+ - af2f2ad: `/cd <directory>` continues the conversation in another directory.
1698
+
1699
+ - **A move is a new session in the target directory.** In the TUI, Robota saves a copy of the
1700
+ conversation where the target's session store will find it, ends the current run through its
1701
+ normal end-of-life flow, and starts again in the target directory resuming that copy. The target's
1702
+ settings, trust decision, tools, skills and `AGENTS.md` apply, exactly as if Robota had been
1703
+ launched there. The process boundary makes the move atomic, so no tool call can straddle it.
1704
+ - **The system prompt is kept as recorded**, so a provider's prompt cache survives. One appended
1705
+ `<workspace-move>` message tells the model the new directory, and which project instructions now
1706
+ apply.
1707
+ - **Refused** while a turn is running or a background task is still running, for a missing directory
1708
+ or the current one, and for a target a `Cd(...)` deny rule names.
1709
+ - **Access never widens:** a restricted session stays restricted after a move, and a trusted session
1710
+ takes the target's own trust decision.
1711
+ - New contracts: the `workspace-move` host action, `ICommandWorkspaceAdapter` /
1712
+ `IWorkspaceMoveRequest`, `InteractiveSession.moveWorkspace`, and the `workspaceMovedFrom` session
1713
+ option. The CLI's `--moved-from` and `--restricted-workspace` flags are internal.
1714
+
1715
+ - 267af5f: The execution containment seam states where tools run.
1716
+
1717
+ - **`ISandboxClient.filesystem`** (`'shared' | 'separate'`, absent means `separate`).
1718
+ - With a **separate** filesystem (E2B, in-memory), every file tool goes through the sandbox, and
1719
+ `Glob`/`Grep`, which can only read the host, are withheld. Previously a remote sandbox still
1720
+ offered host-reading search tools beside sandbox-writing edit tools.
1721
+ - With a **shared** filesystem (OS-level confinement over the host's files), file tools stay on
1722
+ the host under the path guard, and only commands go through the sandbox.
1723
+ - **`describeExecutionContainment` / `routesFilesThroughSandbox`** name the containment (`host`,
1724
+ `sandbox-shared`, `sandbox-separate`) instead of inferring it from an absent value.
1725
+ - **`robota doctor` reports `execution.containment`.** Robota composes no sandbox today, so the
1726
+ doctor says shell commands run unconfined on the host and the permission rules are the only
1727
+ boundary. The CLI composition and the doctor read the same value.
1728
+ - **The `Agent` and `BackgroundProcess` tools no longer fall back to `process.cwd()`** when they
1729
+ were built without an execution root. They report an assembly error instead, as the file tools
1730
+ already did (ARCH-010).
1731
+
1732
+ - f336838: One permission evaluation order for every caller. The interactive session, background tasks and
1733
+ subagents used to run two different resolvers, so the same call could be decided differently
1734
+ depending on who made it. They now share `evaluatePermission`, and a background policy only adds a
1735
+ ceiling, an ask-everything flag and the task's own lists to it:
1736
+
1737
+ deny → caller ceiling → unevaluable deny (ask) → never-auto-approve set (ask) → ask-everything →
1738
+ bypassPermissions → allow → mode.
1739
+
1740
+ - **`ask` rules.** `permissions.ask` patterns always ask, in every mode including
1741
+ `bypassPermissions`. They are matched per command like a deny rule, and are validated at
1742
+ construction alongside `allow` and `deny`.
1743
+ - **Never auto-approved, bypass included:** removing a critical path with `rm`/`rmdir` (the root, a
1744
+ top-level directory, home, the working directory or a parent), and a modify-class write into
1745
+ `.git`, `.Robota`, `.claude`, `.agents`, `.mcp.json`, `.gitconfig`, `.npmrc` or a shell rc file.
1746
+ Files inside an isolated worktree (`$PRODUCT_PROJECT_STATE_DIR/worktrees/<name>/…`) are ordinary files. With no
1747
+ approver attached, an ask is a denial.
1748
+ - **A ceiling is checked before bypass and before any ask.** A subagent's `inherit-allowlist` ceiling
1749
+ is now the parent's _effective_ rules, read live at spawn: settings, preset lists and command
1750
+ auto-allows. It used to be the raw settings file. An unevaluable deny under a policy now asks,
1751
+ like everywhere else, where it used to deny outright; with no approver it is still a denial.
1752
+ - **Settings layers union `permissions.allow`**, as they already did `deny`. A checked-in project
1753
+ file no longer silently discards the user's allow list.
1754
+ - **Print mode, `createQuery()` and headless sessions default to `default` mode**, not
1755
+ `bypassPermissions`. They have no approver, so a call that would ask is denied. Pass
1756
+ `--permission-mode` / `permissionMode: 'bypassPermissions'` explicitly for unattended runs.
1757
+
1758
+ **Breaking:**
1759
+ - `@robota-sdk/agent-core` removes `resolvePermissionByPolicy` and `TPermissionPolicyDecision` in
1760
+ favour of `projectPermissionPolicy` plus `evaluatePermission`'s new `context` argument.
1761
+ - `@robota-sdk/agent-framework` changes the settings merge rule for `permissions.allow`, and the
1762
+ default permission mode of `createQuery()` and headless sessions.
1763
+
1764
+ - 34e50f0: A new permission mode, `auto`, lets a model classifier approve or block what would otherwise prompt.
1765
+
1766
+ - **What it decides:**
1767
+ - Reads and in-workspace edits run as in `acceptEdits`.
1768
+ - Commands and other calls the mode leaves open go to the classifier, a side call to the
1769
+ session's own model. It sees the call, the working directory and the git remotes, never the
1770
+ conversation.
1771
+ - A block reaches the model with its reason, so it can take another route.
1772
+ - **What still reaches a person, or is refused:**
1773
+ - Deny rules and background ceilings apply first.
1774
+ - `ask` rules, critical removals and protected paths ask a person.
1775
+ - After 3 refusals in a row (blocks, or no usable verdict), or 20 blocks in the session, the
1776
+ mode asks a person until one approves.
1777
+ With no one to ask, the call is denied.
1778
+ - **Allow rules:** in `auto` mode, allow rules that approve any command are set aside while the
1779
+ mode is on. Examples are `Bash(*)`, an interpreter (`Bash(python *)`), a package runner
1780
+ (`Bash(npm run *)`, `Bash(pnpm exec *)`), `Agent`, `ExecuteCommand` or `Computer`. Narrow
1781
+ rules still apply.
1782
+ - **Retry:** `/permissions` lists classifier blocks. `/permissions retry <n>` lets that exact call
1783
+ run once, unjudged, when the model tries it again.
1784
+ - **Turning it on and off:**
1785
+ - `--permission-mode auto`, `/mode auto` or `/permissions auto`.
1786
+ - An organization turns it off with `disableAutoMode` in the org policy.
1787
+ - **New contracts:**
1788
+ - `TPermissionMode` gains `'auto'`.
1789
+ - `allowRulesForAutoMode` and `isBroadExecutionAllowRule`.
1790
+ - `IPermissionClassifier` and `AutoModeGate`.
1791
+ - The `permissionClassifier` session option.
1792
+ - `Session.retryPermissionDenial`, and `retryDenial` on the permission-mode adapter.
1793
+ - The `'classifier'` denial reason.
1794
+ - `createModelPermissionClassifier`.
1795
+ - The `disableAutoMode` option on `createSession` and `IOrgPolicy`.
1796
+
1797
+ - 722e88a: Shell commands can run in an OS-level sandbox: bubblewrap on Linux and WSL2, Seatbelt on macOS.
1798
+
1799
+ - **Confinement:** covers the command and every process it starts.
1800
+ - Writes are limited to the working directory, the temporary directories and
1801
+ `sandbox.filesystem.allowWrite`.
1802
+ - Agent, git-hook, MCP and shell configuration inside the workspace stays read-only.
1803
+ - `sandbox.filesystem.denyRead` hides paths from the command.
1804
+ - The network is on or off (`sandbox.network.enabled`).
1805
+ - **Modes:** `/sandbox` switches between `auto-allow`, `regular` and `off` for the next command and
1806
+ saves the choice.
1807
+ - In `auto-allow` (`sandbox.autoAllowBashIfSandboxed`), a confined command runs without a prompt
1808
+ in `default` and `acceptEdits`.
1809
+ - Deny rules, ask rules, critical removals and plan mode still apply first.
1810
+ - **Exclusions:** `sandbox.excludedCommands` run unconfined, through the ordinary permission path.
1811
+ - **When the sandbox cannot run:** a missing or unusable backend is reported at startup, in
1812
+ `robota doctor` and in `/sandbox`, and commands then run unconfined.
1813
+ `sandbox.failIfUnavailable` refuses to start instead.
1814
+ - **New contracts:**
1815
+ - `OsSandboxClient`, `detectOsSandbox`, `bubblewrapArguments`, `seatbeltProfile`.
1816
+ - `ISandboxClient.wrapCommand` and `autoApproves`.
1817
+ - `IPermissionEvaluationContext.sandboxAutoApproved`.
1818
+ - The `commandSandbox` session option.
1819
+ - The `sandbox` settings key and the `sandbox` command host adapter.
1820
+
1821
+ - a5cc36e: `/permissions` shows the rules the session enforces and the calls it refused.
1822
+
1823
+ - **Rules by source:** each allow, deny and ask rule the gate reads is listed under the settings file
1824
+ that declares it. Rules added by a CLI flag, a preset or a command are listed under "this session".
1825
+ A rule a settings file declares but the session does not enforce is not shown.
1826
+ - **Recent denials:** the latest refused calls, most recent first, with the reason: a rule or the
1827
+ mode, the user declining, or no one available to approve.
1828
+ - New contracts: `Session.getRecentPermissionDenials()`, `PermissionEnforcer.getRecentDenials()`,
1829
+ `IPermissionDenial`, the `permissionRules` host adapter (`createSettingsPermissionRulesAdapter`),
1830
+ and `getPermissionRules` / `listRecentDenials` on `ICommandPermissionModeAdapter`. Settings
1831
+ provenance now covers `permissions.ask`.
1832
+
1833
+ - b70fa3d: `robota --safe-mode` starts a session with every customization off, to rule one out in one run.
1834
+
1835
+ - **Off:** project and user instruction files, skills, custom commands, agent definitions, output
1836
+ styles, external presets, plugins, hooks from every settings layer, and MCP servers.
1837
+ - **Unchanged:** provider, model, built-in tools and permissions work as usual. Nothing on disk
1838
+ changes.
1839
+ - **Project access:** the project starts Restricted whatever its trust decision, so safe mode also
1840
+ runs in print and serve mode.
1841
+ - **Notice:** a line at startup says that safe mode is on.
1842
+ - **Embedders:** `startCli({ safeMode: true })` does the same as the flag.
1843
+ - **New framework option:** `skipConfiguredHooks` on interactive and headless sessions, also
1844
+ carried by the TUI and serve session options.
1845
+
1846
+ - 4dd45cc: `/peers` shows how each other local session's workspace relates to this one's: `same worktree`,
1847
+ `same repo`, `different repo`, or `workspace unknown`.
1848
+
1849
+ A session now publishes a workspace claim with its rendezvous entry — the root commits reachable
1850
+ from HEAD, a SHA-256 of its normalized `origin` URL (never the URL itself), and the real path of its
1851
+ worktree — and re-reads it as it runs, so a first commit or a checkout does not leave it stale. The
1852
+ reader does not take the claim as written: it reads git at the claimed path itself, and a claim whose
1853
+ contents disagree is shown as `workspace claim mismatched, not believed` and relates to nothing. Those
1854
+ git reads are local-only and asynchronous, run only for the view that shows the relation (and for
1855
+ the sender of an incoming peer message), and are cached per announcement. The relation also reaches
1856
+ the peer-turn origin, computed by the receiver; a relation the sender put on the wire is discarded,
1857
+ and the admission never carries it.
1858
+
1859
+ The relation is display and routing information only and is never an authorization input.
1860
+
1861
+ Additive: `agent-interface-session-mobility` exports `TWorkspaceRelation` and gains the optional
1862
+ `IPeerOrigin.workspaceRelation`; `agent-framework`'s `ILocalPeerSummary` gains the optional
1863
+ `workspaceRelation` and `workspaceClaim`, and `ICommandLocalPeersAdapter` the optional
1864
+ `listWithWorkspace()`.
1865
+
1866
+ - 4c5148e: ARCH-005 Stage S2 — `robota` is now expressed as an `IProductProfile` and assembled by `assembleProduct`;
1867
+ the hand-wired composition root in `agent-cli` is gone.
1868
+
1869
+ - **`@robota-sdk/agent-product`** — provider construction returns IN-KERNEL. `assembleProduct` builds the
1870
+ provider from `providerDefinitions` + the shell's already-resolved `providerSettings` via agent-core's
1871
+ pure `createProviderFromConfig`; `provider` is now an OPTIONAL injected override. Both are optional, so a
1872
+ Mode A profile can carry only `providerDefinitions`. The fold stays pure and IO-free — every
1873
+ settings/env/file read still happens in the shell. Adds `IAssembledProduct.buildRuntimeOptions`, the pure
1874
+ overlay `buildRuntime` delegates through, and `IAssembledProduct.providerDefinitions`.
1875
+ **Breaking for pre-release consumers:** `IProductProfile.providerDefinitions` is now required and
1876
+ `IProductProfile.provider` / `IAssembledProduct.provider` are now optional.
1877
+ - **`@robota-sdk/agent-framework`** — a scoped, additive session seam: `agentDefinitions` on
1878
+ `TInteractiveSessionOptions` / `ICreateSessionOptions`, composed into the built-in agent tier ahead of
1879
+ `BUILT_IN_AGENTS`, so capability-pack subagents actually reach the runtime. Precedence: discovered
1880
+ project/user definitions > injected > built-in. `AgentDefinitionLoader` now dedupes within that tier
1881
+ (first wins). Absent ⇒ unchanged behavior.
1882
+ - **`@robota-sdk/agent-transport` / `@robota-sdk/agent-transport-tui`** — forward the optional
1883
+ `agentDefinitions` through the headless and TUI channels so every Robota surface carries the seam.
1884
+ - **`@robota-sdk/agent-cli`** — `robota`'s identity (branding, provider surface, presets,
1885
+ `packs: [codingPack]`, base command modules, injected transports/runners/subagent factory) is declared as
1886
+ data in a product profile and folded by `assembleProduct`. The coding command modules (`/shell`,
1887
+ `/editor`) now come from `pack-coding` rather than the base set, so the pack is load-bearing. What remains
1888
+ in the CLI is product-shell only: arg parsing, settings/file IO, terminal notices, first-run/init/
1889
+ `--configure`, memory + session-resume UX, and print/serve/TUI mode dispatch.
1890
+
1891
+ End-user `robota` behavior is unchanged in substance — the assembled command-module set, provider surface,
1892
+ tool set, subagent roster, and preset resolution all match the pre-change assembly — with one accepted
1893
+ cosmetic delta: `/shell` and `/editor` now appear at the END of `/help` output and of the slash-command
1894
+ autocomplete popup rather than mid-list, because they arrive from `pack-coding` and both surfaces render in
1895
+ module-insertion order. Same commands, same behavior, different position.
1896
+
1897
+ - 37af5dc: ARCH-006 + ARCH-007 — the capability-pack TOOL axis reaches parity with the command and subagent axes,
1898
+ and `robota` consumes the composition kernel's RUNTIME SEAM instead of only its materials.
1899
+
1900
+ - **`@robota-sdk/agent-framework` (ARCH-006)** — the default tool set is no longer hard-coded.
1901
+ `createSession` accepts `defaultTools`, which REPLACES the `createDefaultTools()` tier (`[]` suppresses
1902
+ it entirely) — the tool-axis mirror of NEUT-003's `builtInAgents` seam for subagents. The assembled
1903
+ list `defaultTools ⊕ additionalTools ⊕ goalTool` is now **deduplicated by tool name, first occurrence
1904
+ wins**, the same rule `AgentDefinitionLoader` applies within the subagent built-in tier. So a
1905
+ contributed tool with a NEW name is additive, a contributed tool that mirrors a framework default is
1906
+ deduped rather than listed twice, and a product can hand its whole tool surface to its capability
1907
+ packs. A name collision keeps the framework default and drops the contribution: the default tier is
1908
+ built WITH the session context (`cwd` supplies `agent-tools`' working-directory path guard, plus the
1909
+ sandbox client and retrieval adapter) and an already-constructed contribution carries none of it, so
1910
+ replacement is expressible only through the explicit `defaultTools` seam — never as a side effect of a
1911
+ collision. The edit-checkpoint wrap now covers the assembled set, so a pack-contributed `Write`/`Edit`
1912
+ is checkpointed too. Option threaded through `ICreateSessionOptions` / `IInitOptions` /
1913
+ `IInteractiveSessionStandardOptions`. Absent `defaultTools` and absent a duplicate name, every existing
1914
+ path is byte-identical.
1915
+ - **`@robota-sdk/agent-product` (ARCH-007)** — `buildRuntimeOptions` no longer overwrites a
1916
+ caller-supplied `commandModules`. A shell that has already narrowed the merged `base ⊕ packs` superset
1917
+ (as `robota` does with its preset's enabled/disabled delta) keeps that selection; the assembled set is
1918
+ overlaid only when the caller left it unset — the same rule `permissionMode` already followed.
1919
+ - **`@robota-sdk/agent-cli` (ARCH-007)** — `startCli` now routes through
1920
+ `product.buildRuntimeOptions(...)`. The shell resolves its own session inputs and the kernel lays the
1921
+ product-owned materials on top: the packs' tools (`additionalTools`), the packs' subagents
1922
+ (`agentDefinitions`), and the default preset's permission posture when `--permission-mode` left it
1923
+ unset. The hand-threaded `product.subagents` path and the three per-surface
1924
+ `args.permissionMode ?? resolvedPreset.permissionMode` expressions are gone — every surface binds to
1925
+ the one kernel result.
1926
+
1927
+ End-user `robota` behavior is unchanged: the assembled command-module set, provider surface, tool set,
1928
+ subagent roster, preset resolution, and permission posture all match the pre-change assembly.
1929
+
1930
+ - 0116a29: ARCH-006 completion — Robota's capability packs now OWN its tool surface, and `pack-coding` is built by a
1931
+ context-bound factory.
1932
+
1933
+ - **`@robota-sdk/pack-coding` (BREAKING)** — the module-level `codingPack` constant is **removed** and
1934
+ replaced by `createCodingPack({ cwd, sandboxClient })`, with `cwd` **required**. This is a safety change,
1935
+ not a style one: `agent-tools` disarms its working-directory path guard when `cwd` is `undefined`, so a
1936
+ pack whose file tools are built with no options contributes an **unsandboxed** `Read`/`Write`/`Edit`.
1937
+ That was inert while `agent-framework` always supplied its own context-bound default tier, but ARCH-006
1938
+ lets a product hand the whole tool surface to its packs (`defaultTools: []`) — and a context-free pack in
1939
+ that position is a real hole. Keeping a zero-option export beside that seam would be a loaded gun, so it
1940
+ is gone rather than deprecated. Each call returns fresh instances bound to the supplied context, so two
1941
+ products in one process get independently-scoped file tools. Migration: replace `codingPack` with
1942
+ `createCodingPack({ cwd: process.cwd() })`.
1943
+ - **`@robota-sdk/agent-cli`** — `robota`'s packs are built from the shell's resolved `cwd`
1944
+ (`createProductCapabilityPacks({ cwd })`) before command setup, and the runtime seam passes
1945
+ `PRODUCT_PACKS_OWN_TOOL_SURFACE` (an empty `defaultTools`) so the framework's `createDefaultTools()` tier
1946
+ is REPLACED. Every tool Robota runs now arrives from a capability pack: dropping a pack drops its tools,
1947
+ exactly as it already dropped its command modules and subagents.
1948
+ - **`@robota-sdk/agent-transport` / `@robota-sdk/agent-transport-tui`** — forward the optional
1949
+ `additionalTools` and `defaultTools` through the headless and TUI channels, mirroring the existing
1950
+ `agentDefinitions` pass-through, so print, serve and TUI carry an identical tool surface.
1951
+
1952
+ End-user `robota` behavior is unchanged, including the security property: the real binary still answers a
1953
+ read outside the working directory with `Access denied: "…" is outside the working directory`.
1954
+
1955
+ - 7937c19: Split the `@robota-sdk/agent-provider` monolith into SDK-aligned leaf packages (ARCH-PROVIDER-002 Stage A). The single package that hard-bundled all three vendor SDKs (`@anthropic-ai/sdk`, `openai`, `@google/genai`) is **removed** and replaced by per-vendor leaves, each depending only on `@robota-sdk/agent-core` + its one SDK: `@robota-sdk/agent-provider-anthropic`, `@robota-sdk/agent-provider-openai`, `@robota-sdk/agent-provider-openai-compatible` (DeepSeek/Qwen/Gemma over the shared OpenAI-compatible base), `@robota-sdk/agent-provider-gemini` (+ a `./google` entry), and `@robota-sdk/agent-provider-bytedance` (media/video `IVideoGenerationProvider`). The aggregated `createDefaultProviderDefinitions()` now lives in the new `@robota-sdk/agent-builtin-providers` leaf.
1956
+
1957
+ Migration: replace `@robota-sdk/agent-provider/<vendor>` imports with the corresponding `@robota-sdk/agent-provider-<vendor>` package (`/deepseek`, `/qwen`, `/gemma` → `@robota-sdk/agent-provider-openai-compatible`; `/google` → `@robota-sdk/agent-provider-gemini/google`), and import `createDefaultProviderDefinitions` from `@robota-sdk/agent-builtin-providers`. Consumers now pull only the vendor SDK(s) they actually use.
1958
+
1959
+ - 1e3f91a: CMD-004 Phase 2 Stage E (breaking, beta line): the legacy `TCommandEffect` union and
1960
+ `ICommandResult.effects` are DELETED. Commands emit the split contract directly —
1961
+ `hostActions` (session-executed via `ICommandHostAdapters`; works headless and remote) and
1962
+ `uiIntents` (requester-routed `ui_intent` session events with UI-neutral `show-*` names).
1963
+ Final carriers for the former notification effects: `session_renamed` and the new
1964
+ `history_cleared` are BROADCAST session events (forwarded to every WS surface, folded by the
1965
+ TUI transcript/title and the GUI reducer's new `sessionName`/transcript-reset state);
1966
+ `session-execution-started` rides `result.data.sessionExecution`; the plugin-registry refresh
1967
+ rides `result.data.pluginRegistryReloaded`. A mechanical grep floor
1968
+ (`command-effect-grep-floor.test.ts`) keeps `tui-requested`/`TCommandEffect`/`effects:` out of
1969
+ every `packages/*/src` production tree.
1970
+ - 44393be: Add the `/remote-control` enable path (REMOTE-008 Stage B4-2b) — turn on P2P remote control locally, get
1971
+ a QR + link, and a paired device co-drives the SAME live session over pairing-gated WebRTC. The command
1972
+ is a declarative trigger returning `remote-control-enable-requested`/`-stop-requested` effects (SSOT
1973
+ agent-interface-transport) and reads state via a new `ICommandHostAdapters.remoteControl.getStatus()`;
1974
+ the TUI dispatches the effects to injected callbacks; all transport construction lives at the agent-cli
1975
+ composition root (`WsSignalingClient` + pairing-gated `WebRtcTransport`, relay URL from
1976
+ `transports.webrtc.options.relayUrl`, QR/link rendered into history). Fail-closed: no relay configured ⇒
1977
+ does nothing; pairing mismatch/timeout ⇒ the session is never exposed. Consumes REMOTE-007 so a paired
1978
+ remote owner answers their own permission/ask prompts over the WebRTC channel.
1979
+ - 9f602df: Add user-supplied TURN fallback for remote control (REMOTE-010 / Stage E1) so P2P works behind symmetric
1980
+ NAT / restrictive firewalls. The host reads + validates `transports.webrtc.options.iceServers`/`forceTurn`
1981
+ at the agent-cli composition root (a fail-closed validator narrowing the untyped value → `IIceServer[]`;
1982
+ `IWebRtcTransportOptions.iceServers` widened to carry TURN `username`/`credential`), and the browser reads a
1983
+ validated `ice`/`forceTurn` pairing-URL query param (fail-closed decoder for the attacker-influenceable value;
1984
+ `forceTurn` → `iceTransportPolicy: 'relay'`) — both threaded into their `RTCPeerConnection`. `forceTurn` without
1985
+ a TURN server fails closed (else ICE gathers no candidates and silently never connects). Absent ICE config ⇒
1986
+ host-candidate-only, unchanged.
1987
+
1988
+ (Bump target corrected during REL-023 triage: `@robota-sdk/agent-web-ui` was dissolved by GUI-006 (#1141, 2026-07-12) before this work was ever published; the browser-side ICE/`forceTurn` handling described here now lives in `@robota-sdk/agent-transport-webrtc-web`.)
1989
+
1990
+ - 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 }`.
1991
+
1992
+ ### Patch Changes
1993
+
1994
+ - 1698be4: A child-process subagent can no longer send the parent's provider credential to a different
1995
+ endpoint.
1996
+
1997
+ - **Before spawning a child**, the parent compares every environment variable that decides where
1998
+ the provider connects or which credential it sends. If the child's environment differs, the job
1999
+ is refused before the credential leaves the parent. The error names the variable, never its
2000
+ value. The variables compared are:
2001
+ - the proxy and TLS variables;
2002
+ - the variables the provider's SDK reads, such as `OPENAI_BASE_URL`, `ANTHROPIC_AUTH_TOKEN` and
2003
+ the Vertex settings;
2004
+ - the credential's own variable.
2005
+ - **The child** repeats the check before it builds its provider. It builds that provider from the
2006
+ parent's effective connection exactly: it no longer fills in a base URL, options or a default
2007
+ credential from its own registry, and a credential reference that resolves to nothing is refused.
2008
+ - **Where the effective connection comes from:** the parent applies its own definition defaults
2009
+ (base URL, options). `profileName` is sent only when it names the connection actually sent.
2010
+ - **New contracts:**
2011
+ - `IProviderDefinition.destinationEnvironment`, declared by every built-in provider.
2012
+ - `createProviderFromExactProfile`, `connectionEnvironmentNames`,
2013
+ `findConnectionEnvironmentDivergence`, `sealConnectionEnvironment`,
2014
+ `verifyConnectionEnvironment` and `TRANSPORT_ENVIRONMENT`.
2015
+ - The start payload's `connectionCheck`.
2016
+ - The child-process runner's `providerDefinitions` option, now required: a provider with no
2017
+ definition there is refused, because its connection cannot be checked.
2018
+
2019
+ - 2711ec6: One broken skill or agent file no longer stops the session.
2020
+
2021
+ Skill, command and agent definitions are shared with other hosts through `.claude`, and those hosts
2022
+ define fields of their own. The frontmatter decoder now validates only the fields Robota owns and
2023
+ ignores the rest (nested `metadata` entries included); a malformed value of an owned field is still
2024
+ refused. A refused file is skipped with a single warning and still claims its name, so a
2025
+ lower-priority definition cannot stand in for it. An empty `argument-hint` reads as no hint. When
2026
+ initialization does fail, the session reports the cause to readiness probes instead of "not
2027
+ initialized", so the terminal shows the reason at once rather than a 15-second timeout.
2028
+
2029
+ The terminal also stops printing type names for message-less telemetry records, keeps its own notices
2030
+ in place across a history sync so the following answer is not skipped, and CLI diagnostics print
2031
+ their context instead of `[object Object]`, through the console so Ink renders them above the frame.
2032
+
2033
+ - d157f4b: ARCH-008 — `robota` resolves presets through the composition kernel's per-call registry, so there is one
2034
+ preset resolution path instead of two.
2035
+
2036
+ - **`@robota-sdk/agent-product`** — `IProductProfile` gains two optional fields. `presetRegistry` lets a
2037
+ consumer hand in an `IPresetRegistry` it already built (`createPresetRegistry`); when present the
2038
+ assembler ADOPTS that instance instead of building a second, equivalent one, so `product.presets` is
2039
+ that very object. This is the seam for a shell that must resolve a preset BEFORE it can build its
2040
+ profile — a preset can carry the `model` and `agentName` the profile is itself constructed from, so
2041
+ "resolve, then assemble" is a real ordering constraint. `presetContext` carries the override layers
2042
+ (`cliOverrides` / `explicit`) used when resolving `defaultPresetId`, so `product.defaultPreset` is the
2043
+ caller's full resolution rather than a variant missing its overrides. R8 is unaffected: the registry is
2044
+ still instance-scoped and no module-level state is read or mutated. Both fields are optional and the
2045
+ existing `presets` + `defaultPresetId` shape behaves exactly as before.
2046
+ - **`@robota-sdk/agent-cli`** — `resolveCliPreset` is replaced by `resolveShellPreset(externalPresets,
2047
+ args, settingsPreset)`, which builds the per-call registry, resolves over it, and returns
2048
+ `{ registry, presetId, context, options }` as one value. `createSelectedProductProfile` takes that whole value,
2049
+ so the shell cannot hand the kernel a registry, id, or override context other than the ones it actually
2050
+ resolved with. `robota`'s startup path no longer reads `agent-preset`'s module-global resolver; that
2051
+ registry remains only as the in-session `/preset` DISCOVERY surface, which is executed inside the
2052
+ session and has no handle on the assembled product. Both surfaces are fed by the one
2053
+ `loadExternalPresets()` call, so they cannot disagree.
2054
+
2055
+ End-user `robota` behavior is unchanged: the same preset resolves to the same options, external presets
2056
+ in `$PRODUCT_USER_STATE_DIR/presets/*.json` remain visible to both `--preset <id>` and `/preset`, and the assembled
2057
+ command-module set, provider surface, tool set, subagent roster, and permission posture are untouched.
2058
+
2059
+ - e82215f: **ARCH-011: replace the ambiguous transport lifecycle stub with executable conformance.**
2060
+
2061
+ `ITransportAdapter` now requires a frozen `service | runner` lifecycle descriptor. `start()` resolves
2062
+ at the concrete transport's documented readiness boundary; start before attach and repeated active
2063
+ start reject a stable lifecycle error, repeated stop is safe, and stopped adapters can reattach and
2064
+ restart.
2065
+
2066
+ Runner adapters launch separately and expose a typed terminal outcome through
2067
+ `waitForCompletion()`. The registry accepts base adapters, rejects duplicate names, keeps
2068
+ configuration as an optional capability, returns complete ordered records whose pending slots become
2069
+ registry-owned `abandoned` outcomes on stop/rollback, and exposes a real-runner-only first-failure
2070
+ wait. It serializes startup/stop, rejects active restart before mutation, and reverses partial startup
2071
+ from the currently failing adapter with typed safe rollback details. Runtime host and serve mode
2072
+ propagate real nonzero runner results without treating normal shutdown abandonment as failure.
2073
+
2074
+ HTTP, MCP, both WebSocket adapters, WebRTC, and headless invoke one shared public conformance kit.
2075
+ The former `TuiTransport` export is removed because it ignored the attached session; use `renderApp`
2076
+ or `TuiInteractionChannel`, which honestly own their session lifecycle.
2077
+
2078
+ - 0c07176: **BREAKING — ARCH-021: child-process subagents compose the PRODUCT's surface, not imported defaults.**
2079
+
2080
+ The child-process worker built its surface from `createDefaultProviderDefinitions()` and `createDefaultTools()` — a fixed six-vendor registry and the framework's default tool tier — while the composition root had already handed the runner the fully composed surface and the runner dropped it. So a product's custom providers and pack-contributed tools reached an **in-process** subagent and not a **child-process** one, and ARCH-006's landed invariant "every tool Robota runs comes from a pack" was **false in the child**: dropping a pack did not drop its tools.
2081
+
2082
+ This is the second finding at that line. ARCH-010 — judged BLOCKER, a subagent `Read` returning `/etc/hostname` — patched one argument there and left the reconstruction standing.
2083
+
2084
+ ```ts
2085
+ // the port: what the product composes, stated by the composition root
2086
+ export interface ISubagentWorkerComposition {
2087
+ createTools(context: { readonly cwd: string }): IToolWithEventService[];
2088
+ readonly providerDefinitions: readonly IProviderDefinition[];
2089
+ }
2090
+
2091
+ export function runSubagentWorkerMain(composition: ISubagentWorkerComposition): void;
2092
+ ```
2093
+
2094
+ **Why a recipe and not a broker.** A composition cannot be projected across a process boundary because it is _code_: `createProvider` is a function and a tool carries `execute`. The two sound answers are to proxy the instances or to stop expressing the contract as instances. Proxying loses on containment — a proxied tool executes in the **parent**, bound to the parent's checkout, while a worktree-isolated child's execution root is a different directory — and a prior-art sweep found **no specification that defines a per-call working root for a proxied tool invocation** (MCP roots are session-scoped and pull-based). So the recipe crosses and the child builds an equivalent surface at its own root, which is what every comparable product does.
2095
+
2096
+ **Per package, classified against each barrel:**
2097
+
2098
+ - **`agent-subagent-runner` (major)** — `runSubagentWorkerMain` gains a **required** parameter; `ISubagentWorkerComposition` is added to the barrel; `ISubagentWorkerReadyMessage` gains `composedToolNames?`; and the `@robota-sdk/agent-provider-defaults` **dependency edge is removed**.
2099
+ - **`agent-cli` (patch)** — composition-root wiring plus one new internal `product/` module; no barrel change.
2100
+
2101
+ **The structural guarantee reaches one axis, and the document says so.** Deleting the manifest edge makes the **provider** axis a compile error, because that package solely owns `createDefaultProviderDefinitions`. The **tool** axis cannot be cut the same way: `createDefaultTools` is barrel-exported by `agent-framework`, which this package must keep for `createSubagentSession`. That axis is held by a new `harness:scan` check instead — and it is the axis with the failure history, so claiming compile-time enforcement across both would have been an overclaim exactly where it matters. The cause (no defaults-aggregator leaf for the tool surface) is tracked as ARCH-035.
2102
+
2103
+ **Fail closed on what a recipe cannot reproduce.** A recipe carries anything that is a pure function of (execution root, serialized payload, ambient durable state) — not a live, unrepeatable handle. Today that is `sandboxClient`, and it is reachable with public code (`E2BSandboxClient` and `InMemorySandboxClient` are both on `agent-tools`' barrel). The composition root now **refuses** to select the child-process runner in that case, naming the capability, rather than yielding a sandboxed parent with a host-tool child. Projection is tracked as ARCH-033.
2104
+
2105
+ **Verified per run, not by construction.** The child declares its composed tool names in `ready`, so the built binary can be asked what it actually composed. Measured on the real artifact: `["Shell","Bash","Read","Write","Edit","Glob","Grep","WebFetch","WebSearch","AskUserQuestion"]` — `pack-coding`'s surface, from the product's own packs.
2106
+
2107
+ **Also filed rather than folded in:** ARCH-034 (in-process and child-process subagents get different tool surfaces), ARCH-036 (`deps.builtInAgents` is dropped by the child-process path), SEC-009 (`apiKey` rides in the IPC start payload; comparable products use the child's environment).
2108
+
2109
+ - 4772067: **BREAKING — ARCH-031: the subagent seam is derived from its transport SSOT instead of copied.**
2110
+
2111
+ One field family — what a subagent job IS — was declared three times as independent shapes and carried
2112
+ between them by six hand-written object literals that nothing checked for totality. A field added to
2113
+ either side had to be hand-copied at every hop, and a miss compiled clean as a silent no-op. TYPE-003
2114
+ named this cause and derived one hop; the next two changes each dropped a field at a hop it had skipped
2115
+ (CORE-025's permission policy, and ANALYTICS-001's `usage`, dropped in the very commit that added it).
2116
+
2117
+ ```ts
2118
+ // now, in @robota-sdk/agent-interface-transport
2119
+ export type ISubagentSpawnRequest = Omit<IAgentBackgroundTaskRequest, 'kind'>;
2120
+ export type ISubagentJobResult = Omit<IBackgroundTaskResult, 'kind' | 'exitCode' | 'signalCode'>;
2121
+ ```
2122
+
2123
+ All four projections collapse to spreads. `parentTaskId` and `providerProfile` now reach the runner
2124
+ because they exist on the source, not because someone remembered them.
2125
+
2126
+ **Per package, classified against each barrel:**
2127
+
2128
+ - **`agent-executor` (major)** — the barrel loses `ISubagentSpawnRequest` and `ISubagentJobResult` (they
2129
+ moved to their owner; re-publishing them here would be a pass-through re-export). `ISubagentJobStart`
2130
+ and `ISubagentJobHandle` rename `jobId` → `taskId`, `ISubagentJobStart` gains `worktree?`, and
2131
+ `ISubagentWorktreePrepareRequest` renames `jobId` → `taskId`.
2132
+ - **`agent-framework` (major)** — the barrel loses eleven type-only re-exports of `agent-executor`-owned
2133
+ types. They carried zero runtime values, so they bought none of the assembly convenience a runtime
2134
+ facade exists for, while making one field family look like it had three owners. Separately,
2135
+ `ISpawnAgentTaskRequest.permissionPolicy` goes optional → **required**.
2136
+ - **`agent-subagent-runner` (major)** — `ISubagentWorkerStartPayload` renames `jobId` → `taskId` and gains
2137
+ `worktree?`. This package is not in the item's declared `area:`; the audit that caught it is the reason
2138
+ it is here.
2139
+ - **`agent-interface-transport` (minor)** — two new barrel exports; nothing removed or renamed.
2140
+ - **`agent-core` (minor)** — **two** new barrel exports. `DEFAULT_BACKGROUND_PERMISSION_POLICY` is the
2141
+ intended one; collapsing the hand-listed permissions block to `export *` also surfaced
2142
+ `clearRegisteredToolArgumentKeys`, which the old list had omitted. It is documented as public rather
2143
+ than re-narrowed — a barrel that cannot fall out of step with its owner is the point of the collapse.
2144
+ Nothing was removed: all nine previously-listed permission types remain on the barrel.
2145
+ - **`agent-cli` (patch)** — migrated as the only in-repo implementer of `ISubagentWorktreeAdapter`; no
2146
+ barrel change.
2147
+
2148
+ **`permissionPolicy` is now required at the spawn boundary**, and its default is one exported constant
2149
+ owned by the permission SSOT. It was previously applied as `?? 'inherit-allowlist'` in the middle of a
2150
+ projection, in **two** packages independently, with nothing keeping them equal — a security-relevant value
2151
+ whose default was declared twice. Every spawn site now states its own policy.
2152
+
2153
+ **The worktree identity moved to the runner envelope.** It is runner-produced — the worktree does not
2154
+ exist when a caller builds a request — so it rides on `ISubagentJobStart.worktree` and crosses the IPC
2155
+ boundary there. The runner no longer also rewrites `request.cwd`, which had given ARCH-010's execution-root
2156
+ rule two carriers that could disagree. `branchName` **relocated rather than being deleted**: it has no
2157
+ reader in this repository today, and for a library that is not a reason to drop a legitimate contract.
2158
+
2159
+ **Renames are consistent across the SPI** (`type` → `agentType`, `jobId` → `taskId`) rather than applied to
2160
+ one shape, which would have left two names for one identifier in a single file. The IPC validator's
2161
+ string-literal keys are now typed against the contract, so the next rename is a compile error instead of a
2162
+ runtime rejection of every start payload.
2163
+
2164
+ - 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).
2165
+ - 6fab98f: **BREAKING — DIST-006: the built `robota` binary could not spawn a subagent at all.**
2166
+
2167
+ `/agent run` failed on every distributed build with `Subagent worker exited before result: exit code 1`. The child's real stderr was `Cannot find module '…/agent-cli/dist/node/child-process-subagent-worker.js'`. `getDefaultSubagentWorkerPath()` resolved the worker relative to its own `import.meta.url`; INFRA-028 bundles every workspace package into `agent-cli/dist/node/bin.js`, so at runtime that directory is **agent-cli's** dist, where the worker was never emitted. It worked from source, which is why nothing caught it.
2168
+
2169
+ **Second occurrence of one cause.** `agent-subagent-runner/tsdown.config.ts` already carried a comment naming this exact failure: _"Without this entry the file never existed, so the child-process subagent silently failed from any dist build."_ That fix put the worker next to its OWN package's bundle; bundling then moved the resolver's notion of "next to me" one package along.
2170
+
2171
+ **The defect was the function, not the missing file.** `getDefaultSubagentWorkerPath()` answered _"where is my worker file on disk?"_ from a library that cannot know — the answer is a property of the packaging step. Emitting the file where the resolver looks would have fixed **one of three shipped artifacts**: the npm bundle, but not the Bun single-file binaries published on every tag, nor the Electron desktop sidecar that embeds them. A compiled single-file executable has no sibling directory to emit into.
2172
+
2173
+ ```ts
2174
+ // removed — a question no library can answer
2175
+ export function getDefaultSubagentWorkerPath(): string;
2176
+
2177
+ // now: the composition root states how to start a copy of ITSELF
2178
+ export interface ISubagentWorkerEntry {
2179
+ readonly execPath: string;
2180
+ readonly args: readonly string[];
2181
+ readonly execArgv?: readonly string[];
2182
+ }
2183
+ ```
2184
+
2185
+ `IChildProcessSubagentRunnerOptions.workerPath` → **`workerEntry`**, and the runner `spawn`s `execPath args… --__agent-subagent-worker` instead of forking a module path. `robota`'s own entry enters worker mode through the new `runSubagentWorkerMain()`, so **there is no second artifact and no path to get wrong**. The one seam satisfies all three shapes: a bundled Node build names the file it is executing, a `tsx` source run names the same and adds `--import tsx`, and a compiled binary names _nothing_ — `process.execPath` is the binary, and re-executing it re-enters its embedded entry.
2186
+
2187
+ **Per package, classified against each barrel:**
2188
+
2189
+ - **`agent-subagent-runner` (major)** — the barrel loses `getDefaultSubagentWorkerPath` (deleted, not renamed) and gains `SUBAGENT_WORKER_MODE_FLAG`, `isSubagentWorkerModeArgv`, `runSubagentWorkerMain`, `ISubagentWorkerEntry`. `IChildProcessSubagentRunnerOptions` renames `workerPath` → `workerEntry` and drops `execArgv` — it now has exactly one owner, on the entry descriptor. The separate `child-process-subagent-worker` bundle entry is gone.
2190
+ - **`agent-cli` (patch)** — composition-root wiring only; no barrel change.
2191
+
2192
+ **Two things this also repairs, both found in review:**
2193
+
2194
+ - **The child's stderr was discarded** (`stdio: [..., 'ignore', 'ipc']`), so a worker that died before its first IPC message reported only an exit code. That is why occurrence #2 had to be diagnosed by hand. It is now captured, bounded, and appended to the error, making the next occurrence self-reporting.
2195
+ - **A source run executed the BUILT worker, not the source worker**, because package `exports` resolve to `dist`. `resolveExecArgv`'s `--import tsx` branch was therefore dead code. Self-fork names the entry actually running, so source runs finally run source.
2196
+
2197
+ **Verified against the artifacts, not from source:** the built `dist/node/bin.js` and a real `bun --compile` single-file binary each complete the worker IPC handshake (`{type:'ready'}`), each refuse a hand-typed flag with no IPC channel (exit 2, "Silence is not success" — measured on both), and the shipped bundle no longer contains the string `child-process-subagent-worker.js` — there is nothing left to look for.
2198
+
2199
+ - 1e40b5b: Move handoff source/destination orchestration and its contract from agent-framework to the session-mobility owner. Import `HandoffSource`, `HandoffDestination`, and their option types from `@robota-sdk/agent-interface-session-mobility`. Mobility now applies offer and authority decisions directly, while the CLI supplies wire effects and the session-record decoder. A successful offer no longer returns a mutable authority transaction.
2200
+ - f3fe3db: The terminal renderer no longer reads `PRODUCT_SCREEN_READER_STARTUP_QUIET_MS` or
2201
+ `PRODUCT_SCREEN_READER_PREPARK_MS` from the process environment. SDK hosts calling `renderApp`
2202
+ directly should pass raw timing choices and diagnostic labels through the new optional
2203
+ `screenReaderPacing` render option. Without it, the renderer uses its existing 900 ms startup
2204
+ quiet period and 50 ms pre-write park when screen-reader mode is enabled. The Robota CLI still
2205
+ reads the same environment variables and preserves their validation, bounds, and warnings.
2206
+ - 724d5b6: The terminal renderer no longer reads `PRODUCT_IME_CURSOR` or `PRODUCT_TURN_MARKS` from the process environment. SDK hosts calling `renderApp` directly should pass cursor and turn-mark choices through the optional `terminalCapabilities` render option. Without a host choice, the renderer keeps its terminal and TTY defaults. The Robota CLI still reads both environment variables and preserves its exact `1` and `0` overrides.
2207
+ - c6c56a6: Move the concrete `GitWorktreeIsolationAdapter` (git CLI + filesystem I/O) out of the reusable
2208
+ `@robota-sdk/agent-executor` runtime-primitives package into the `@robota-sdk/agent-cli` composition
2209
+ root, restoring the executor's "creates no Git worktrees" boundary (ARL-02 / ARCH-FIX-024, INFRA-031).
2210
+
2211
+ **Breaking (`@robota-sdk/agent-subagent-runner`):** `worktreeAdapter` is now a **required** option on
2212
+ `createChildProcessSubagentRunnerFactory` / `IChildProcessSubagentRunnerOptions`. The concrete git
2213
+ default (`createGitWorktreeIsolationAdapter()`) has been removed — inject the adapter at the composition
2214
+ root. `@robota-sdk/agent-executor` no longer exports `GitWorktreeIsolationAdapter`,
2215
+ `createGitWorktreeIsolationAdapter`, or `IGitWorktreeIsolationAdapterOptions` (the
2216
+ `ISubagentWorktreeAdapter` port and `WorktreeSubagentRunner` remain). CLI runtime behavior is unchanged.
2217
+
2218
+ - 4b76cfa: NEUT-005 (wave 2): restore an actionable context-capacity hint at the surface tier, neutrally. The zero-dependency `agent-core` layer emits a product-neutral hard-capacity notice and exposes the `IAgentConfig.contextCapacityHint` seam (wave 1). This wave wires that seam end-to-end without baking product vocabulary into a neutral library:
2219
+
2220
+ - `agent-session`: `ISessionOptions.contextCapacityHint` is forwarded into the agent configuration factory, making the core seam reachable from the consuming layer.
2221
+ - `agent-framework`: new `deriveContextCapacityHint(commandModules)` derives the concrete remediation wording from the surface's OWN registered command set (names a registered `compact` command → `"Run /compact and retry."`; `undefined` when none, leaving the neutral core default). It is applied automatically in interactive session assembly across the TUI, print, and `--serve` surfaces.
2222
+ - `agent-cli`: the default command set registers `/compact`, so end users regain the actionable hint.
2223
+ - `agent-interface-transport`: reworded the `'allow-project'` permission comment so it no longer hardcodes a storage path (the location is owned by the consuming layer), matching the `agent-session` twin.
2224
+
2225
+ - 796ddb4: Use a neutral default MCP client name and let hosts supply their own protocol identity. Robota CLI startup now explicitly supplies its prior `Robota-agent-mcp` name, preserving its initialize handshake; embedders relying on that implicit name can set `clientInfo` explicitly.
2226
+ - e13c30c: MCP servers now publish a neutral submission tool by default. Hosts that need the previous
2227
+ `PRODUCT_submit` tool must pass `submitTool: { name: 'PRODUCT_submit', description: '...' }` to
2228
+ `createAgentMcpServer`, `createMcpTransport`, or `createMcpHttpHost`. The Robota CLI supplies its
2229
+ existing name and description for both stdio and HTTP carriers.
2230
+ - 90e7a10: The default background observer warning code and exported `OBSERVER_FAILURE_WARNING_CODE` value
2231
+ change from `PRODUCT_BACKGROUND_OBSERVER_FAILURE` to `BACKGROUND_OBSERVER_FAILURE`. Hosts matching
2232
+ the old warning code should match the new neutral code or provide `observerFailureWarningCode` in
2233
+ their background manager or session options. The Robota CLI supplies its existing code explicitly
2234
+ across print, goal, serve, MCP, and TUI sessions.
2235
+ - 2db1b97: Remote-control pairing binds to the negotiated DTLS certificate.
2236
+
2237
+ - The Node host reads the remote fingerprint from the certificate the DTLS layer verified, not from the answer's
2238
+ SDP text, and builds the pairing gate once the DTLS handshake completes.
2239
+ - An SDP must advertise exactly one DTLS fingerprint. `extractDtlsFingerprint` now throws when two different
2240
+ fingerprints are present, and `extractDtlsFingerprintAttribute` returns the algorithm with the value.
2241
+ - A start takes one answer (host) and a connection takes one offer (browser client); a later description is
2242
+ ignored.
2243
+
2244
+ - fcb0da3: PAYLOAD-2153: make external-payload replay stable across Linux, macOS, and Windows.
2245
+
2246
+ - Add the domain-free `@robota-sdk/agent-file-authority` leaf with bounded, root-relative reads over retained native handles and a typed, path-safe refusal taxonomy.
2247
+ - Route session replay and framework project reads through the shared authority while preserving their existing domain-specific budgets, integrity checks, and error mappings.
2248
+ - Expose the canonical safe session-id predicate through the framework facade so CLI exact-session lookup stays within the SDK package boundary.
2249
+ - Package the pinned native bridge in clean-installed Node CLI archives and exact-host standalone Bun binaries, refusing unsupported or mismatched targets before artifact mutation.
2250
+
2251
+ - 07b627f: A local peer can now be answered, and what its turn may do depends on where it runs.
2252
+
2253
+ - `agent-core`: the permission evaluator takes a peer turn's authority as one more input
2254
+ (`IPermissionEvaluationContext.peerTurn`), decided after the deny list and ceiling and before
2255
+ bypass and allow rules. A peer on another host uses no tool. A peer on the same host may use an
2256
+ inspect-class tool that declares `workspacePaths`, only when every named location resolves inside
2257
+ the workspace and is not a credential (`isSecretPath`); write and execute tools are refused unless
2258
+ enabled, and then every use asks. A tool declaring `repliesToPeer` exists only in a peer turn and
2259
+ asks once the turn used another tool. New: `isToolAvailableInPeerTurn`, `TPeerReach`,
2260
+ `IPeerTurnAuthority`. `IRunOptions.withholdHostedTools` leaves a provider's hosted tools out of a
2261
+ run's requests (`nativeWebTools` with `false` withholds a hosted tool for one call).
2262
+ - `agent-tools`: `Read` and `Glob` declare the arguments that say where they look. `Grep` does not:
2263
+ it reads files it was never named, so a peer turn does not get it.
2264
+ - `agent-session`: `ISessionRunOptions.peerReach` makes a run a peer turn for the permission policy;
2265
+ every ask in it needs a fresh approval, and its requests carry no provider-hosted tool. `ISessionOptions.allowPeerChanges` enables write and execute
2266
+ tools for same-host peer turns.
2267
+ - `agent-interface-session`: `ISubmitOptions.peer` (`IPeerTurnContext`) carries a peer turn's reach,
2268
+ the message it answers and the session a reply goes to.
2269
+ - `agent-interface-session-mobility`: `IPeerMessage.inReplyTo` threads a conversation;
2270
+ `peerReachOf(admission)` maps admission to a reach.
2271
+ - `agent-framework`: a peer turn is offered what its origin allows, and a new `peer_reply` tool
2272
+ answers the peer that sent the message, threaded to it. The setting `peers.allowChanges` enables
2273
+ write and execute tools for same-host peer turns.
2274
+ - `agent-ui-terminal`: a permission prompt in a peer turn names the requesting peer.
2275
+ - `agent-cli`: incoming peer turns carry their reach and reply route; a conversation is limited in
2276
+ depth and in how often this session answers it, and a reply over a limit is not sent and the
2277
+ operator is told.
2278
+ - `agent-provider-anthropic`, `agent-provider-openai-compatible` (Qwen): a request whose
2279
+ `nativeWebTools` sets a hosted tool to `false` is sent without it.
2280
+
2281
+ - 5ccd1e9: Add the browser remote client (REMOTE-009 Stage D) — the P2P peer that opens the pairing URL and
2282
+ co-drives a live session over WebRTC. `agent-web-ui` gains a native-`WebSocket` signaling client, a
2283
+ fail-closed responder pairing gate (session exposed only after the DTLS-fingerprint-bound handshake
2284
+ accepts), an RTC data-channel session client with the same contract as the WS client, a
2285
+ fragment-injected `spa/remote.html` static entry, and the REMOTE-007 permission/ask render+answer
2286
+ (the paired owner answers its own prompts — local == remote) shared by both the WS and RTC clients. It
2287
+ reuses the isomorphic `@robota-sdk/agent-remote-pairing` leaf and takes no node/werift dependency.
2288
+ `agent-cli` removes the fabricated `Robota-remote://pair` client-URL default and fails closed when
2289
+ `transports.webrtc.options.clientUrl` is unset (no dead link).
2290
+
2291
+ (Bump targets corrected during REL-023 triage: `@robota-sdk/agent-web-ui` was dissolved by GUI-006 (#1141, 2026-07-12) before this work was ever published; the browser client described here now lives in `@robota-sdk/agent-transport-webrtc-web` + `@robota-sdk/agent-remote-client`.)
2292
+
2293
+ - c7fa299: REMOTE-003 + REMOTE-006 (merged — net behavior; the interim deny-by-default gate never appeared in a
2294
+ published version): commands now carry an invocation source. A `'remote'` value is added to the command
2295
+ invocation source (SSOT relocated to `@robota-sdk/agent-interface-transport`, re-exported by
2296
+ `agent-framework`) and an optional `source` is threaded into
2297
+ `IInteractiveSession.executeCommand(name, args, source?)` (defaults to `'user'`, so all local callers are
2298
+ unchanged). The shared `createWsHandler` tags transport-origin commands `'remote'`. Policy: local == remote
2299
+ (owner principle) — pairing is the sole trust boundary, so a transport-origin command runs exactly as a
2300
+ locally-typed one under the universal permission system (permission modes + PermissionEnforcer + the
2301
+ ask/approval handler); `createDefaultRemoteCommandPolicy()` allows by default. The `IRemoteCommandPolicy`
2302
+ seam remains as an OPTIONAL, user-configured restriction, and the genuinely-remote WebRTC path stays
2303
+ pairing-gated.
2304
+ - d19feda: Build a separate verified headless Bun artifact for the Electron desktop app. The full CLI binary and npm entry remain available; the desktop resource now copies the smaller presentation-free artifact while preserving the `--serve` wire and launch contract.
2305
+ - 833afe1: Remove the remaining polynomial-ReDoS backtracking (SEC-003, CodeQL `js/polynomial-redos`) and stop the DTLS fingerprint binding to SDP free text.
2306
+
2307
+ **`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.
2308
+
2309
+ **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.
2310
+
2311
+ **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.
2312
+
2313
+ **`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.
2314
+
2315
+ 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.
2316
+
2317
+ - fde558e: Forward organization policy through TUI, print, and goal sessions so blocked commands are enforced consistently. Preserve print/goal preset generation, language, prompt-seed, and structured-response options through the headless channel, and guard declared session-capability projections against silent field loss.
2318
+
2319
+ ## 3.0.0-beta.79
2320
+
2321
+ ### Patch Changes
2322
+
2323
+ - Coordinated beta.79 release. Ships the natural-language workflow authoring feature and its
2324
+ follow-ups, all bundled into the published CLI:
2325
+
2326
+ - **FLOW-007 `/workflows create "<description>"`** — author a workflow from natural language via the
2327
+ active provider, save it as a reusable `.workflows/<name>.json` artifact, and run it immediately;
2328
+ composes existing nodes and creates prompt-backed nodes on the fly; model-invocable so the agent can
2329
+ author + run from chat. Storage de-jargoned to a flat `.workflows/` layout with an injectable
2330
+ workspace.
2331
+ - Live-LLM hardening found by running against a real provider: the authoring call now threads the
2332
+ resolved model; the spec parser tolerates a Markdown code fence; authored prompt nodes inherit +
2333
+ persist the active provider; and the unit suite no longer makes real key-using calls (forces the
2334
+ no-key path + asserts detection), with real coverage isolated in an opt-in `test:live`.
2335
+ - **DATA-003** — the instant-node package now owns a runtime provider SSOT (`INSTANT_NODE_PROVIDERS`)
2336
+ and a symmetric persistence round-trip (`parsePersistedInstantNode` / `rehydrateInstantNode` +
2337
+ guards), removing duplicated provider lists and a hand-rolled, prompt-only reload path.
2338
+
2339
+ (The DAG/workflow subsystem stays private and is bundled into the CLI; no new runtime `@robota-sdk`
2340
+ edge is added.)
2341
+
2342
+ ## 3.0.0-beta.78
2343
+
2344
+ ## 3.0.0-beta.77
2345
+
2346
+ ### Patch Changes
2347
+
2348
+ - Updated dependencies
2349
+ - @robota-sdk/agent-core@3.0.0-beta.77
2350
+ - @robota-sdk/agent-command@3.0.0-beta.77
2351
+ - @robota-sdk/agent-executor@3.0.0-beta.77
2352
+ - @robota-sdk/agent-framework@3.0.0-beta.77
2353
+ - @robota-sdk/agent-provider@3.0.0-beta.77
2354
+ - @robota-sdk/agent-session-analytics@3.0.0-beta.77
2355
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.77
2356
+ - @robota-sdk/agent-transport@3.0.0-beta.77
2357
+ - @robota-sdk/agent-transport-tui@3.0.0-beta.77
2358
+ - @robota-sdk/agent-transport-ws@3.0.0-beta.77
2359
+ - @robota-sdk/agent-preset@3.0.0-beta.77
2360
+
2361
+ ## 3.0.0-beta.76
2362
+
2363
+ ### Patch Changes
2364
+
2365
+ - c0a6287: Relocate session feature logic out of the CLI shell and the transport (DQ-AUDIT-004):
2366
+
2367
+ - Extract session-log timing analysis into the new `@robota-sdk/agent-session-analytics` package (pure analysis over canonical session records — no duplicate types, no file I/O). `agent-cli`'s `session analyze` command shrinks to thin wiring and loads records via the new `createUserSessionStore()` / existing `createProjectSessionStore()` framework facades.
2368
+ - Move LLM-based session auto-naming (`generateSessionName`) from `agent-transport/tui` into `agent-framework` (session-lifecycle owner); the TUI transport now invokes it through the framework.
2369
+
2370
+ - 9df3a88: Split the consolidated `@robota-sdk/agent-transport` package into per-concern transport packages (DQ-AUDIT-005) so unrelated heavy dependencies (React/Ink, ws, Hono, MCP SDK) no longer share one publishable unit and are not dragged into non-TUI consumers' graphs:
2371
+
2372
+ - `@robota-sdk/agent-transport` — lean core: headless adapter + `TransportRegistry` + scripted-provider testing fixtures (no external runtime deps).
2373
+ - `@robota-sdk/agent-transport-tui` — React + Ink terminal UI.
2374
+ - `@robota-sdk/agent-transport-ws` — WebSocket transport + protocol (`agent-web-ui` now depends only on this for WS types).
2375
+ - `@robota-sdk/agent-transport-http` — Hono HTTP transport.
2376
+ - `@robota-sdk/agent-transport-mcp` — MCP server transport.
2377
+
2378
+ The default transport-registry wiring (pre-registering `WsTransport`) moves to the CLI composition root, removing the core→ws edge.
2379
+
2380
+ - Updated dependencies
2381
+ - Updated dependencies
2382
+ - Updated dependencies [c0a6287]
2383
+ - Updated dependencies [9df3a88]
2384
+ - Updated dependencies
2385
+ - Updated dependencies
2386
+ - Updated dependencies [576af62]
2387
+ - @robota-sdk/agent-core@3.0.0-beta.76
2388
+ - @robota-sdk/agent-command@3.0.0-beta.76
2389
+ - @robota-sdk/agent-transport@3.0.0-beta.76
2390
+ - @robota-sdk/agent-transport-tui@3.0.0-beta.76
2391
+ - @robota-sdk/agent-session-analytics@3.0.0-beta.76
2392
+ - @robota-sdk/agent-framework@3.0.0-beta.76
2393
+ - @robota-sdk/agent-transport-ws@3.0.0-beta.76
2394
+ - @robota-sdk/agent-provider@3.0.0-beta.76
2395
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.76
2396
+ - @robota-sdk/agent-preset@3.0.0-beta.76
2397
+
2398
+ ## 3.0.0-beta.75
2399
+
2400
+ ### Patch Changes
2401
+
2402
+ - Agent preset system + live preset switching + context/history correctness fixes.
2403
+
2404
+ - **Preset system (PRESET-001~017):** new `@robota-sdk/agent-preset` package layering framework
2405
+ assembly options into named, selectable profiles (`default`, `autonomous-builder`, `careful-reviewer`,
2406
+ `neutral-executor`) plus user-authored external presets loaded from `$PRODUCT_USER_STATE_DIR/presets/*.json`.
2407
+ - **Live preset switching:** `/preset` command (list + active marker + switch) and a TUI active-preset
2408
+ display. Switching live re-applies permission posture, model/effort, persona, command-module
2409
+ selection, parallel-subagents gating, and a self-verification system-prompt section via the single
2410
+ `applyPresetToSession` engine.
2411
+ - **CTX-001:** the TUI Context display + session auto-compact now use the accurate provider-based token
2412
+ estimate (system prompt + tool schemas included) instead of a crude history-only char heuristic.
2413
+ - **HIST-001:** conversation history is now append-only — removed the silent 100-message count cap that
2414
+ could drop early context; context size is managed solely by size-based compaction.
2415
+
2416
+ - Updated dependencies
2417
+ - @robota-sdk/agent-preset@3.0.0-beta.75
2418
+ - @robota-sdk/agent-framework@3.0.0-beta.75
2419
+ - @robota-sdk/agent-core@3.0.0-beta.75
2420
+ - @robota-sdk/agent-command@3.0.0-beta.75
2421
+ - @robota-sdk/agent-transport@3.0.0-beta.75
2422
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.75
2423
+ - @robota-sdk/agent-provider@3.0.0-beta.75
2424
+
2425
+ ## 3.0.0-beta.74
2426
+
2427
+ ### Patch Changes
2428
+
2429
+ - Updated dependencies
2430
+ - @robota-sdk/agent-framework@3.0.0-beta.74
2431
+ - @robota-sdk/agent-command@3.0.0-beta.74
2432
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.74
2433
+ - @robota-sdk/agent-transport@3.0.0-beta.74
2434
+ - @robota-sdk/agent-core@3.0.0-beta.74
2435
+ - @robota-sdk/agent-provider@3.0.0-beta.74
2436
+
2437
+ ## 3.0.0-beta.73
2438
+
2439
+ ### Patch Changes
2440
+
2441
+ - Updated dependencies [d4fd33f]
2442
+ - @robota-sdk/agent-transport@3.0.0-beta.73
2443
+ - @robota-sdk/agent-command@3.0.0-beta.73
2444
+ - @robota-sdk/agent-core@3.0.0-beta.73
2445
+ - @robota-sdk/agent-framework@3.0.0-beta.73
2446
+ - @robota-sdk/agent-provider@3.0.0-beta.73
2447
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.73
2448
+
2449
+ ## 3.0.0-beta.72
2450
+
2451
+ ### Patch Changes
2452
+
2453
+ - Updated dependencies
2454
+ - Updated dependencies
2455
+ - @robota-sdk/agent-framework@3.0.0-beta.72
2456
+ - @robota-sdk/agent-transport@3.0.0-beta.72
2457
+ - @robota-sdk/agent-command@3.0.0-beta.72
2458
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.72
2459
+ - @robota-sdk/agent-core@3.0.0-beta.72
2460
+ - @robota-sdk/agent-provider@3.0.0-beta.72
2461
+
2462
+ ## 3.0.0-beta.71
2463
+
2464
+ ### Patch Changes
2465
+
2466
+ - fix(context): unify token estimation to single SSOT — status bar and /context list now use the same serialized JSON estimate
2467
+ - Updated dependencies
2468
+ - @robota-sdk/agent-command@3.0.0-beta.71
2469
+ - @robota-sdk/agent-core@3.0.0-beta.71
2470
+ - @robota-sdk/agent-framework@3.0.0-beta.71
2471
+ - @robota-sdk/agent-provider@3.0.0-beta.71
2472
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.71
2473
+ - @robota-sdk/agent-transport@3.0.0-beta.71
2474
+
2475
+ ## 3.0.0-beta.70
2476
+
2477
+ ### Patch Changes
2478
+
2479
+ - Updated dependencies
2480
+ - @robota-sdk/agent-command@3.0.0-beta.70
2481
+ - @robota-sdk/agent-transport@3.0.0-beta.70
2482
+ - @robota-sdk/agent-framework@3.0.0-beta.70
2483
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.70
2484
+ - @robota-sdk/agent-core@3.0.0-beta.70
2485
+ - @robota-sdk/agent-provider@3.0.0-beta.70
2486
+
2487
+ ## 3.0.0-beta.69
2488
+
2489
+ ### Patch Changes
2490
+
2491
+ - Updated dependencies
2492
+ - @robota-sdk/agent-framework@3.0.0-beta.69
2493
+ - @robota-sdk/agent-command@3.0.0-beta.69
2494
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.69
2495
+ - @robota-sdk/agent-transport@3.0.0-beta.69
2496
+ - @robota-sdk/agent-core@3.0.0-beta.69
2497
+ - @robota-sdk/agent-provider@3.0.0-beta.69
2498
+
2499
+ ## 3.0.0-beta.68
2500
+
2501
+ ### Patch Changes
2502
+
2503
+ - Updated dependencies
2504
+ - @robota-sdk/agent-transport@3.0.0-beta.68
2505
+ - @robota-sdk/agent-command@3.0.0-beta.68
2506
+ - @robota-sdk/agent-core@3.0.0-beta.68
2507
+ - @robota-sdk/agent-framework@3.0.0-beta.68
2508
+ - @robota-sdk/agent-provider@3.0.0-beta.68
2509
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.68
2510
+
2511
+ ## 3.0.0-beta.67
2512
+
2513
+ ### Patch Changes
2514
+
2515
+ - CLIR: agent-cli layer separation, agent-framework interactive session improvements, subagent runner fix, TUI interface README
2516
+ - Updated dependencies
2517
+ - @robota-sdk/agent-framework@3.0.0-beta.67
2518
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.67
2519
+ - @robota-sdk/agent-command@3.0.0-beta.67
2520
+ - @robota-sdk/agent-transport@3.0.0-beta.67
2521
+ - @robota-sdk/agent-core@3.0.0-beta.67
2522
+ - @robota-sdk/agent-provider@3.0.0-beta.67
2523
+
2524
+ ## 3.0.0-beta.66
2525
+
2526
+ ### Patch Changes
2527
+
2528
+ - refactor: CLI-001/002 — agent-cli layer separation and monorepo-wide readability lint rules
2529
+ - CLI-001: Extract startup phases into focused modules; enforce agent-cli layer separation
2530
+ - CLI-002: Apply import/order, consistent-type-imports, explicit-function-return-type, prefer-const, object-shorthand across all packages
2531
+ - Fix stale child-process-subagent-worker entry in agent-cli tsdown.config.ts (build fix)
2532
+
2533
+ - Updated dependencies
2534
+ - @robota-sdk/agent-framework@3.0.0-beta.66
2535
+ - @robota-sdk/agent-command@3.0.0-beta.66
2536
+ - @robota-sdk/agent-transport@3.0.0-beta.66
2537
+ - @robota-sdk/agent-subagent-runner@3.0.0-beta.66
2538
+ - @robota-sdk/agent-core@3.0.0-beta.66
2539
+ - @robota-sdk/agent-executor@3.0.0-beta.66
2540
+ - @robota-sdk/agent-provider@3.0.0-beta.66
2541
+
2542
+ ## 3.0.0-beta.65
2543
+
2544
+ ### Minor Changes
2545
+
2546
+ - feat(CMD-003): TUI command interaction — picker/confirm overlay on missing args
2547
+ - Add `command-interaction.ts` with `ITuiPickerInteraction`, `ITuiConfirmInteraction`, `TAnyTuiCommandInteraction` types
2548
+ - Add `CommandPicker` and `CommandConfirm` Ink overlay components
2549
+ - Extend `TCommandSelectionResult` with `open-interaction` variant in input-area-flow
2550
+ - `resolveEnterCommandSelection` opens interaction overlay when command has `onMissingArgs` and no args typed
2551
+ - `InputArea` accepts `resolveInteraction` prop; renders picker/confirm overlay when active
2552
+ - Add `resolveInteraction` to `IRenderOptions` / `App` / `render`
2553
+ - Add `TUI_COMMAND_INTERACTIONS` registry in `agent-cli` with mode/language/provider/exit/clear interactions
2554
+ - Fix `SlashAutocomplete` to show technical command name (`cmd.name`) instead of `displayName`
2555
+
2556
+ ### Patch Changes
2557
+
2558
+ - Updated dependencies
2559
+ - @robota-sdk/agent-transport@3.0.0-beta.65
2560
+ - @robota-sdk/agent-command@3.0.0-beta.65
2561
+ - @robota-sdk/agent-core@3.0.0-beta.65
2562
+ - @robota-sdk/agent-executor@3.0.0-beta.65
2563
+ - @robota-sdk/agent-framework@3.0.0-beta.65
2564
+ - @robota-sdk/agent-interface-transport@3.0.0-beta.65
2565
+ - @robota-sdk/agent-provider@3.0.0-beta.65
2566
+
2567
+ ## 3.0.0-beta.64
2568
+
2569
+ ### Patch Changes
2570
+
2571
+ - Updated dependencies
2572
+ - @robota-sdk/agent-framework@3.0.0-beta.64
2573
+ - @robota-sdk/agent-command@3.0.0-beta.64
2574
+ - @robota-sdk/agent-transport@3.0.0-beta.64
2575
+ - @robota-sdk/agent-core@3.0.0-beta.64
2576
+ - @robota-sdk/agent-executor@3.0.0-beta.64
2577
+ - @robota-sdk/agent-interface-transport@3.0.0-beta.64
2578
+ - @robota-sdk/agent-provider@3.0.0-beta.64
2579
+
2580
+ ## 3.0.0-beta.63
2581
+
2582
+ ### Patch Changes
2583
+
2584
+ - Add --help / -h CLI flag to print usage information and exit
2585
+ - @robota-sdk/agent-core@3.0.0-beta.63
2586
+ - @robota-sdk/agent-framework@3.0.0-beta.63
2587
+ - @robota-sdk/agent-command-agent@3.0.0-beta.63
2588
+ - @robota-sdk/agent-command-background@3.0.0-beta.63
2589
+ - @robota-sdk/agent-command-compact@3.0.0-beta.63
2590
+ - @robota-sdk/agent-command-context@3.0.0-beta.63
2591
+ - @robota-sdk/agent-command-exit@3.0.0-beta.63
2592
+ - @robota-sdk/agent-command-help@3.0.0-beta.63
2593
+ - @robota-sdk/agent-command-language@3.0.0-beta.63
2594
+ - @robota-sdk/agent-command-memory@3.0.0-beta.63
2595
+ - @robota-sdk/agent-command-mode@3.0.0-beta.63
2596
+ - @robota-sdk/agent-command-model@3.0.0-beta.63
2597
+ - @robota-sdk/agent-command-permissions@3.0.0-beta.63
2598
+ - @robota-sdk/agent-command-plugin@3.0.0-beta.63
2599
+ - @robota-sdk/agent-command-provider@3.0.0-beta.63
2600
+ - @robota-sdk/agent-command-reset@3.0.0-beta.63
2601
+ - @robota-sdk/agent-command-rewind@3.0.0-beta.63
2602
+ - @robota-sdk/agent-command-session@3.0.0-beta.63
2603
+ - @robota-sdk/agent-command-statusline@3.0.0-beta.63
2604
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.63
2605
+ - @robota-sdk/agent-provider-deepseek@3.0.0-beta.63
2606
+ - @robota-sdk/agent-provider-openai@3.0.0-beta.63
2607
+ - @robota-sdk/agent-provider-gemma@3.0.0-beta.63
2608
+ - @robota-sdk/agent-provider-gemini@3.0.0-beta.63
2609
+ - @robota-sdk/agent-transport-ws@3.0.0-beta.63
2610
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.63
2611
+ - @robota-sdk/agent-command-settings@3.0.0-beta.63
2612
+ - @robota-sdk/agent-command-skills@3.0.0-beta.63
2613
+ - @robota-sdk/agent-command-user-local@3.0.0-beta.63
2614
+ - @robota-sdk/agent-interface-transport@3.0.0-beta.63
2615
+ - @robota-sdk/agent-provider-qwen@3.0.0-beta.63
2616
+ - @robota-sdk/agent-transport-tui@3.0.0-beta.63
2617
+
2618
+ ## 3.0.0-beta.62
2619
+
2620
+ ### Patch Changes
2621
+
2622
+ - Updated dependencies
2623
+ - @robota-sdk/agent-core@3.0.0-beta.62
2624
+ - @robota-sdk/agent-command-provider@3.0.0-beta.62
2625
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.62
2626
+ - @robota-sdk/agent-provider-deepseek@3.0.0-beta.62
2627
+ - @robota-sdk/agent-provider-gemini@3.0.0-beta.62
2628
+ - @robota-sdk/agent-provider-gemma@3.0.0-beta.62
2629
+ - @robota-sdk/agent-provider-openai@3.0.0-beta.62
2630
+ - @robota-sdk/agent-provider-qwen@3.0.0-beta.62
2631
+ - @robota-sdk/agent-framework@3.0.0-beta.62
2632
+ - @robota-sdk/agent-command-agent@3.0.0-beta.62
2633
+ - @robota-sdk/agent-command-background@3.0.0-beta.62
2634
+ - @robota-sdk/agent-command-compact@3.0.0-beta.62
2635
+ - @robota-sdk/agent-command-context@3.0.0-beta.62
2636
+ - @robota-sdk/agent-command-exit@3.0.0-beta.62
2637
+ - @robota-sdk/agent-command-help@3.0.0-beta.62
2638
+ - @robota-sdk/agent-command-language@3.0.0-beta.62
2639
+ - @robota-sdk/agent-command-memory@3.0.0-beta.62
2640
+ - @robota-sdk/agent-command-model@3.0.0-beta.62
2641
+ - @robota-sdk/agent-command-permissions@3.0.0-beta.62
2642
+ - @robota-sdk/agent-command-plugin@3.0.0-beta.62
2643
+ - @robota-sdk/agent-command-reset@3.0.0-beta.62
2644
+ - @robota-sdk/agent-command-rewind@3.0.0-beta.62
2645
+ - @robota-sdk/agent-command-session@3.0.0-beta.62
2646
+ - @robota-sdk/agent-command-skills@3.0.0-beta.62
2647
+ - @robota-sdk/agent-command-statusline@3.0.0-beta.62
2648
+ - @robota-sdk/agent-command-user-local@3.0.0-beta.62
2649
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.62
2650
+ - @robota-sdk/agent-transport-ws@3.0.0-beta.62
2651
+
2652
+ ## 3.0.0-beta.61
2653
+
2654
+ ### Minor Changes
2655
+
2656
+ - b7cb169: Add first-class DeepSeek API provider support and include it in the default CLI provider definitions.
2657
+
2658
+ ### Patch Changes
2659
+
2660
+ - cfb8b5a: Clean up the CLI status bar by hiding the baseline default permission mode and removing the duplicate right-side thinking indicator.
2661
+ - cc0223d: Add SDK-owned provider profile name suggestions, create model-derived profile keys during interactive setup, and show the active provider profile identity in the CLI status area.
2662
+ - 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.
2663
+ - Updated dependencies [e243fb0]
2664
+ - Updated dependencies [b7cb169]
2665
+ - Updated dependencies [1c0d44c]
2666
+ - Updated dependencies [36eb7a9]
2667
+ - Updated dependencies [cc0223d]
2668
+ - Updated dependencies [18fcc5b]
2669
+ - Updated dependencies [d97bdf2]
2670
+ - Updated dependencies [3bde012]
2671
+ - @robota-sdk/agent-framework@3.0.0-beta.61
2672
+ - @robota-sdk/agent-provider-deepseek@3.0.0-beta.61
2673
+ - @robota-sdk/agent-core@3.0.0-beta.61
2674
+ - @robota-sdk/agent-command-session@3.0.0-beta.61
2675
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.61
2676
+ - @robota-sdk/agent-provider-gemini@3.0.0-beta.61
2677
+ - @robota-sdk/agent-provider-gemma@3.0.0-beta.61
2678
+ - @robota-sdk/agent-provider-openai@3.0.0-beta.61
2679
+ - @robota-sdk/agent-provider-qwen@3.0.0-beta.61
2680
+ - @robota-sdk/agent-command-provider@3.0.0-beta.61
2681
+ - @robota-sdk/agent-command-model@3.0.0-beta.61
2682
+ - @robota-sdk/agent-command-agent@3.0.0-beta.61
2683
+ - @robota-sdk/agent-command-background@3.0.0-beta.61
2684
+ - @robota-sdk/agent-command-compact@3.0.0-beta.61
2685
+ - @robota-sdk/agent-command-context@3.0.0-beta.61
2686
+ - @robota-sdk/agent-command-exit@3.0.0-beta.61
2687
+ - @robota-sdk/agent-command-help@3.0.0-beta.61
2688
+ - @robota-sdk/agent-command-language@3.0.0-beta.61
2689
+ - @robota-sdk/agent-command-memory@3.0.0-beta.61
2690
+ - @robota-sdk/agent-command-permissions@3.0.0-beta.61
2691
+ - @robota-sdk/agent-command-plugin@3.0.0-beta.61
2692
+ - @robota-sdk/agent-command-reset@3.0.0-beta.61
2693
+ - @robota-sdk/agent-command-rewind@3.0.0-beta.61
2694
+ - @robota-sdk/agent-command-statusline@3.0.0-beta.61
2695
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.61
2696
+ - @robota-sdk/agent-command-skills@3.0.0-beta.61
2697
+
2698
+ ## 3.0.0-beta.60
2699
+
2700
+ ### Minor Changes
2701
+
2702
+ - 7439391: Add provider-neutral native web search/fetch capability contracts, explicit unsupported handling for OpenAI-compatible/LM Studio profiles, and local WebFetch/WebSearch permission/documentation alignment.
2703
+
2704
+ ### Patch Changes
2705
+
2706
+ - 0c77089: Validate the merged active provider profile during CLI startup so higher-priority provider selections with missing API keys are not masked by unrelated valid settings files.
2707
+ - 41ae788: Restore the CLI thinking indicator and add structured Agent tool batch provenance/count metadata.
2708
+ - Updated dependencies [41ae788]
2709
+ - Updated dependencies [3d6bdf6]
2710
+ - Updated dependencies [7439391]
2711
+ - @robota-sdk/agent-framework@3.0.0-beta.60
2712
+ - @robota-sdk/agent-provider-gemini@3.0.0-beta.60
2713
+ - @robota-sdk/agent-core@3.0.0-beta.60
2714
+ - @robota-sdk/agent-session@3.0.0-beta.60
2715
+ - @robota-sdk/agent-provider-openai@3.0.0-beta.60
2716
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.60
2717
+ - @robota-sdk/agent-provider-gemma@3.0.0-beta.60
2718
+ - @robota-sdk/agent-provider-qwen@3.0.0-beta.60
2719
+ - @robota-sdk/agent-command-agent@3.0.0-beta.60
2720
+ - @robota-sdk/agent-command-background@3.0.0-beta.60
2721
+ - @robota-sdk/agent-command-compact@3.0.0-beta.60
2722
+ - @robota-sdk/agent-command-context@3.0.0-beta.60
2723
+ - @robota-sdk/agent-command-exit@3.0.0-beta.60
2724
+ - @robota-sdk/agent-command-help@3.0.0-beta.60
2725
+ - @robota-sdk/agent-command-language@3.0.0-beta.60
2726
+ - @robota-sdk/agent-command-memory@3.0.0-beta.60
2727
+ - @robota-sdk/agent-command-mode@3.0.0-beta.60
2728
+ - @robota-sdk/agent-command-model@3.0.0-beta.60
2729
+ - @robota-sdk/agent-command-permissions@3.0.0-beta.60
2730
+ - @robota-sdk/agent-command-plugin@3.0.0-beta.60
2731
+ - @robota-sdk/agent-command-provider@3.0.0-beta.60
2732
+ - @robota-sdk/agent-command-reset@3.0.0-beta.60
2733
+ - @robota-sdk/agent-command-rewind@3.0.0-beta.60
2734
+ - @robota-sdk/agent-command-session@3.0.0-beta.60
2735
+ - @robota-sdk/agent-command-statusline@3.0.0-beta.60
2736
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.60
2737
+
2738
+ ## 3.0.0-beta.59
2739
+
2740
+ ### Patch Changes
2741
+
2742
+ - @robota-sdk/agent-framework@3.0.0-beta.59
2743
+ - @robota-sdk/agent-command-agent@3.0.0-beta.59
2744
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.59
2745
+ - @robota-sdk/agent-core@3.0.0-beta.59
2746
+ - @robota-sdk/agent-session@3.0.0-beta.59
2747
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.59
2748
+ - @robota-sdk/agent-provider-openai@3.0.0-beta.59
2749
+ - @robota-sdk/agent-provider-gemma@3.0.0-beta.59
2750
+ - @robota-sdk/agent-provider-gemini@3.0.0-beta.59
2751
+ - @robota-sdk/agent-provider-qwen@3.0.0-beta.59
2752
+
2753
+ ## 3.0.0-beta.58
2754
+
2755
+ ### Patch Changes
2756
+
2757
+ - Refresh package docs and Robota.io content for the beta 57 feature set.
2758
+ - Updated dependencies
2759
+ - @robota-sdk/agent-command-agent@3.0.0-beta.58
2760
+ - @robota-sdk/agent-core@3.0.0-beta.58
2761
+ - @robota-sdk/agent-provider-gemini@3.0.0-beta.58
2762
+ - @robota-sdk/agent-provider-gemma@3.0.0-beta.58
2763
+ - @robota-sdk/agent-provider-qwen@3.0.0-beta.58
2764
+ - @robota-sdk/agent-framework@3.0.0-beta.58
2765
+ - @robota-sdk/agent-session@3.0.0-beta.58
2766
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.58
2767
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.58
2768
+ - @robota-sdk/agent-provider-openai@3.0.0-beta.58
2769
+
2770
+ ## 3.0.0-beta.57
2771
+
2772
+ ### Minor Changes
2773
+
2774
+ - b80e51e: Add SDK-owned automatic project memory capture, approval review, bounded retrieval, and session-log provenance.
2775
+ - f61e2cb: Add Qwen provider-owned Responses API support for built-in web search/fetch tools and pass provider-owned profile options through generic CLI/runtime configuration.
2776
+
2777
+ ### Patch Changes
2778
+
2779
+ - 16c3b6f: Persist and render provider-neutral per-turn usage summaries with pre-send context updates in CLI sessions.
2780
+ - d3bcc0e: Move the active status indicator into the primary status-bar scan path with deterministic tool, thinking, background, queued, and idle priority.
2781
+ - 4eca470: Render command tool output as bounded transcript previews and persist tool result metadata in SDK tool summaries.
2782
+ - 1cfdce9: Add SDK-owned edit checkpointing for Write/Edit tool mutations with `/rewind` list and code restore commands.
2783
+ - 90a2802: Render Edit tool summaries as context-aware diff hunks with structured truncation metadata.
2784
+ - 26a1718: Preserve and render Edit tool diff metadata in persisted CLI tool summaries.
2785
+ - 3509f1d: Add canonical Gemini provider package and keep the Google provider package as a compatibility wrapper.
2786
+ - 8e056b1: Adopt Ink 7 paste and window-size hooks for CJK-aware input handling.
2787
+ - 7e9e81c: Render background work as compact one-level tree rows with shared formatting and bounded previews.
2788
+ - Updated dependencies [16c3b6f]
2789
+ - Updated dependencies [b80e51e]
2790
+ - Updated dependencies [4eca470]
2791
+ - Updated dependencies [1cfdce9]
2792
+ - Updated dependencies [90a2802]
2793
+ - Updated dependencies [26a1718]
2794
+ - Updated dependencies [3509f1d]
2795
+ - Updated dependencies [e504d30]
2796
+ - Updated dependencies [f61e2cb]
2797
+ - Updated dependencies [0e0e533]
2798
+ - Updated dependencies [822a78b]
2799
+ - Updated dependencies [9817f99]
2800
+ - @robota-sdk/agent-core@3.0.0-beta.57
2801
+ - @robota-sdk/agent-session@3.0.0-beta.57
2802
+ - @robota-sdk/agent-framework@3.0.0-beta.57
2803
+ - @robota-sdk/agent-provider-gemini@3.0.0-beta.57
2804
+ - @robota-sdk/agent-provider-qwen@3.0.0-beta.57
2805
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.57
2806
+ - @robota-sdk/agent-provider-gemma@3.0.0-beta.57
2807
+ - @robota-sdk/agent-provider-openai@3.0.0-beta.57
2808
+ - @robota-sdk/agent-command-agent@3.0.0-beta.57
2809
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.57
2810
+
2811
+ ## 3.0.0-beta.56
2812
+
2813
+ ### Patch Changes
2814
+
2815
+ - Updated dependencies
2816
+ - @robota-sdk/agent-core@3.0.0-beta.56
2817
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.56
2818
+ - @robota-sdk/agent-provider-gemma@3.0.0-beta.56
2819
+ - @robota-sdk/agent-provider-openai@3.0.0-beta.56
2820
+ - @robota-sdk/agent-framework@3.0.0-beta.56
2821
+ - @robota-sdk/agent-session@3.0.0-beta.56
2822
+ - @robota-sdk/agent-command-agent@3.0.0-beta.56
2823
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.56
2824
+
2825
+ ## 3.0.0-beta.55
2826
+
2827
+ ### Patch Changes
2828
+
2829
+ - 38a72bf: fix: resolve ESLint tsconfig parsing errors and improve pnpm CI reliability
2830
+ - Add tsconfig.eslint.json to all packages for per-package ESLint runs
2831
+ - Migrate typecheck from pnpm -r exec tsc to per-package typecheck scripts
2832
+ - Add --if-present to all recursive pnpm run scripts
2833
+ - Fix React type imports, dynamic imports in tests, Express.Multer types
2834
+
2835
+ - Updated dependencies [38a72bf]
2836
+ - @robota-sdk/agent-core@3.0.0-beta.55
2837
+ - @robota-sdk/agent-framework@3.0.0-beta.55
2838
+ - @robota-sdk/agent-session@3.0.0-beta.55
2839
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.55
2840
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.55
2841
+
2842
+ ## 3.0.0-beta.54
2843
+
2844
+ ### Patch Changes
2845
+
2846
+ - fix: resolve all typecheck errors across packages
2847
+ - Updated dependencies
2848
+ - @robota-sdk/agent-session@3.0.0-beta.54
2849
+ - @robota-sdk/agent-framework@3.0.0-beta.54
2850
+ - @robota-sdk/agent-core@3.0.0-beta.54
2851
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.54
2852
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.54
2853
+
2854
+ ## 3.0.0-beta.53
2855
+
2856
+ ### Patch Changes
2857
+
2858
+ - refactor: monolith decomposition — all agent-\* files under 300 lines
2859
+ - fix: PR #69 code review — session resume tool messages, type SSOT, fork isolation, settings crash, Notification removal, chat validation
2860
+ - Updated dependencies
2861
+ - Updated dependencies
2862
+ - @robota-sdk/agent-core@3.0.0-beta.53
2863
+ - @robota-sdk/agent-framework@3.0.0-beta.53
2864
+ - @robota-sdk/agent-session@3.0.0-beta.53
2865
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.53
2866
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.53
2867
+
2868
+ ## 3.0.0-beta.52
2869
+
2870
+ ### Patch Changes
2871
+
2872
+ - fix: paste at cursor position + cursor moves to end of pasted label + cursorHint prop
2873
+ - @robota-sdk/agent-core@3.0.0-beta.52
2874
+ - @robota-sdk/agent-session@3.0.0-beta.52
2875
+ - @robota-sdk/agent-framework@3.0.0-beta.52
2876
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.52
2877
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.52
2878
+
2879
+ ## 3.0.0-beta.51
2880
+
2881
+ ### Patch Changes
2882
+
2883
+ - feat: debounce streaming text rendering (300ms) to reduce CPU load
2884
+ - @robota-sdk/agent-core@3.0.0-beta.51
2885
+ - @robota-sdk/agent-session@3.0.0-beta.51
2886
+ - @robota-sdk/agent-framework@3.0.0-beta.51
2887
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.51
2888
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.51
2889
+
2890
+ ## 3.0.0-beta.50
2891
+
2892
+ ### Patch Changes
2893
+
2894
+ - fix: reinsert repository/homepage/bugs in correct field order
2895
+ - Updated dependencies
2896
+ - @robota-sdk/agent-framework@3.0.0-beta.50
2897
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.50
2898
+ - @robota-sdk/agent-core@3.0.0-beta.50
2899
+ - @robota-sdk/agent-session@3.0.0-beta.50
2900
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.50
2901
+
2902
+ ## 3.0.0-beta.49
2903
+
2904
+ ### Patch Changes
2905
+
2906
+ - fix: add repository, homepage, bugs metadata to all publishable packages
2907
+ - Updated dependencies
2908
+ - @robota-sdk/agent-framework@3.0.0-beta.49
2909
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.49
2910
+ - @robota-sdk/agent-core@3.0.0-beta.49
2911
+ - @robota-sdk/agent-session@3.0.0-beta.49
2912
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.49
2913
+
2914
+ ## 3.0.0-beta.48
2915
+
2916
+ ### Patch Changes
2917
+
2918
+ - fix: record individual tool-start/tool-end in history + fix streaming tool display
2919
+ - Individual tool-start/tool-end events recorded as IHistoryEntry for persistence
2920
+ - TuiStateManager.onToolEnd uses findIndex (first match only, not all with same name)
2921
+ - MessageList hides tool-start/tool-end entries (not rendered as System:)
2922
+
2923
+ - Updated dependencies
2924
+ - @robota-sdk/agent-framework@3.0.0-beta.48
2925
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.48
2926
+ - @robota-sdk/agent-core@3.0.0-beta.48
2927
+ - @robota-sdk/agent-session@3.0.0-beta.48
2928
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.48
2929
+
2930
+ ## 3.0.0-beta.47
2931
+
2932
+ ### Minor Changes
2933
+
2934
+ - feat: ITransportAdapter unified interface + headless transport + CLI adapter pattern
2935
+ - ITransportAdapter interface in agent-sdk (name, attach, start, stop)
2936
+ - InteractiveSession.attachTransport(transport) method
2937
+ - createHttpTransport, createWsTransport, createMcpTransport, createHeadlessTransport factories
2938
+ - CLI print mode uses adapter pattern: session.attachTransport(transport)
2939
+ - agent-transport-headless: text/json/stream-json output, stdin pipe, exit codes
2940
+ - --output-format, --system-prompt, --append-system-prompt CLI flags
2941
+
2942
+ ### Patch Changes
2943
+
2944
+ - Updated dependencies
2945
+ - @robota-sdk/agent-framework@3.0.0-beta.47
2946
+ - @robota-sdk/agent-transport-headless@3.0.0-beta.47
2947
+ - @robota-sdk/agent-core@3.0.0-beta.47
2948
+ - @robota-sdk/agent-session@3.0.0-beta.47
2949
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.47
2950
+
2951
+ ## 3.0.0-beta.46
2952
+
2953
+ ### Minor Changes
2954
+
2955
+ - feat: session continue/resume — persist, restore, and switch sessions
2956
+ - ISessionRecord.history field (required) for UI timeline restoration
2957
+ - Session.injectMessage() for AI context restoration on resume
2958
+ - InteractiveSession: sessionStore, resumeSessionId, forkSession, getName/setName
2959
+ - CLI: --continue, --resume, --fork-session, --name flags
2960
+ - TUI: /resume (session picker), /rename (session naming)
2961
+ - ListPicker generic component with viewport scrolling
2962
+ - Session name display: input border title, terminal title, StatusBar
2963
+ - Session picker: cwd filtering, date+time, response preview
2964
+ - React key remount for instant session switching
2965
+
2966
+ ### Patch Changes
2967
+
2968
+ - Updated dependencies
2969
+ - @robota-sdk/agent-session@3.0.0-beta.46
2970
+ - @robota-sdk/agent-framework@3.0.0-beta.46
2971
+ - @robota-sdk/agent-core@3.0.0-beta.46
2972
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.46
2973
+
2974
+ ## 3.0.0-beta.45
2975
+
2976
+ ### Patch Changes
2977
+
2978
+ - Updated dependencies
2979
+ - @robota-sdk/agent-framework@3.0.0-beta.45
2980
+ - @robota-sdk/agent-core@3.0.0-beta.45
2981
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.45
2982
+
2983
+ ## 3.0.0-beta.44
2984
+
2985
+ ### Patch Changes
2986
+
2987
+ - feat: IHistoryEntry universal history architecture + test quality cleanup
2988
+ - IHistoryEntry as universal history type across all 4 packages (core → sessions → sdk → cli)
2989
+ - Tool summary stored as event entry in history (category: 'event', type: 'tool-summary')
2990
+ - TuiStateManager pure TypeScript class for CLI rendering state
2991
+ - MessageList renders IHistoryEntry[] with Tool:/System:/You:/Robota: labels
2992
+ - Display order fixed: Tool → Robota (both streaming and abort)
2993
+ - Remove 25 tautological, duplicate, and hardcoded tests
2994
+
2995
+ - Updated dependencies
2996
+ - @robota-sdk/agent-core@3.0.0-beta.44
2997
+ - @robota-sdk/agent-framework@3.0.0-beta.44
2998
+ - @robota-sdk/agent-provider-anthropic@3.0.0-beta.44