@robota-sdk/agent-session 3.0.0-beta.97 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1003 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,1007 @@
|
|
|
1
1
|
# @robota-sdk/agent-session
|
|
2
2
|
|
|
3
|
+
## 3.0.1
|
|
4
|
+
### Patch Changes
|
|
5
|
+
|
|
6
|
+
- @robota-sdk/agent-core@3.0.1
|
|
7
|
+
- @robota-sdk/agent-file-authority@3.0.1
|
|
8
|
+
- @robota-sdk/agent-interface-execution@3.0.1
|
|
9
|
+
- @robota-sdk/agent-interface-session@3.0.1
|
|
10
|
+
|
|
11
|
+
## 3.0.0
|
|
12
|
+
### Major Changes
|
|
13
|
+
|
|
14
|
+
- 807d161: **BREAKING — ARCH-010: the execution root is a required contract field, and the containment guard now fails closed.**
|
|
15
|
+
|
|
16
|
+
The file-tool containment guard was fail-open: with no root configured it answered "allowed". A tool
|
|
17
|
+
built with no `cwd` therefore had no boundary — measured, a `Read` constructed that way returned the
|
|
18
|
+
contents of `/etc/hostname` — and the child-process subagent worker called `createDefaultTools()` with
|
|
19
|
+
no argument at all, so subagents got exactly that. Three independent auditors found three different
|
|
20
|
+
symptoms of this one missing field.
|
|
21
|
+
|
|
22
|
+
**Removed — seven context-free tool singletons.** `readTool`, `writeTool`, `editTool`, `globTool`,
|
|
23
|
+
`grepTool`, `shellTool`, `bashTool` are gone from `@robota-sdk/agent-tools`. A module-level instance is
|
|
24
|
+
bound at import time and can carry no containment root, so after the guard was inverted they could only
|
|
25
|
+
refuse everything.
|
|
26
|
+
|
|
27
|
+
Migrate to the factory of the same name, passing the directory the tool is allowed to work in:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// before
|
|
31
|
+
import { readTool, globTool } from '@robota-sdk/agent-tools';
|
|
32
|
+
const tools = [readTool, globTool];
|
|
33
|
+
|
|
34
|
+
// after
|
|
35
|
+
import { createReadTool, createGlobTool } from '@robota-sdk/agent-tools';
|
|
36
|
+
const cwd = process.cwd(); // or the workspace this agent is scoped to
|
|
37
|
+
const tools = [createReadTool({ cwd }), createGlobTool({ cwd })];
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`webFetchTool`, `webSearchTool` and `askUserQuestionTool` are unchanged — they touch no filesystem, so
|
|
41
|
+
there is no root to contain them by.
|
|
42
|
+
|
|
43
|
+
**`cwd` is now REQUIRED** on `ISandboxToolOptions`, `IContainedBuiltinToolOptions` (and everything
|
|
44
|
+
extending them), `ICreateDefaultToolsOptions`, `ISessionOptions` and `ISubagentOptions`. The `= {}`
|
|
45
|
+
default parameter was removed from every builtin factory — that default was the mechanism by which
|
|
46
|
+
"forgot the root" was legal. `new Session({...})` without `cwd` no longer compiles, and also throws at
|
|
47
|
+
construction, because a required field is only required to a TypeScript caller.
|
|
48
|
+
|
|
49
|
+
**`Session` no longer reads `process.cwd()`.** It uses the root it was given, and `getCwd()` exposes it
|
|
50
|
+
so a fork or subagent asks the session instead of re-deriving a root that can disagree with it.
|
|
51
|
+
|
|
52
|
+
**Behavioural change even for callers that already passed a root**: a tool that somehow reaches the
|
|
53
|
+
guard with no root now REFUSES with an explicit error ("no containment root is configured … this is an
|
|
54
|
+
assembly bug, not a path problem") instead of allowing the access.
|
|
55
|
+
- 64ba748: **BREAKING — ARCH-042: project filesystem access is now an explicit, host-issued authority instead of an ambient consequence of `cwd`.**
|
|
56
|
+
|
|
57
|
+
`@robota-sdk/agent-framework` adds `WorkspaceTrustService` and the opaque
|
|
58
|
+
`IWorkspaceProjectAuthority`, plus bounded reader, settings-writer, state-storage, and mutation
|
|
59
|
+
facets. Public session, settings, context, checkpoint, memory, contribution, query, and replay
|
|
60
|
+
contracts consume those facets. A caller that does not supply `projectAccess` is deliberately
|
|
61
|
+
restricted to user-owned host state and receives no project filesystem capability.
|
|
62
|
+
|
|
63
|
+
The framework removes or renames ambient Node/project exports. Migrate `checkSettingsFile` to
|
|
64
|
+
`checkNodeHostSettingsFile`, `readMergedProviderSettingsFromPaths` to
|
|
65
|
+
`readMergedProviderSettingsFromSources`, `resolveProviderSettingsWriteTargetPath` to
|
|
66
|
+
`resolveProviderSettingsWriteTarget`, `FileSystemMemoryStore` / `createFileSystemMemoryStore` to
|
|
67
|
+
`WorkspaceMemoryStore` / `createWorkspaceMemoryStore`, and `PluginSettingsStore` to
|
|
68
|
+
`NodeHostPluginSettingsStore`. Host-only git helpers now carry the `FromNodeHost` suffix.
|
|
69
|
+
`projectPaths`, `resolveSettingsPathForScope`, and `getProviderSettingsPaths` are removed; project
|
|
70
|
+
consumers must use the authority facets rather than recover absolute paths.
|
|
71
|
+
|
|
72
|
+
`@robota-sdk/agent-session` renames the Node filesystem implementation `SessionStore` to
|
|
73
|
+
`NodeSessionStore` and adds explicit session-log/external-payload source and sink ports. Session
|
|
74
|
+
replay no longer resolves external payload files from an ambient directory.
|
|
75
|
+
|
|
76
|
+
`@robota-sdk/agent-interface-transport` changes `ISkillExecutionPort.loadCommands(cwd, home?)` to
|
|
77
|
+
the authority-bound `loadCommands()` and removes the optional absolute-path leak
|
|
78
|
+
`IInteractiveSessionStore.getFilePath`. `@robota-sdk/agent-command` consequently replaces the
|
|
79
|
+
`cwd` option of `createSkillsCommandModule` with required `contributionSources`; default command
|
|
80
|
+
composition accepts explicit contribution sources and discovers no project skills when none are
|
|
81
|
+
provided.
|
|
82
|
+
|
|
83
|
+
`@robota-sdk/agent-cli`, `@robota-sdk/agent-transport`, and
|
|
84
|
+
`@robota-sdk/agent-transport-tui` thread the trusted-or-restricted project decision through every
|
|
85
|
+
session surface. Embedded callers that need project settings, state, context, skills, checkpoints,
|
|
86
|
+
or mutation must mint access through `WorkspaceTrustService` and pass the returned
|
|
87
|
+
`projectAccess` (and a separately approved mutation/settings facet where required). Omitting it is
|
|
88
|
+
still type-compatible but is behaviorally breaking: the surface now fails closed instead of
|
|
89
|
+
reading or writing the current directory.
|
|
90
|
+
- 242a644: Require versioned, event-decoded session replay logs. Reject unknown, malformed, and unsupported
|
|
91
|
+
entries instead of dropping them or inventing message fields. Replay-only session loads and lists
|
|
92
|
+
report damaged logs explicitly. Legacy unversioned JSONL is not accepted; snapshot encoding remains
|
|
93
|
+
unchanged. Persisted logs now use schema version 1, and all replay entry points share the session-owned
|
|
94
|
+
decoder and preserve sidecar integrity failures.
|
|
95
|
+
|
|
96
|
+
### Minor Changes
|
|
97
|
+
|
|
98
|
+
- 9368d00: Advisor escalation: the main model can consult a second model at the decision points it chooses.
|
|
99
|
+
|
|
100
|
+
With an advisor configured (`--advisor <profile>[:<model>]`, or the `advisorModel` setting that
|
|
101
|
+
`/advisor` saves; the flag wins), the session gets an `Advisor({ question? })` tool. The advisor reads
|
|
102
|
+
the whole conversation — system prompt, messages, tool calls and results — serialized into one prompt
|
|
103
|
+
and sent with `toolChoice: 'none'`, truncated from the front to fit its window with the system prompt
|
|
104
|
+
kept, and declines when even that does not fit. Its answer comes back framed as guidance to verify;
|
|
105
|
+
an empty or refusing answer reads as declined. Calls are limited to two per turn and a fixed number
|
|
106
|
+
per session, parallel calls in one round share those limits, and a repeated question in the same
|
|
107
|
+
turn returns the earlier answer. A request that was sent counts even when the provider failed (the
|
|
108
|
+
decline is reported by class, never by its text); only a call declined before sending gives its slot
|
|
109
|
+
back.
|
|
110
|
+
|
|
111
|
+
Advisor usage, including in-process subagents', is recorded where each turn's usage is recorded —
|
|
112
|
+
the persisted session history, under the advisor's own provider and model — so `/cost`, usage reports
|
|
113
|
+
and resumed sessions include it. `/cost` now totals that history and prices each part on its own
|
|
114
|
+
model, showing "mixed" when more than one model was priced.
|
|
115
|
+
|
|
116
|
+
`/advisor <model>` and `/advisor off` change only where calls go, never the tool list, so the main
|
|
117
|
+
model's prompt cache is not invalidated mid-session; the tool is added only when a session starts
|
|
118
|
+
with an advisor. Sending history to a destination (provider type and endpoint host) the main model
|
|
119
|
+
does not already use needs a one-time consent per destination, kept in the user settings file; a
|
|
120
|
+
refusal is remembered for the session. The organization's `allowedProviders` applies, and
|
|
121
|
+
`PRODUCT_DISABLE_ADVISOR=1` turns it off completely. In-process subagents inherit the advisor, bound
|
|
122
|
+
to their own conversation; child-process subagents do not get it.
|
|
123
|
+
|
|
124
|
+
**`@robota-sdk/agent-framework` is `major` for one reason: `ICommandHostSessionAccess` gains a
|
|
125
|
+
required member,** `getSessionUsage()`, the session's persisted usage records. An external
|
|
126
|
+
implementation of that role port stops compiling until it adds the method. The rest is additive.
|
|
127
|
+
|
|
128
|
+
- `agent-session` — `formatConversationEntries`, the one text rendering of a conversation, now used
|
|
129
|
+
by compaction too. It keeps tool calls and results, marks a user message a peer session sent
|
|
130
|
+
(`user [from "peer:<id>"]`), and JSON-encodes every message onto one line so no content can forge
|
|
131
|
+
another entry; compaction previously flattened all of this. `Session.getProvider()` returns the
|
|
132
|
+
provider the session currently uses.
|
|
133
|
+
- `agent-framework` — `AdvisorController`, `createAdvisorTool`, the advisor spec helpers, provider
|
|
134
|
+
destinations (`describeProviderDestination`, `rememberProviderDestination`), `getSessionUsage` and
|
|
135
|
+
`ISessionUsageRecord`, the `onUsageRecorded` session option, and the optional `advisor` command
|
|
136
|
+
host adapter. Session assembly binds a host-supplied Advisor tool to the session holding it.
|
|
137
|
+
- `agent-command` — the `/advisor` command module; `/cost` reads the session's persisted usage.
|
|
138
|
+
- `agent-cli` — the `--advisor` flag, `advisorModel` setting, per-destination consent store and kill
|
|
139
|
+
switch.
|
|
140
|
+
- `agent-ui-terminal` — a usage line from another source (the advisor, a background task) names that
|
|
141
|
+
source and leaves out the context window it does not have.
|
|
142
|
+
- `agent-session-analytics` — personal usage counts an advisor call's tokens and cost toward its
|
|
143
|
+
turn without counting it as a turn.
|
|
144
|
+
- 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.
|
|
145
|
+
- 196a900: Permission rules can say more than one argument per tool.
|
|
146
|
+
|
|
147
|
+
- **`Tool(name:value)` in deny and ask rules** matches a named top-level parameter, with `*` in the
|
|
148
|
+
value (`Bash(run_in_background:true)`, `Agent(model:opus*)`, `github__create_issue(repo:acme/*)`).
|
|
149
|
+
It is a parameter rule only when `name` is one of the tool's parameters, so
|
|
150
|
+
`WebFetch(https://…)` keeps its meaning. A parameter the call omits never matches, and a
|
|
151
|
+
non-scalar value is unevaluable, so the call asks. Allow rules may not use the form.
|
|
152
|
+
- **A rule on the primary field** (`Bash(command:rm *)`) is reported at startup and asks on every
|
|
153
|
+
call, instead of being silently ignored.
|
|
154
|
+
- **Tool-name globs.** Deny and ask rules may glob the tool name (`github__*`). Allow rules may do so
|
|
155
|
+
only after a literal `<server>__` prefix; an unanchored allow glob is refused at construction.
|
|
156
|
+
- **A bare-name deny removes the tool from the model's context.** `Tool`, `Tool(*)` or a name glob
|
|
157
|
+
withholds it from the offered set and the deferred-tool catalogue, live, instead of offering it
|
|
158
|
+
and refusing every call. `IAgentConfig.isToolVisible` is the new seam.
|
|
159
|
+
- **MCP canonical names keep the whole `<server>__` prefix when truncated**, so a server glob still
|
|
160
|
+
names every tool of that server.
|
|
161
|
+
- f336838: One permission evaluation order for every caller. The interactive session, background tasks and
|
|
162
|
+
subagents used to run two different resolvers, so the same call could be decided differently
|
|
163
|
+
depending on who made it. They now share `evaluatePermission`, and a background policy only adds a
|
|
164
|
+
ceiling, an ask-everything flag and the task's own lists to it:
|
|
165
|
+
|
|
166
|
+
deny → caller ceiling → unevaluable deny (ask) → never-auto-approve set (ask) → ask-everything →
|
|
167
|
+
bypassPermissions → allow → mode.
|
|
168
|
+
|
|
169
|
+
- **`ask` rules.** `permissions.ask` patterns always ask, in every mode including
|
|
170
|
+
`bypassPermissions`. They are matched per command like a deny rule, and are validated at
|
|
171
|
+
construction alongside `allow` and `deny`.
|
|
172
|
+
- **Never auto-approved, bypass included:** removing a critical path with `rm`/`rmdir` (the root, a
|
|
173
|
+
top-level directory, home, the working directory or a parent), and a modify-class write into
|
|
174
|
+
`.git`, the configured project-state directory, `.claude`, `.agents`, `.mcp.json`, `.gitconfig`, `.npmrc` or a shell rc file.
|
|
175
|
+
Files inside an isolated worktree (`the configured project-state directory/worktrees/<name>/…`) are ordinary files. With no
|
|
176
|
+
approver attached, an ask is a denial.
|
|
177
|
+
- **A ceiling is checked before bypass and before any ask.** A subagent's `inherit-allowlist` ceiling
|
|
178
|
+
is now the parent's *effective* rules, read live at spawn: settings, preset lists and command
|
|
179
|
+
auto-allows. It used to be the raw settings file. An unevaluable deny under a policy now asks,
|
|
180
|
+
like everywhere else, where it used to deny outright; with no approver it is still a denial.
|
|
181
|
+
- **Settings layers union `permissions.allow`**, as they already did `deny`. A checked-in project
|
|
182
|
+
file no longer silently discards the user's allow list.
|
|
183
|
+
- **Print mode, `createQuery()` and headless sessions default to `default` mode**, not
|
|
184
|
+
`bypassPermissions`. They have no approver, so a call that would ask is denied. Pass
|
|
185
|
+
`--permission-mode` / `permissionMode: 'bypassPermissions'` explicitly for unattended runs.
|
|
186
|
+
|
|
187
|
+
**Breaking:**
|
|
188
|
+
- `@robota-sdk/agent-core` removes `resolvePermissionByPolicy` and `TPermissionPolicyDecision` in
|
|
189
|
+
favour of `projectPermissionPolicy` plus `evaluatePermission`'s new `context` argument.
|
|
190
|
+
- `@robota-sdk/agent-framework` changes the settings merge rule for `permissions.allow`, and the
|
|
191
|
+
default permission mode of `createQuery()` and headless sessions.
|
|
192
|
+
- 34e50f0: A new permission mode, `auto`, lets a model classifier approve or block what would otherwise prompt.
|
|
193
|
+
|
|
194
|
+
- **What it decides:**
|
|
195
|
+
- Reads and in-workspace edits run as in `acceptEdits`.
|
|
196
|
+
- Commands and other calls the mode leaves open go to the classifier, a side call to the
|
|
197
|
+
session's own model. It sees the call, the working directory and the git remotes, never the
|
|
198
|
+
conversation.
|
|
199
|
+
- A block reaches the model with its reason, so it can take another route.
|
|
200
|
+
- **What still reaches a person, or is refused:**
|
|
201
|
+
- Deny rules and background ceilings apply first.
|
|
202
|
+
- `ask` rules, critical removals and protected paths ask a person.
|
|
203
|
+
- After 3 refusals in a row (blocks, or no usable verdict), or 20 blocks in the session, the
|
|
204
|
+
mode asks a person until one approves.
|
|
205
|
+
With no one to ask, the call is denied.
|
|
206
|
+
- **Allow rules:** in `auto` mode, allow rules that approve any command are set aside while the
|
|
207
|
+
mode is on. Examples are `Bash(*)`, an interpreter (`Bash(python *)`), a package runner
|
|
208
|
+
(`Bash(npm run *)`, `Bash(pnpm exec *)`), `Agent`, `ExecuteCommand` or `Computer`. Narrow
|
|
209
|
+
rules still apply.
|
|
210
|
+
- **Retry:** `/permissions` lists classifier blocks. `/permissions retry <n>` lets that exact call
|
|
211
|
+
run once, unjudged, when the model tries it again.
|
|
212
|
+
- **Turning it on and off:**
|
|
213
|
+
- `--permission-mode auto`, `/mode auto` or `/permissions auto`.
|
|
214
|
+
- An organization turns it off with `disableAutoMode` in the org policy.
|
|
215
|
+
- **New contracts:**
|
|
216
|
+
- `TPermissionMode` gains `'auto'`.
|
|
217
|
+
- `allowRulesForAutoMode` and `isBroadExecutionAllowRule`.
|
|
218
|
+
- `IPermissionClassifier` and `AutoModeGate`.
|
|
219
|
+
- The `permissionClassifier` session option.
|
|
220
|
+
- `Session.retryPermissionDenial`, and `retryDenial` on the permission-mode adapter.
|
|
221
|
+
- The `'classifier'` denial reason.
|
|
222
|
+
- `createModelPermissionClassifier`.
|
|
223
|
+
- The `disableAutoMode` option on `createSession` and `IOrgPolicy`.
|
|
224
|
+
- 722e88a: Shell commands can run in an OS-level sandbox: bubblewrap on Linux and WSL2, Seatbelt on macOS.
|
|
225
|
+
|
|
226
|
+
- **Confinement:** covers the command and every process it starts.
|
|
227
|
+
- Writes are limited to the working directory, the temporary directories and
|
|
228
|
+
`sandbox.filesystem.allowWrite`.
|
|
229
|
+
- Agent, git-hook, MCP and shell configuration inside the workspace stays read-only.
|
|
230
|
+
- `sandbox.filesystem.denyRead` hides paths from the command.
|
|
231
|
+
- The network is on or off (`sandbox.network.enabled`).
|
|
232
|
+
- **Modes:** `/sandbox` switches between `auto-allow`, `regular` and `off` for the next command and
|
|
233
|
+
saves the choice.
|
|
234
|
+
- In `auto-allow` (`sandbox.autoAllowBashIfSandboxed`), a confined command runs without a prompt
|
|
235
|
+
in `default` and `acceptEdits`.
|
|
236
|
+
- Deny rules, ask rules, critical removals and plan mode still apply first.
|
|
237
|
+
- **Exclusions:** `sandbox.excludedCommands` run unconfined, through the ordinary permission path.
|
|
238
|
+
- **When the sandbox cannot run:** a missing or unusable backend is reported at startup, in
|
|
239
|
+
`robota doctor` and in `/sandbox`, and commands then run unconfined.
|
|
240
|
+
`sandbox.failIfUnavailable` refuses to start instead.
|
|
241
|
+
- **New contracts:**
|
|
242
|
+
- `OsSandboxClient`, `detectOsSandbox`, `bubblewrapArguments`, `seatbeltProfile`.
|
|
243
|
+
- `ISandboxClient.wrapCommand` and `autoApproves`.
|
|
244
|
+
- `IPermissionEvaluationContext.sandboxAutoApproved`.
|
|
245
|
+
- The `commandSandbox` session option.
|
|
246
|
+
- The `sandbox` settings key and the `sandbox` command host adapter.
|
|
247
|
+
- a5cc36e: `/permissions` shows the rules the session enforces and the calls it refused.
|
|
248
|
+
|
|
249
|
+
- **Rules by source:** each allow, deny and ask rule the gate reads is listed under the settings file
|
|
250
|
+
that declares it. Rules added by a CLI flag, a preset or a command are listed under "this session".
|
|
251
|
+
A rule a settings file declares but the session does not enforce is not shown.
|
|
252
|
+
- **Recent denials:** the latest refused calls, most recent first, with the reason: a rule or the
|
|
253
|
+
mode, the user declining, or no one available to approve.
|
|
254
|
+
- New contracts: `Session.getRecentPermissionDenials()`, `PermissionEnforcer.getRecentDenials()`,
|
|
255
|
+
`IPermissionDenial`, the `permissionRules` host adapter (`createSettingsPermissionRulesAdapter`),
|
|
256
|
+
and `getPermissionRules` / `listRecentDenials` on `ICommandPermissionModeAdapter`. Settings
|
|
257
|
+
provenance now covers `permissions.ask`.
|
|
258
|
+
- d23c848: Built-in read-only shell commands run without a prompt.
|
|
259
|
+
|
|
260
|
+
A Bash call is decided like a read, and runs without a prompt in every mode (`plan` included), when
|
|
261
|
+
every command in it comes from the fixed read-only set (`ls`, `cat`, `grep`, `find` without actions,
|
|
262
|
+
`git status`/`log`/`diff`/`show` and similar) and it also:
|
|
263
|
+
|
|
264
|
+
- stays inside the workspace: every path operand resolves inside once symlinks are followed, as
|
|
265
|
+
`Read` requires;
|
|
266
|
+
- uses only printable ASCII syntax that bash, zsh, fish and PowerShell all read the same way.
|
|
267
|
+
|
|
268
|
+
Deny and ask rules still apply first. A command that writes through a redirect, expands or
|
|
269
|
+
substitutes anything, or runs git outside the session's repository takes the ordinary path.
|
|
270
|
+
|
|
271
|
+
New exports: `isReadOnlyCommandLine` and `TResolveInWorkspace`. `IPermissionEvaluationContext`
|
|
272
|
+
gains `resolveInWorkspace`, which `PermissionEnforcer` supplies.
|
|
273
|
+
- 3038eb7: A message from another session is instant messaging: text from an untrusted third party that
|
|
274
|
+
carries no authority. What the model does with it is decided by the session's ordinary permissions —
|
|
275
|
+
rules, permission mode and remembered consent — exactly like the session's own work. The per-origin
|
|
276
|
+
peer policy is gone.
|
|
277
|
+
|
|
278
|
+
**BREAKING**
|
|
279
|
+
|
|
280
|
+
- `agent-core`: `IPermissionEvaluationContext.peerTurn` is now a boolean. It decides only whether a
|
|
281
|
+
`repliesToPeer` tool exists; every other call in a peer turn is decided as in any turn. Removed:
|
|
282
|
+
`isToolAvailableInPeerTurn`, `isSecretPath`, `IPeerTurnAuthority`, `TPeerReach` (now exported by
|
|
283
|
+
`agent-interface-session-mobility`) and `IToolPermissionProfile.workspacePaths`.
|
|
284
|
+
- `agent-tools`: `Read` and `Glob` no longer declare `workspacePaths`.
|
|
285
|
+
- `agent-session`: `ISessionRunOptions.peerReach` is replaced by `peerTurn?: boolean`, and
|
|
286
|
+
`ISessionOptions.allowPeerChanges` is removed. An ask in a peer turn is answered like any other:
|
|
287
|
+
a consent the operator remembered answers it, and an "always allow" given there is remembered.
|
|
288
|
+
- `agent-interface-session`: `IPeerTurnContext` no longer has `reach`; it carries only the reply
|
|
289
|
+
route.
|
|
290
|
+
- `agent-interface-session-mobility`: `peerReachOf` is removed; `TPeerReach` moves here. A delegated
|
|
291
|
+
turn carries no reach.
|
|
292
|
+
- `agent-framework`: the `peers.allowChanges` setting is removed (an existing value is ignored). A
|
|
293
|
+
peer turn is offered the ordinary tools, plus `peer_reply`.
|
|
294
|
+
|
|
295
|
+
**Changes**
|
|
296
|
+
|
|
297
|
+
- `agent-framework`: the per-turn statement tells the model the message is an opinion from an
|
|
298
|
+
untrusted third party, not its owner's instruction, and that it decides for itself whether and how
|
|
299
|
+
to act. A message still expands no `@path` and attaches no context reference, and its requests
|
|
300
|
+
still carry no provider-hosted tool, since no permission step can decide one. The session takes
|
|
301
|
+
at most 6 messages a minute and 30 an hour from each sender for a turn; a message over the limit
|
|
302
|
+
is refused with a reason the sender receives. A prompt answer given in the name of a `peer:` or `external:`
|
|
303
|
+
driver is ignored. External-event turns keep their tool-less baseline.
|
|
304
|
+
- `agent-cli`: incoming peer turns carry only their reply route.
|
|
305
|
+
- 02b7452: A reply to a peer session is decided by the permission system like any call that sends something
|
|
306
|
+
off this machine, and it goes only to the sender that was admitted.
|
|
307
|
+
|
|
308
|
+
- `agent-core` — a tool declaring `repliesToPeer` is still refused outside a peer turn; inside one it
|
|
309
|
+
is decided by the ordinary steps: deny, ask and allow rules, then the mode. It declares no risk
|
|
310
|
+
class, so it asks by default, is refused in plan mode and proceeds under bypass. `IPeerTurnAuthority`
|
|
311
|
+
no longer has `toolUsed`.
|
|
312
|
+
- `agent-session` — an "always allow" answer to the reply is remembered and answers later replies,
|
|
313
|
+
as for any tool; other asks in a peer turn still need a fresh approval each time.
|
|
314
|
+
- `agent-framework` — `peer_reply` asks the operator by default, showing the full text and the peer
|
|
315
|
+
it goes to; `permissions.allow`/`deny` rules naming `peer_reply` apply. `PeerMessageIngress`
|
|
316
|
+
refuses a message whose origin names a sender other than the admitted one, or whose admission names
|
|
317
|
+
no sender, and submits the turn with the admitted identity.
|
|
318
|
+
- `agent-cli` — a local peer message is taken as coming from the session it names only when that
|
|
319
|
+
session confirms, at its own socket, that it is sending exactly that message to this receiver.
|
|
320
|
+
The reply target, the driver id and the operator's notices come from the confirmed sender. A
|
|
321
|
+
session on an earlier version cannot confirm, so its messages are refused; both sessions need this
|
|
322
|
+
version to message each other.
|
|
323
|
+
- e689c8e: Follow-ups to connection approval and `/handoff`.
|
|
324
|
+
|
|
325
|
+
- `agent-transport-webrtc-web` — the browser remote client says `Waiting for the host to approve this connection…`
|
|
326
|
+
(`awaiting-approval`) after pairing, and `Connected` only once the host's session answers. A host that closes the
|
|
327
|
+
channel instead is shown as `refused` and is not retried, since a retry would only ask the operator again; a
|
|
328
|
+
first connection lost before the host answered is `failed`. Reconnect attempts count until the host admits a
|
|
329
|
+
connection, so a link that keeps dropping while approval is pending gives up instead of asking again and again.
|
|
330
|
+
- `agent-core`, `agent-session`, `agent-framework` — a turn that did not come from the operator stores its
|
|
331
|
+
`turnSource` (`peer`, `external`, `agent-wakeup`) beside `driverId` on the user message and in the display
|
|
332
|
+
history (`IRunOptions.turnSource`), so a session handed off keeps where each turn came from.
|
|
333
|
+
- `agent-cli` — a `/handoff` resent after a lost confirmation reports what stays behind as it is now, not as it was
|
|
334
|
+
at the first attempt, and after refusing to resend a session that changed, the hand-off status shows that refusal
|
|
335
|
+
instead of the earlier lost confirmation.
|
|
336
|
+
- c7f9203: Connected sessions can send each other files.
|
|
337
|
+
|
|
338
|
+
- `/peers send-file <session-id> <path>` sends a copy of any file the operator can read to another
|
|
339
|
+
live session on this host.
|
|
340
|
+
- The model sends a file only through the `peer_send_file` tool. Every call asks the user, showing
|
|
341
|
+
the path, size, hash and destination; no permission mode, rule or remembered consent answers it.
|
|
342
|
+
The tool reaches only files inside the workspace whose path does not look like it holds secrets
|
|
343
|
+
(`.env*`, `~/.ssh`, keys and credentials), and it does not exist in a turn a peer's message started.
|
|
344
|
+
- The receiving operator approves every file. A received file is kept as an inert copy (mode 0600)
|
|
345
|
+
under `the configured user data directory<sender>/`. It is never run and never placed in the model's context.
|
|
346
|
+
The conversation is told only its name, size and sha256. A name that leaves that directory is
|
|
347
|
+
refused, a symbolic link is never written through, and nothing is overwritten.
|
|
348
|
+
- Transfers travel on a channel of their own (a separate connection on this host, a separate data
|
|
349
|
+
channel between devices), in chunks the receiver paces, up to 32 MiB, and are kept only when the
|
|
350
|
+
whole content matches the offered sha256. A transfer that ends early is discarded; there is no
|
|
351
|
+
resume.
|
|
352
|
+
|
|
353
|
+
**API**
|
|
354
|
+
|
|
355
|
+
- `agent-interface-session-mobility`: the `file` capability, which asks the operator for every
|
|
356
|
+
request; `ConnectionAuthority.authorizeFile`; `IFileOffer` and `IFileFrameChannel`.
|
|
357
|
+
- `agent-transport/node`: `sendFileOverChannel` and `receiveFileOverChannel`, the carrier over any
|
|
358
|
+
`IFileFrameChannel`; `DEFAULT_MAX_FILE_BYTES`.
|
|
359
|
+
- `agent-transport-webrtc`: `IDeviceMeshLink.openFileChannel` and `onFileChannel`.
|
|
360
|
+
- `agent-remote-pairing`: `file` joins `DEVICE_CAPABILITIES`. A device certificate that names it is
|
|
361
|
+
refused as malformed by an earlier version.
|
|
362
|
+
- `agent-core`: `IToolPermissionProfile.notInPeerTurn` withholds a tool from a turn a peer's message
|
|
363
|
+
started.
|
|
364
|
+
- `agent-framework`: `ICommandLocalPeersAdapter.prepareFile`.
|
|
365
|
+
- 18b52cc: Built-in commands are offered to the model deliberately, each described for the model, and never
|
|
366
|
+
with a trust, credential or permission-widening action.
|
|
367
|
+
|
|
368
|
+
- `agent-interface-command` — `ICommand` gains `modelDescription?` (what the model is told, beside the
|
|
369
|
+
short `/help` line), and `modelInvocable` on a subcommand entry now narrows what the model may run.
|
|
370
|
+
- `agent-framework` — `ISystemCommand` gains `modelDescription?` and `modelRequiresPermission?`.
|
|
371
|
+
Once any subcommand of a model-invocable command declares `modelInvocable`, the model may run only
|
|
372
|
+
the bare command and the subcommands declared `true`; everything else, including an alias or an
|
|
373
|
+
undeclared subcommand, is refused before the command runs. The model-facing descriptor lists only
|
|
374
|
+
that subset. A model-requested monitor's command is now decided by the shell tool's gate (its
|
|
375
|
+
Bash/Shell rules, the mode and the prompt) and a refusal rejects with the new
|
|
376
|
+
`MonitorCommandRefusedError`. The `scriptedSession` test harness accepts `permissions` patterns.
|
|
377
|
+
- `agent-session` — `Session.checkToolPermission(toolName, toolArgs)` decides an action that has a
|
|
378
|
+
tool's effect by another route: that tool's PreToolUse hooks, then the gate's rules, mode,
|
|
379
|
+
remembered consent and prompt — never the command sandbox's auto-approval, since the action does
|
|
380
|
+
not run inside the sandbox.
|
|
381
|
+
- `agent-framework` also: `ICommandMCPActivationAdapter.userActionSurface?` tells `/mcp` whether the
|
|
382
|
+
user can type a session command, so the model's status names the terminal sign-in otherwise.
|
|
383
|
+
- `agent-command` — `/context` (bare and `list`), `/cost` (the report, not `budget`) and `/mcp`
|
|
384
|
+
(`status` only, without a prompt) are now model-invocable; `/memory approve` and `/memory reject`
|
|
385
|
+
are now user-only; the model's `/monitor` is decided by the shell gate rather than by consent to
|
|
386
|
+
the command's name. Every model-invocable command carries a model-facing description. The model's
|
|
387
|
+
`/mcp status` shows only safe names, states and the command to suggest, and a caller that does not
|
|
388
|
+
identify itself gets that view. `/context list` accounts only for turns still in context. New
|
|
389
|
+
`mcpUserActionNotice`, `mcpUserActionCommand` and `mcpUnavailableServersNotice` build the fixed
|
|
390
|
+
notices.
|
|
391
|
+
- `agent-command-workflows` — `/workflows` carries a model-facing description.
|
|
392
|
+
- `agent-mcp` — `createDiscoveredTool` accepts `authFailureNotice`: a call the server refuses for
|
|
393
|
+
authentication returns that host text instead of the generic failure.
|
|
394
|
+
- `agent-cli` — an MCP server that did not start because the user must approve it, trust the
|
|
395
|
+
workspace or sign in is named to the model at the start of an interactive session with the
|
|
396
|
+
command to suggest, and a
|
|
397
|
+
signed-in OAuth server that refuses a call tells the model to suggest `/mcp login <server>` — or, in print and serve runs, the terminal
|
|
398
|
+
`robota mcp login <server>`.
|
|
399
|
+
|
|
400
|
+
A command whose bare form is a complete action declares `runsBare`, so choosing `/cost` or `/mcp`
|
|
401
|
+
from the autocomplete menu still runs it even though they now declare subcommands.
|
|
402
|
+
- 9843fe6: Sign in to a remote MCP server from inside a session, and use its tools without restarting.
|
|
403
|
+
|
|
404
|
+
- **`/mcp login <server> [--no-browser]`** runs the same per-server OAuth sign-in as
|
|
405
|
+
`robota mcp login` (discovery checks, PKCE, `state`, RFC 9207 `iss`, RFC 8707 resource, the
|
|
406
|
+
loopback listener, the lock-guarded store). It opens the browser through the argv opener; with
|
|
407
|
+
`--no-browser`, or when no browser can be opened, it shows the authorization URL and asks for the
|
|
408
|
+
redirect URL in the session's own prompt (masked), held to the same rules as a pasted redirect in
|
|
409
|
+
the terminal. A failed, refused, timed-out or cancelled sign-in changes nothing and is reported by
|
|
410
|
+
a fixed reason only. `/mcp login <server> --client-secret` is refused: a secret is never typed into
|
|
411
|
+
a session, and `robota mcp login <server> --client-secret` is named instead (also after a failed
|
|
412
|
+
token exchange for a pre-registered client). `/mcp` stays user-only (`modelInvocable: false`).
|
|
413
|
+
- **Connected in the same session:** after a sign-in, a server that could not connect for want of
|
|
414
|
+
one goes through the normal admission (approval, fingerprint, trust) and connects, and its tools
|
|
415
|
+
are offered from the next message; a server that was already connected is admitted again and
|
|
416
|
+
reconnects, and its authenticator drops what it held and reads the new credential. A tool left out
|
|
417
|
+
because the session already has its name is reported, and only tools actually added get
|
|
418
|
+
provenance. In browser mode the authorization URL is shown in the session prompt before the
|
|
419
|
+
browser opens, where the user can also choose to paste the redirect instead or cancel.
|
|
420
|
+
- **`runMCPOAuthLogin`** takes `readRedirectWhenBrowserFails`: when `openBrowser` rejects, the
|
|
421
|
+
loopback listener stops and the pasted redirect is read instead, for the same redirect URI. The
|
|
422
|
+
loopback listener's time limit now runs from its first `wait()` — once the authorization page is
|
|
423
|
+
handed over — so time spent at a prompt before the browser opens does not count against it.
|
|
424
|
+
- **`Session.addTools`** (and `ICommandSessionTools.addTools`) offers tools that became usable
|
|
425
|
+
mid-session, through the same permission gate and — via `ISessionOptions.wrapAddedTools`, which
|
|
426
|
+
`createSession` sets — the same edit-checkpoint and reversible-execution wraps as the assembled
|
|
427
|
+
tools. A name the session already has is left out, never replaced. Calls are serialized, and tools
|
|
428
|
+
added while a turn runs are applied when the next turn starts, so a turn's rounds all see one
|
|
429
|
+
tool list and the prompt cache misses once, at that boundary.
|
|
430
|
+
- **Breaking (`agent-framework`, major):** `addTools` is a new required member of the
|
|
431
|
+
`ICommandSessionTools` role port (and so of `ICommandSessionRuntime`). A host that implements the
|
|
432
|
+
port itself must add it; one that passes an `agent-session` `Session` already has it.
|
|
433
|
+
- **`ICommandMCPActivationAdapter.oauthLogin`** (`ICommandMCPOAuthLoginRequest`,
|
|
434
|
+
`ICommandMCPOAuthLoginResult`, `ICommandMCPOAuthRedirectPrompt`) is the port behind it.
|
|
435
|
+
- The sign-in notice and `/mcp status` now suggest `/mcp login <server>` in a session and
|
|
436
|
+
`robota mcp login <server>` in a terminal; the server's name is shown only when it is safe to paste
|
|
437
|
+
into any shell, otherwise `<server>`.
|
|
438
|
+
- 9f46375: A session can save a pending tool approval and answer it after it is reopened.
|
|
439
|
+
`Session.runRecoverable()` runs a prompt with a host-owned execution journal from
|
|
440
|
+
`@robota-sdk/agent-core`. When a tool call needs approval, it returns `waiting` with the saved
|
|
441
|
+
request (the exact action and its arguments) instead of asking a live approver.
|
|
442
|
+
`Session.resumeRecoverable()` takes the answer and continues that execution without adding another
|
|
443
|
+
user message; a session recreated with the same session ID, workspace, provider and model can do the
|
|
444
|
+
same. Ordinary `run()` keeps asking for approval live.
|
|
445
|
+
|
|
446
|
+
An approval covers only the action and arguments it was given; it never grants session-wide consent.
|
|
447
|
+
Current permissions, hooks and task restrictions are still checked when the tool runs, and a saved
|
|
448
|
+
denial holds even if the policy has since become more permissive. Reusing a response ID with
|
|
449
|
+
different content is refused.
|
|
450
|
+
|
|
451
|
+
While an execution waits, the session refuses new prompts. `Session.getPendingExecution()` returns
|
|
452
|
+
its `executionId` and saved requests, also after a cancellation that ended the turn once the wait
|
|
453
|
+
was saved. A resume refused before it continues, such as a stale response or a storage failure,
|
|
454
|
+
leaves the execution pending for another attempt; once it has continued, a later failure ends it
|
|
455
|
+
like an ordinary turn. `Session.abandonPendingExecution(executionId)` gives it up so the session
|
|
456
|
+
accepts new input: nothing runs and nothing is written to the journal, its open tool calls are
|
|
457
|
+
closed as failed in history, and that execution can no longer be resumed. The same journal also
|
|
458
|
+
records compaction's model call before history changes, and a journal write failure stays a failure
|
|
459
|
+
even when the turn is being cancelled.
|
|
460
|
+
- 94b2c87: A PreToolUse `command` hook can now steer a tool call, not only refuse it. The permission gate
|
|
461
|
+
applies the winning `hookSpecificOutput.permissionDecision`:
|
|
462
|
+
|
|
463
|
+
- `allow` skips the prompt a person would otherwise get. Deny rules, refusals from the mode, the
|
|
464
|
+
`auto` mode classifier and asks that must reach a person (an `ask` rule, a protected path, a
|
|
465
|
+
policy that asks about everything) still apply.
|
|
466
|
+
- `ask` sends the call to a person even when the mode or a remembered consent would run it. The
|
|
467
|
+
answer is not remembered, and with no one to ask the call is refused.
|
|
468
|
+
- `defer` leaves the call to the normal flow, as before.
|
|
469
|
+
|
|
470
|
+
Before this change these decisions were read and then ignored.
|
|
471
|
+
|
|
472
|
+
`updatedInput` is still not applied: the call runs the input it was made with, and an `allow` sent
|
|
473
|
+
with an `updatedInput` is not applied either, since it approved another input.
|
|
474
|
+
|
|
475
|
+
`runHooks` (agent-core) now reads a decision and an `updatedInput` only from `command` hooks: a
|
|
476
|
+
`prompt` or `agent` hook answers from a model that reads the tool input it judges, so it can only
|
|
477
|
+
refuse. With several hooks, `ask` now outranks `defer` (`deny` > `ask` > `defer` > `allow`), and the
|
|
478
|
+
reported `updatedInput` is the one sent with the winning decision.
|
|
479
|
+
- 04719a9: **A permission request names the tool call it is about.**
|
|
480
|
+
|
|
481
|
+
`IPermissionRequestEvent` gains an optional `executionId`: the same id the call's `tool_start` and
|
|
482
|
+
`tool_end` carry, so a surface can hold one row from the request to the call's end. The session's
|
|
483
|
+
permission path carries the call's id from the tool wrapper to the prompt (`IPermissionAskContext.executionId`)
|
|
484
|
+
and the framework's prompt registry stamps it on the event, so it reaches every client that receives
|
|
485
|
+
the event, the WebSocket and HTTP `permission_request` frames included. It is present whenever the
|
|
486
|
+
session reports that call (its own, or an in-process background agent's) and absent for a
|
|
487
|
+
child-process agent's forwarded request.
|
|
488
|
+
|
|
489
|
+
The ACP agent transport keys its permission rows by that id: the request's `toolCallId` is the
|
|
490
|
+
call's execution id, its start and end address the same row, a denial the session decides on its
|
|
491
|
+
own while the editor's dialog is open closes that one row, and two same-named calls each follow
|
|
492
|
+
their own row whatever order the editor answers in. A request that names no call gets a row that
|
|
493
|
+
closes with the answer, and is closed at the end of the turn if no answer came. The name-ordered
|
|
494
|
+
matching is gone.
|
|
495
|
+
- 04a6de4: **Reasoning text a provider streams now reaches the editor as an ACP `agent_thought_chunk`, through a new `thinking_delta` session event.**
|
|
496
|
+
|
|
497
|
+
`IChatOptions.onThinkingDelta`, `IRunOptions.onThinkingDelta` and `IResumeExecutionOptions.onThinkingDelta`
|
|
498
|
+
(typed `TThinkingDeltaCallback`) carry reasoning text from a provider through the run, and the session
|
|
499
|
+
options (`ISessionOptions.onThinkingDelta`, `ICreateSessionOptions.onThinkingDelta`) hand it to the
|
|
500
|
+
session's owner. `InteractiveSession` emits it as `thinking_delta(delta)`, beside `text_delta`. The
|
|
501
|
+
text is never part of the reply: it is not appended to the conversation, not streamed through
|
|
502
|
+
`onTextDelta`, and the core adds no record of its own for it (no text-delta log entry, no
|
|
503
|
+
`provider_stream_raw_delta`); a provider's own native payload capture and response metadata keep
|
|
504
|
+
what the provider sent, as before. A reasoning delta counts as provider activity for the idle
|
|
505
|
+
timeout whether or not anyone listens to it, so a model that is only thinking is not cut off. The
|
|
506
|
+
existing `thinking(isThinking)` event is unchanged and remains the busy indicator. Where a provider
|
|
507
|
+
sends no reasoning text, nothing is emitted and nothing new is switched on.
|
|
508
|
+
|
|
509
|
+
`@robota-sdk/agent-interface-session` is a major change because a new session event name makes an
|
|
510
|
+
exhaustive map over the event names stop compiling until it classifies the event.
|
|
511
|
+
|
|
512
|
+
`@robota-sdk/agent-transport-acp` sends each non-empty `thinking_delta` to the editor as an
|
|
513
|
+
`agent_thought_chunk`, in order with the message chunks, so `robota acp` shows the model's reasoning
|
|
514
|
+
in the editor. A turn that streamed only thoughts still shows its result text, because only reply
|
|
515
|
+
text marks a turn as streamed. The WebSocket wire, the HTTP stream, headless stream-json, `query()`
|
|
516
|
+
and the terminal UI do not carry the new event in this change (`@robota-sdk/agent-transport` and
|
|
517
|
+
`@robota-sdk/agent-ui-terminal` classify it as non-surface). A forked skill and an in-process subagent
|
|
518
|
+
pass their reply text up but not their reasoning.
|
|
519
|
+
|
|
520
|
+
Two providers already receive reasoning text and now report it. `@robota-sdk/agent-provider-openai`
|
|
521
|
+
passes `response.reasoning_summary_text.delta` events of a streamed Responses call to
|
|
522
|
+
`onThinkingDelta`; the API sends them only when the provider profile asks for a reasoning summary, and
|
|
523
|
+
no request option changed; a summary streamed in several parts, and the summary of a later reasoning
|
|
524
|
+
item, each start on a new paragraph (`\n\n`) so the parts do not run together.
|
|
525
|
+
`@robota-sdk/agent-provider-openai-compatible` reports the text of Gemma's thought channel, which the
|
|
526
|
+
provider already removed from the reply, as it streams across chunk boundaries, with each later
|
|
527
|
+
channel of one reply on a new paragraph; the reply text is unchanged. No other provider reports
|
|
528
|
+
reasoning, and Anthropic thinking is not enabled. A model fallback chain still moves to the next model when only reasoning had streamed.
|
|
529
|
+
|
|
530
|
+
The scripted test provider (`@robota-sdk/agent-core/testing`) accepts an optional `thinking` string on
|
|
531
|
+
a turn and delivers it through `onThinkingDelta` before the turn's text; the scripted session harness
|
|
532
|
+
collects `thinking_delta` and now keeps a scripted tool-call turn's `usage`, which it used to drop.
|
|
533
|
+
- 0ba247b: A tool result can now carry a view its source declared (an MCP app), and a surface can invoke a
|
|
534
|
+
runtime tool through the session's permission gate with a prompt. This is the contract and wiring
|
|
535
|
+
layer: nothing produces a view yet, and the GUI does not yet host one.
|
|
536
|
+
|
|
537
|
+
- `agent-core` — `IToolResult.view` carries an `IToolAppView` (source, `ui://` resource, mime type,
|
|
538
|
+
HTML, structured content, callable tools, the call's input). `view` is now a typed key of
|
|
539
|
+
`IToolResult`, so an extension that already used a `view` key with another type must rename it.
|
|
540
|
+
The view is display data: it never enters model context or the size that decides a spill, and a
|
|
541
|
+
result replaced by its reference keeps it.
|
|
542
|
+
- `agent-interface-session` — `invokeRuntimeTool` takes `permissionInteraction` (`'interactive'` or
|
|
543
|
+
the default `'deny'`), a `driverId` (a host that attributes prompts names it as the requester) and
|
|
544
|
+
an `executionId` its events carry. A finished model call's `tool_end` names its view with
|
|
545
|
+
`IToolState.appView` (`viewId`, source, resource), and the new
|
|
546
|
+
`ISessionToolAppViews.readToolAppView(viewId)` reads the document. A permission handler may take
|
|
547
|
+
`{ signal }`, and aborting it withdraws the prompt.
|
|
548
|
+
- `agent-session` — a direct call asks a person only when its caller says so, and the handler is
|
|
549
|
+
given the call's own signal; the tool end event carries the view, never in the logged result data.
|
|
550
|
+
- `agent-framework` — the view of a model's call is kept under a `viewId` the session mints, in a
|
|
551
|
+
bounded per-session store (16 views, 4 MiB each, 16 MiB in all, measured on the whole serialized
|
|
552
|
+
view), and read on demand, so the document never rides `tool_end` to every surface, never enters
|
|
553
|
+
the recorded history, and a repeated provider tool-call id cannot overwrite another turn's view.
|
|
554
|
+
A malformed view is dropped. A direct call keeps and names no view, recognised by the id it was
|
|
555
|
+
started with. A submission or agent wakeup that arrives while a direct call holds the session is
|
|
556
|
+
queued behind it, as during a turn, instead of refused. A direct call acting for a driver
|
|
557
|
+
attributes its permission prompt and tool events to that driver, and aborting its signal withdraws
|
|
558
|
+
an open prompt from every surface.
|
|
559
|
+
- `agent-transport` — a driving connection sends `call-app-tool` (answered by `app_tool_result`) and
|
|
560
|
+
`read-app-view` (answered by `app_view`). A call names its view and reaches only the tools that
|
|
561
|
+
view's source declared; a call or read that decodes but cannot be served answers `protocol_error`
|
|
562
|
+
echoing the `requestId`, while an observer's attempt is refused like any non-read. A call still
|
|
563
|
+
waiting on its prompt is cancelled when its connection goes away (for the resume bridge, when the
|
|
564
|
+
bridge is disposed). `maxFrameBytes` bounds an app answer to what a carrier's channel carries;
|
|
565
|
+
the WebRTC carrier uses a history page's worth. `IProtocolSession` now includes the runtime-tool
|
|
566
|
+
and app-view roles.
|
|
567
|
+
- `agent-ui-web` — the two new server messages are recorded as not rendered until the app-view host
|
|
568
|
+
consumes them.
|
|
569
|
+
|
|
570
|
+
An implementation of `IInteractiveSession` or `IProtocolSession` outside this repository must add
|
|
571
|
+
`readToolAppView` (and, for `IProtocolSession`, `listRuntimeTools` and `invokeRuntimeTool`); the
|
|
572
|
+
members are required so a host cannot silently lack them.
|
|
573
|
+
- b078afa: Restore full-fidelity replay for session-log values externalized to content-addressed sidecar files.
|
|
574
|
+
|
|
575
|
+
`agent-session` now exports a bounded, containment- and integrity-checked recursive payload resolver,
|
|
576
|
+
hydrates JSONL logs at their read boundary, and rejects unresolved replay-substrate values during raw
|
|
577
|
+
validation. `agent-provider-replay` reuses that resolver for direct construction and file loading so
|
|
578
|
+
large recorded responses remain aligned with later calls.
|
|
579
|
+
- b078afa: Preserve complete resumable session records when the raw Session writer re-saves them.
|
|
580
|
+
|
|
581
|
+
`IInteractiveSessionStore` is now the canonical persistence port, including its optional file-backed
|
|
582
|
+
record-path capability. `agent-session` consumes the canonical record and store contracts directly and
|
|
583
|
+
keeps its former type names only as compatibility re-exports.
|
|
584
|
+
- 2ebff01: Complete the public session-log event vocabulary for every production writer/reader event and pass
|
|
585
|
+
the session-owned compaction trigger unchanged to PreCompact, PostCompact, logging, and callbacks.
|
|
586
|
+
- 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.
|
|
587
|
+
- 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:
|
|
588
|
+
|
|
589
|
+
- `agent-session`: `ISessionOptions.contextCapacityHint` is forwarded into the ConversationAgent config (`buildConversationAgent`), making the core seam reachable from the consuming layer.
|
|
590
|
+
- `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.
|
|
591
|
+
- `agent-cli`: the default command set registers `/compact`, so end users regain the actionable hint.
|
|
592
|
+
- `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.
|
|
593
|
+
- fcb0da3: PAYLOAD-2153: make external-payload replay stable across Linux, macOS, and Windows.
|
|
594
|
+
|
|
595
|
+
- 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.
|
|
596
|
+
- Route session replay and framework project reads through the shared authority while preserving their existing domain-specific budgets, integrity checks, and error mappings.
|
|
597
|
+
- Expose the canonical safe session-id predicate through the framework facade so CLI exact-session lookup stays within the SDK package boundary.
|
|
598
|
+
- 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.
|
|
599
|
+
- 9814afc: Type-SSOT convergence (TYPE-003; re-audit CONTRACT-002/003/011/012 + RUNTIME-47 + STRUCT-04). Behavior is unchanged — this is a type-level refactor. `ITokenUsage` (agent-core) is confirmed as the usage-triple SSOT: `ISessionUsageTotals` and `IBackgroundTaskUsage` become aliases, and every inline `{ promptTokens; completionTokens; totalTokens }` copy (service/orchestration/executor/remote-client shapes) now references the SSOT (structurally identical → patch). The subagent-job contracts derive from the background-task SSOT — `TSubagentJobStatus = Exclude<TBackgroundTaskStatus, 'paused'>`, mode alias, and a `Pick`-projection `ISubagentJobState` — with a compile-enforced parity test so a drifting hand copy can no longer exist. `@robota-sdk/agent-session` is minor because the public `ISessionRecord` type is now the typed `IInteractiveSessionRecord` alias (previously a relaxed `unknown[]` mirror): runtime behavior of `SessionStore` is identical, but downstream code that assigned loose payloads to the record's fields may need explicit casts at its own trust boundary (the framework store facade's `as unknown as` cast bridge is deleted). agent-session's duplicate `@robota-sdk/agent-core` deps/devDeps declaration is also removed (STRUCT-04).
|
|
600
|
+
|
|
601
|
+
### Patch Changes
|
|
602
|
+
|
|
603
|
+
- 5307f8a: Discriminate `IBackgroundTaskResult` by kind, the same way `TBackgroundTaskRequest` already is:
|
|
604
|
+
`exitCode`/`signalCode` exist only on the `process` member and `usage` only on the `agent` member,
|
|
605
|
+
instead of being optional-and-unreachable on every kind. `IBackgroundTaskResult<K>` narrows to the
|
|
606
|
+
kind-specific member; called with no type argument it is still the full union, which is what
|
|
607
|
+
`IBackgroundTaskState.result` continues to hold (that field stays undiscriminated — a later change).
|
|
608
|
+
`ISubagentJobResult` is now derived as `Omit<IBackgroundTaskResult<'agent'>, 'kind'>` rather than a
|
|
609
|
+
hand-maintained `Omit<IBackgroundTaskResult, 'kind' | 'exitCode' | 'signalCode'>`.
|
|
610
|
+
|
|
611
|
+
`IBackgroundTaskHandle` gains the same kind parameter as `IBackgroundTaskStart`: a runner declared
|
|
612
|
+
for kind `K` resolves its handle's `result` to `IBackgroundTaskResult<K>`, so a caller that starts a
|
|
613
|
+
known-kind runner gets a correctly-narrowed result with no cast, and reading a cross-kind field on it
|
|
614
|
+
is a compile error. Consumers reading `state.result` through the generic (kind-erased) manager or
|
|
615
|
+
task-state path are unaffected in behavior, but a `.exitCode`/`.signalCode`/`.usage` read there must
|
|
616
|
+
now narrow on the result's own `kind` first, since the fallback default keeps `IBackgroundTaskResult`
|
|
617
|
+
as the full union rather than the previously flat, always-present shape.
|
|
618
|
+
|
|
619
|
+
`agent-session`'s session-record decoder now rejects a persisted result carrying a field outside its
|
|
620
|
+
own kind (e.g. an `'agent'` result with `exitCode`) as corrupt, reported at that field's own path —
|
|
621
|
+
the same corruption-reporting style the taskId/kind identity check already uses.
|
|
622
|
+
|
|
623
|
+
**Breaking for `@robota-sdk/agent-interface-execution` and `@robota-sdk/agent-executor`**: code that
|
|
624
|
+
read `exitCode`/`signalCode`/`usage` off an unnarrowed `IBackgroundTaskResult`, or that implemented
|
|
625
|
+
`IBackgroundTaskHandle`/a custom runner without specifying its kind parameter, needs to narrow on
|
|
626
|
+
`result.kind` (or specify the kind parameter) before those fields are visible again.
|
|
627
|
+
- b462ee7: Discriminate `IBackgroundTaskState` by kind, the same way `TBackgroundTaskRequest` and (as of the
|
|
628
|
+
prior change) `TBackgroundTaskResult` already are. Fields only one runner ever produces now live
|
|
629
|
+
only on that kind's member instead of being optional-and-cross-kind-reachable on every kind:
|
|
630
|
+
|
|
631
|
+
- `agent`-only: `agentType`, `isolation`, `resumeSessionId`, `promptPreview`, and the
|
|
632
|
+
worktree-isolation fields (`worktreePath`, `branchName`, `worktreeStatus`, `worktreeNextAction`,
|
|
633
|
+
`worktreeBaseRevision`, `parentWorktreeStatus`).
|
|
634
|
+
- `scheduled`-only: `schedule`, `nextFireAt`.
|
|
635
|
+
- Every kind except `agent`: `commandPreview` (a process command, an MCP tool-invocation summary, or
|
|
636
|
+
a schedule's shell command / wake instruction).
|
|
637
|
+
- `state.result` is now `IBackgroundTaskResult<K>` — correlated with `state.kind`, not the free
|
|
638
|
+
full-result union.
|
|
639
|
+
|
|
640
|
+
`pid`, `logPath`, and `transcriptPath` stay on the shared base rather than becoming agent- or
|
|
641
|
+
process-exclusive: the runner handle SPI already reports them for whichever runner's process
|
|
642
|
+
happens to produce them, and a subagent run via the worktree/child-process runner carries a `pid`
|
|
643
|
+
exactly as a `process`-kind task does. `timeoutReason` also stays base — session restore sets
|
|
644
|
+
`'stale_worker'` on any non-terminal, non-rearmable task regardless of kind, not only agent ones.
|
|
645
|
+
|
|
646
|
+
`IBackgroundTaskState<K>` narrows to the kind-specific member; called with no type argument it is
|
|
647
|
+
still the full union. The session-record decoder (`agent-session`) now rejects a persisted task
|
|
648
|
+
state carrying a field outside its own kind as corrupt, reported at that field's own path, the same
|
|
649
|
+
way it already rejects a `state.result` whose kind disagrees with `state.kind`.
|
|
650
|
+
|
|
651
|
+
**Breaking for `@robota-sdk/agent-interface-execution` and `@robota-sdk/agent-executor`**: code that
|
|
652
|
+
read a kind-specific field (e.g. `state.agentType`, `state.schedule`, `state.commandPreview`) off an
|
|
653
|
+
unnarrowed `IBackgroundTaskState` needs to narrow on `state.kind` first, since the fallback default
|
|
654
|
+
no longer carries every field on every kind. `agent-session`, `agent-framework`, and `agent-command`
|
|
655
|
+
land the corresponding narrowing at every read site the type change touched; no runtime behavior
|
|
656
|
+
changes there beyond the state decoder's new corruption checks.
|
|
657
|
+
- 30e5e50: Reject background runner results whose task ID or kind differs from the task being completed, and report the same mismatch as corruption when decoding a persisted session record.
|
|
658
|
+
- be0e53c: A tool instance shared by several sessions sends each call's span to the session that made the call.
|
|
659
|
+
|
|
660
|
+
- **Before:** each session set its event service on the shared instance, so the span went to whichever session had set it last.
|
|
661
|
+
- **`agent-core`:** `IToolExecutionContext` gains an optional `instanceEventService`. `FunctionTool` emits its span there when a call carries one.
|
|
662
|
+
- **`agent-session`:** the permission wrapper keeps its session's service and passes it with every call.
|
|
663
|
+
- 57280bf: Every published package now declares `"engines": { "node": ">=22.12.0" }`. Before, 27 of the 38
|
|
664
|
+
packages declared no floor (`agent-core`, `agent-tools` and every provider among them),
|
|
665
|
+
`agent-session` and `agent-file-authority` declared `>=20.19.0`, and the other nine declared
|
|
666
|
+
`>=22.0.0`, so a consumer on Node 20 saw at most a warning from a transitive dependency.
|
|
667
|
+
|
|
668
|
+
Why 22.12: `agent-cli` and `agent-ui-terminal` need Node 22 through `ink` 7, and the CommonJS entries
|
|
669
|
+
of `agent-tools` and its dependents, `agent-transport`/`node` and its dependents, and
|
|
670
|
+
`agent-ui-terminal` `require()` ESM-only dependencies (`p-limit`, `jose`, `chalk`), which Node 22
|
|
671
|
+
supports unflagged only from 22.12. `engines` is advisory unless the consumer enables `engine-strict`.
|
|
672
|
+
|
|
673
|
+
No code changes: `tsdown` now reads `node22.12.0` as its build target from the field.
|
|
674
|
+
- 18c0d5c: Every published package now exports `./package.json`, so `require('<package>/package.json')` and
|
|
675
|
+
`import('<package>/package.json', { with: { type: 'json' } })` work instead of failing with
|
|
676
|
+
`ERR_PACKAGE_PATH_NOT_EXPORTED`, and each tarball now ships the package's `CHANGELOG.md`.
|
|
677
|
+
- 10597b2: Apply command-hook PreToolUse input rewrites only after every enforcing hook evaluates the final
|
|
678
|
+
input. Permission checks and tool execution share those arguments; rewrite cycles and replacements
|
|
679
|
+
that a delegated or already-settled action cannot apply are refused.
|
|
680
|
+
- 2c612ba: 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.
|
|
681
|
+
- e706b43: Live telemetry can name its spans with the OpenTelemetry GenAI semantic conventions. Set
|
|
682
|
+
`PRODUCT_TELEMETRY_SEMCONV` to `gen_ai` to export `invoke_agent`, `chat {model}` (client) and
|
|
683
|
+
`execute_tool {tool}` spans with `gen_ai.*` attributes, keeping Robota-only facts as `agent.*`; or to
|
|
684
|
+
`gen_ai/dup` to emit both attribute sets while dashboards move. The default, `agent`, exports exactly
|
|
685
|
+
the spans it did before. An unknown value, or the setting while neither traces nor metrics are exported,
|
|
686
|
+
refuses startup. Console traces under a GenAI mode print the same projected spans.
|
|
687
|
+
|
|
688
|
+
The live prompt trace now carries each tool body's name (the session's tool-body observation includes it
|
|
689
|
+
and the framework's live projection passes a label-safe name through); it reaches spans only under a
|
|
690
|
+
GenAI mode and is never written to session history.
|
|
691
|
+
- 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.**
|
|
692
|
+
|
|
693
|
+
`@robota-sdk/agent-transport-mcp`: `createMcpTransport`, `createMcpHttpHost`,
|
|
694
|
+
`createMcpRemoteHttpHost` and `createAgentMcpServer` take `askClient`. Off by default, every prompt
|
|
695
|
+
is a tool error as before. On, and when the client declared form-mode elicitation, a permission or
|
|
696
|
+
ask prompt raised by that client's own call is put to it: a permission offers allow once or deny
|
|
697
|
+
only, never a remembered grant; an ask becomes a form (choice, multiple choice, free text); a masked
|
|
698
|
+
ask is never sent; deny, decline, cancel, a client error or an answer naming nothing offered keeps
|
|
699
|
+
today's outcome. stdio asks during the call. The HTTP hosts keep no session state, so they ask only
|
|
700
|
+
a client on the 2026-07-28 revision, through input-required rounds whose `requestState` is a
|
|
701
|
+
single-use random token naming one parked call; a 2025-era HTTP client keeps the tool error.
|
|
702
|
+
A call listens for prompts only while its own work drives the session, so a submission waiting
|
|
703
|
+
in the queue never holds another caller's prompt that it cannot answer.
|
|
704
|
+
Breaking for hand-written sessions: `IMcpTransportSession` now also requires `ISessionEvents`
|
|
705
|
+
(`on`/`off`), `ISessionPromptResolution` (`resolvePermission`/`resolveAsk`) and
|
|
706
|
+
`ISessionDriverAttribution` (`getActiveDriverId`), which `InteractiveSession` and the published
|
|
707
|
+
test double already implement.
|
|
708
|
+
|
|
709
|
+
`@robota-sdk/agent-interface-session` / `@robota-sdk/agent-session` / `@robota-sdk/agent-framework`:
|
|
710
|
+
`ISessionRuntimeTools.invokeRuntimeTool` (and `Session.invokeRuntimeTool`) accept
|
|
711
|
+
`options.permissionInteraction` and `options.driverId`. A direct call still denies a decision that
|
|
712
|
+
would need a person, and finds no one to ask a question, unless its caller passes
|
|
713
|
+
`permissionInteraction: 'interactive'`; then it asks the session's permission handler and its
|
|
714
|
+
tools get the session's ask port. `InteractiveSession` makes `driverId` the active driver while
|
|
715
|
+
the direct call runs, so a prompt the call raises carries it as `requesterDriverId`. With
|
|
716
|
+
`askClient` on, an MCP call runs under its own `mcp:<n>` driver id; a submission made that way is
|
|
717
|
+
attributed to it rather than to the owner.
|
|
718
|
+
|
|
719
|
+
`@robota-sdk/agent-transport`: exports `permissionSummaryArgument`, the argument a person recognises
|
|
720
|
+
a tool call by (`filePath` for the built-in file tools), for every transport that puts a permission
|
|
721
|
+
request to a person in its own wire form.
|
|
722
|
+
|
|
723
|
+
`@robota-sdk/agent-cli`: `mcp serve --ask-client` turns this on for whichever carrier the run serves.
|
|
724
|
+
- c207a31: Keep automatic compaction aligned with the active model and pending input, preserve provider usage when estimating new context, and keep summary text out of the response stream. Display live context changes and successful automatic compaction in the shared app interface.
|
|
725
|
+
- 9b4f63b: Allow explicitly borrowed providers to outlive agent destruction and Session shutdown while keeping
|
|
726
|
+
owned providers as the default. Track replaced and removed provider cleanup, expose current provider
|
|
727
|
+
registrations after swapping, and hold Roundtable resource leases until runtime teardown finishes.
|
|
728
|
+
Borrowing does not permit concurrent provider use across leases.
|
|
729
|
+
Serialize manager lifecycle transitions so reinitialization cannot admit providers during pending teardown.
|
|
730
|
+
- 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).
|
|
731
|
+
- 6085cad: CORE-031: a compaction with nothing to summarise no longer replaces the conversation
|
|
732
|
+
|
|
733
|
+
`Session.compact()` guarded on the FULL conversation and then compacted a DIFFERENT array — the same
|
|
734
|
+
history with system messages filtered out. So a conversation consisting only of system messages
|
|
735
|
+
passed the guard, reached the orchestrator empty, took its `return ''` shortcut, and came back as a
|
|
736
|
+
"summary" the caller wrote over the conversation: cleared, and replaced with an empty
|
|
737
|
+
`[Context Summary]` block.
|
|
738
|
+
|
|
739
|
+
The guard now tests the messages that will actually be compacted. Nothing to summarise is a no-op,
|
|
740
|
+
not a failure — the conversation is left exactly as found, no hook fires, no `context_compact` event
|
|
741
|
+
is written, and the provider is not called.
|
|
742
|
+
|
|
743
|
+
`CompactionOrchestrator.compact()` correspondingly throws `CompactionError` on an empty `history`
|
|
744
|
+
rather than returning `''`, which contradicted the contract two lines above it in its own docblock
|
|
745
|
+
("always a non-empty string") and was the value that made the overwrite possible. Whether there is
|
|
746
|
+
anything worth compacting is the caller's judgement, made before it commits to replacing anything.
|
|
747
|
+
|
|
748
|
+
Reachable from the public SDK surface: `Session.injectMessage` is what `--resume` and `--fork` drive
|
|
749
|
+
on every restore.
|
|
750
|
+
- 93d061d: CORE-043: structured output now knows which transport can carry the schema before the first call
|
|
751
|
+
|
|
752
|
+
`run(input, { output })` asked every provider for `responseFormat: { type: 'json_schema' }`. A
|
|
753
|
+
provider whose surface cannot express that accepted the option and dropped it — and the schema was
|
|
754
|
+
stated in words only by the RETRY feedback turn, which runs on attempt two. So against such a
|
|
755
|
+
provider, attempt one carried nothing describing the required shape and could only succeed by luck:
|
|
756
|
+
the advertised three attempts were really two, and the first was spent discovering something the
|
|
757
|
+
capability table already knew.
|
|
758
|
+
|
|
759
|
+
A `(provider, model)` pair now resolves to a mechanism (`response_schema` / `json_object` / `none`)
|
|
760
|
+
and a provenance (`catalog` / `vendor-default` / `undeclared` / `unverified-endpoint`), and the
|
|
761
|
+
request is shaped to match at the one seam that holds both the resolved provider and the outgoing
|
|
762
|
+
messages. When the wire cannot carry the shape, the schema is stated in the prompt on the FIRST
|
|
763
|
+
attempt. Each structured request emits a `structured_output_transport` event reporting what the
|
|
764
|
+
request actually did.
|
|
765
|
+
|
|
766
|
+
- `IAIProvider.endpointIsVendorDefault?()` — a provider configured with a custom `baseURL` reports
|
|
767
|
+
it, so the runtime stops claiming enforcement a gateway may not provide. Separate from
|
|
768
|
+
`capabilityTable?()` on purpose: `@robota-sdk/agent-provider-openai` declares no table (nobody has
|
|
769
|
+
verified one) and must still be able to answer.
|
|
770
|
+
- DeepSeek's capability table declared `json_schema`; DeepSeek guarantees the response PARSES but
|
|
771
|
+
takes no schema parameter. Corrected to `json_object`.
|
|
772
|
+
- A provider that declares nothing is still sent the request unchanged — silence is not a denial.
|
|
773
|
+
- bed26ea: Fix: an in-flight autonomous `goal` is no longer lost on session resume. `fromSessionRecord` was a hand-enumerated field whitelist that omitted `goal` (while the write path persisted it), so the goal silently vanished on load. The read path is now a structural mirror of the write path (`{ ...session }`), so every persisted field — including `goal` — round-trips, and a future field cannot be dropped by omission (ARL-08 / DATA-006). `ISessionRecord` gains an opaque `goal?: unknown` for contract honesty.
|
|
774
|
+
- 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.
|
|
775
|
+
- 07b627f: A local peer can now be answered, and what its turn may do depends on where it runs.
|
|
776
|
+
|
|
777
|
+
- `agent-core`: the permission evaluator takes a peer turn's authority as one more input
|
|
778
|
+
(`IPermissionEvaluationContext.peerTurn`), decided after the deny list and ceiling and before
|
|
779
|
+
bypass and allow rules. A peer on another host uses no tool. A peer on the same host may use an
|
|
780
|
+
inspect-class tool that declares `workspacePaths`, only when every named location resolves inside
|
|
781
|
+
the workspace and is not a credential (`isSecretPath`); write and execute tools are refused unless
|
|
782
|
+
enabled, and then every use asks. A tool declaring `repliesToPeer` exists only in a peer turn and
|
|
783
|
+
asks once the turn used another tool. New: `isToolAvailableInPeerTurn`, `TPeerReach`,
|
|
784
|
+
`IPeerTurnAuthority`. `IRunOptions.withholdHostedTools` leaves a provider's hosted tools out of a
|
|
785
|
+
run's requests (`nativeWebTools` with `false` withholds a hosted tool for one call).
|
|
786
|
+
- `agent-tools`: `Read` and `Glob` declare the arguments that say where they look. `Grep` does not:
|
|
787
|
+
it reads files it was never named, so a peer turn does not get it.
|
|
788
|
+
- `agent-session`: `ISessionRunOptions.peerReach` makes a run a peer turn for the permission policy;
|
|
789
|
+
every ask in it needs a fresh approval, and its requests carry no provider-hosted tool. `ISessionOptions.allowPeerChanges` enables write and execute
|
|
790
|
+
tools for same-host peer turns.
|
|
791
|
+
- `agent-interface-session`: `ISubmitOptions.peer` (`IPeerTurnContext`) carries a peer turn's reach,
|
|
792
|
+
the message it answers and the session a reply goes to.
|
|
793
|
+
- `agent-interface-session-mobility`: `IPeerMessage.inReplyTo` threads a conversation;
|
|
794
|
+
`peerReachOf(admission)` maps admission to a reach.
|
|
795
|
+
- `agent-framework`: a peer turn is offered what its origin allows, and a new `peer_reply` tool
|
|
796
|
+
answers the peer that sent the message, threaded to it. The setting `peers.allowChanges` enables
|
|
797
|
+
write and execute tools for same-host peer turns.
|
|
798
|
+
- `agent-ui-terminal`: a permission prompt in a peer turn names the requesting peer.
|
|
799
|
+
- `agent-cli`: incoming peer turns carry their reach and reply route; a conversation is limited in
|
|
800
|
+
depth and in how often this session answers it, and a reply over a limit is not sent and the
|
|
801
|
+
operator is told.
|
|
802
|
+
- `agent-provider-anthropic`, `agent-provider-openai-compatible` (Qwen): a request whose
|
|
803
|
+
`nativeWebTools` sets a hosted tool to `false` is sent without it.
|
|
804
|
+
- d0de5b2: A peer session's message now reaches the model as a peer's, and a peer turn runs on the external
|
|
805
|
+
baseline.
|
|
806
|
+
|
|
807
|
+
- `agent-core` marks every user message whose driver id starts with `peer:` as
|
|
808
|
+
`<peer_message from="…">…</peer_message>` in the outgoing request — both the round and the forced
|
|
809
|
+
summary — while the stored history keeps the text as sent. Wrapper-shaped text in user and tool
|
|
810
|
+
messages is escaped, and an id that is not a plain identifier is printed as `peer:unverified`.
|
|
811
|
+
New exports: `peerDriverOf`, `printablePeerDriver`.
|
|
812
|
+
- `agent-session`'s conversation transcript (compaction, advisor) labels a peer message with the same
|
|
813
|
+
printable id, so no rendering echoes a sender-chosen id that is not a plain identifier.
|
|
814
|
+
- `agent-framework` runs a `peer` turn like an `external` one — no tools (`toolChoice: 'none'`), no
|
|
815
|
+
`@path` expansion, no context references — and adds a per-turn system statement that the message
|
|
816
|
+
came from another session and carries no authority. A `peer` turn must carry a `peer:` driver id.
|
|
817
|
+
- 7d9cc66: Retain typed resource and audio observations alongside structured tool results through history, durable receipts, recovery, size admission, and session persistence. Preserve opaque resource references without fetching them, project embedded text and explicit unsupported binary/audio diagnostics into provider requests and compaction transcripts, and validate persisted observation shapes on reload.
|
|
818
|
+
- 7d9cc66: Retain registered tool source attribution in observations and preserve ordered outcome receipts independently of conversation summaries.
|
|
819
|
+
- 7911a7d: Preserve pending execution discovery and input admission in atomic Session conversation snapshots.
|
|
820
|
+
Strict synchronous checkpoints now gate waiting, settlement and abandonment acknowledgment; failed saves
|
|
821
|
+
require checkpoint repair without repeating execution. Restore before framework queue and autonomous
|
|
822
|
+
input admission, and refuse unresolved conversation copies. Read v1/v2 session envelopes and write v2;
|
|
823
|
+
older readers preserve v2 as unsupported, so coordinate reader versions when rolling back.
|
|
824
|
+
- ebd40a0: Harden on-disk log permissions against CWE-377 (SEC-003, CodeQL `js/insecure-temporary-file`).
|
|
825
|
+
|
|
826
|
+
Session logs, externalized session payloads, and OpenAI request/response payload logs all carry
|
|
827
|
+
conversation and prompt content, but were created with the process umask (typically `0644`) inside a
|
|
828
|
+
caller-supplied directory that may be shared or world-writable. They are now created owner-only
|
|
829
|
+
(`0600`), and the directories that hold them are created `0700`.
|
|
830
|
+
|
|
831
|
+
This is a permissions change only — file locations, names, formats, and APIs are unchanged. Anything
|
|
832
|
+
that read these logs as a _different_ OS user will no longer be able to; the owning user is
|
|
833
|
+
unaffected.
|
|
834
|
+
- 7d9cc66: Retain mixed tool observations alongside structured results through live session events and history
|
|
835
|
+
display. The shared app renderer previews inline raster images and keeps resource URIs inert, with
|
|
836
|
+
explicit diagnostics for unsupported media instead of silently dropping it.
|
|
837
|
+
- 7d9cc66: Retain registered attribution on refused and interrupted call receipts without replacing the source of actual or recovered settlements, and project undispatched registered calls into live and restored presentation history.
|
|
838
|
+
- Updated dependencies [9c19c50]
|
|
839
|
+
- Updated dependencies [7b6234c]
|
|
840
|
+
- Updated dependencies [5307f8a]
|
|
841
|
+
- Updated dependencies [b462ee7]
|
|
842
|
+
- Updated dependencies [37b4bd7]
|
|
843
|
+
- Updated dependencies [4eea54b]
|
|
844
|
+
- Updated dependencies [1698be4]
|
|
845
|
+
- Updated dependencies [d4189b9]
|
|
846
|
+
- Updated dependencies [9edae52]
|
|
847
|
+
- Updated dependencies [4078a72]
|
|
848
|
+
- Updated dependencies [4c73a0b]
|
|
849
|
+
- Updated dependencies [196a900]
|
|
850
|
+
- Updated dependencies [f336838]
|
|
851
|
+
- Updated dependencies [34e50f0]
|
|
852
|
+
- Updated dependencies [722e88a]
|
|
853
|
+
- Updated dependencies [d23c848]
|
|
854
|
+
- Updated dependencies [3038eb7]
|
|
855
|
+
- Updated dependencies [02b7452]
|
|
856
|
+
- Updated dependencies [e689c8e]
|
|
857
|
+
- Updated dependencies [ec5e477]
|
|
858
|
+
- Updated dependencies [c7f9203]
|
|
859
|
+
- Updated dependencies [7b72344]
|
|
860
|
+
- Updated dependencies [6ae3f28]
|
|
861
|
+
- Updated dependencies [be0e53c]
|
|
862
|
+
- Updated dependencies [57f57f5]
|
|
863
|
+
- Updated dependencies [ba822c1]
|
|
864
|
+
- Updated dependencies [6e6b06b]
|
|
865
|
+
- Updated dependencies [8bd5fac]
|
|
866
|
+
- Updated dependencies [57280bf]
|
|
867
|
+
- Updated dependencies [5033dd9]
|
|
868
|
+
- Updated dependencies [18c0d5c]
|
|
869
|
+
- Updated dependencies [dbd888d]
|
|
870
|
+
- Updated dependencies [1887e54]
|
|
871
|
+
- Updated dependencies [9f46375]
|
|
872
|
+
- Updated dependencies [41cca13]
|
|
873
|
+
- Updated dependencies [ada3841]
|
|
874
|
+
- Updated dependencies [5093a30]
|
|
875
|
+
- Updated dependencies [94b2c87]
|
|
876
|
+
- Updated dependencies [3ab2eca]
|
|
877
|
+
- Updated dependencies [e8d70ac]
|
|
878
|
+
- Updated dependencies [2c612ba]
|
|
879
|
+
- Updated dependencies [66af868]
|
|
880
|
+
- Updated dependencies [60f3965]
|
|
881
|
+
- Updated dependencies [115e104]
|
|
882
|
+
- Updated dependencies [04719a9]
|
|
883
|
+
- Updated dependencies [04a6de4]
|
|
884
|
+
- Updated dependencies [0ba247b]
|
|
885
|
+
- Updated dependencies [6f41c53]
|
|
886
|
+
- Updated dependencies [fec722f]
|
|
887
|
+
- Updated dependencies [2d3b2c0]
|
|
888
|
+
- Updated dependencies [4772067]
|
|
889
|
+
- Updated dependencies [9fbab1b]
|
|
890
|
+
- Updated dependencies [a009f5b]
|
|
891
|
+
- Updated dependencies [c207a31]
|
|
892
|
+
- Updated dependencies [9b4f63b]
|
|
893
|
+
- Updated dependencies [4f3c075]
|
|
894
|
+
- Updated dependencies [475e085]
|
|
895
|
+
- Updated dependencies [e477440]
|
|
896
|
+
- Updated dependencies [9dcb5da]
|
|
897
|
+
- Updated dependencies [a95ca85]
|
|
898
|
+
- Updated dependencies [b6d14ce]
|
|
899
|
+
- Updated dependencies [0382a51]
|
|
900
|
+
- Updated dependencies [93d061d]
|
|
901
|
+
- Updated dependencies [39554a1]
|
|
902
|
+
- Updated dependencies [d28430a]
|
|
903
|
+
- Updated dependencies [7d9cc66]
|
|
904
|
+
- Updated dependencies [7d9cc66]
|
|
905
|
+
- Updated dependencies [7d9cc66]
|
|
906
|
+
- Updated dependencies [df02568]
|
|
907
|
+
- Updated dependencies [fcb0da3]
|
|
908
|
+
- Updated dependencies [07b627f]
|
|
909
|
+
- Updated dependencies [d0de5b2]
|
|
910
|
+
- Updated dependencies [9c6a8db]
|
|
911
|
+
- Updated dependencies [dfb9b15]
|
|
912
|
+
- Updated dependencies [7d9cc66]
|
|
913
|
+
- Updated dependencies [7d9cc66]
|
|
914
|
+
- Updated dependencies [7911a7d]
|
|
915
|
+
- Updated dependencies [d6b9404]
|
|
916
|
+
- Updated dependencies [5783499]
|
|
917
|
+
- Updated dependencies [62663ae]
|
|
918
|
+
- Updated dependencies [9c6a8db]
|
|
919
|
+
- Updated dependencies [23a30cd]
|
|
920
|
+
- Updated dependencies [7d9cc66]
|
|
921
|
+
- Updated dependencies [7d9cc66]
|
|
922
|
+
- Updated dependencies [9814afc]
|
|
923
|
+
- Updated dependencies [7d9cc66]
|
|
924
|
+
- Updated dependencies [02053ec]
|
|
925
|
+
- @robota-sdk/agent-interface-execution@3.0.0
|
|
926
|
+
- @robota-sdk/agent-core@3.0.0
|
|
927
|
+
- @robota-sdk/agent-interface-session@3.0.0
|
|
928
|
+
- @robota-sdk/agent-file-authority@3.0.0
|
|
929
|
+
|
|
930
|
+
## 3.0.0-beta.106
|
|
931
|
+
### Patch Changes
|
|
932
|
+
|
|
933
|
+
- @robota-sdk/agent-core@3.0.0-beta.106
|
|
934
|
+
- @robota-sdk/agent-file-authority@3.0.0-beta.106
|
|
935
|
+
- @robota-sdk/agent-interface-execution@3.0.0-beta.106
|
|
936
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.106
|
|
937
|
+
|
|
938
|
+
## 3.0.0-beta.105
|
|
939
|
+
### Patch Changes
|
|
940
|
+
|
|
941
|
+
- @robota-sdk/agent-core@3.0.0-beta.105
|
|
942
|
+
- @robota-sdk/agent-file-authority@3.0.0-beta.105
|
|
943
|
+
- @robota-sdk/agent-interface-execution@3.0.0-beta.105
|
|
944
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.105
|
|
945
|
+
|
|
946
|
+
## 3.0.0-beta.104
|
|
947
|
+
### Patch Changes
|
|
948
|
+
|
|
949
|
+
- @robota-sdk/agent-core@3.0.0-beta.104
|
|
950
|
+
- @robota-sdk/agent-file-authority@3.0.0-beta.104
|
|
951
|
+
- @robota-sdk/agent-interface-execution@3.0.0-beta.104
|
|
952
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.104
|
|
953
|
+
|
|
954
|
+
## 3.0.0-beta.103
|
|
955
|
+
### Patch Changes
|
|
956
|
+
|
|
957
|
+
- Updated dependencies [6f41c53]
|
|
958
|
+
- @robota-sdk/agent-core@3.0.0-beta.103
|
|
959
|
+
- @robota-sdk/agent-interface-execution@3.0.0-beta.103
|
|
960
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.103
|
|
961
|
+
- @robota-sdk/agent-file-authority@3.0.0-beta.103
|
|
962
|
+
|
|
963
|
+
## 3.0.0-beta.102
|
|
964
|
+
### Patch Changes
|
|
965
|
+
|
|
966
|
+
- @robota-sdk/agent-core@3.0.0-beta.102
|
|
967
|
+
- @robota-sdk/agent-file-authority@3.0.0-beta.102
|
|
968
|
+
- @robota-sdk/agent-interface-execution@3.0.0-beta.102
|
|
969
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.102
|
|
970
|
+
|
|
971
|
+
## 3.0.0-beta.101
|
|
972
|
+
### Patch Changes
|
|
973
|
+
|
|
974
|
+
- Updated dependencies [5783499]
|
|
975
|
+
- Updated dependencies [62663ae]
|
|
976
|
+
- @robota-sdk/agent-file-authority@3.0.0-beta.101
|
|
977
|
+
- @robota-sdk/agent-core@3.0.0-beta.101
|
|
978
|
+
- @robota-sdk/agent-interface-execution@3.0.0-beta.101
|
|
979
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.101
|
|
980
|
+
|
|
981
|
+
## 3.0.0-beta.100
|
|
982
|
+
### Patch Changes
|
|
983
|
+
|
|
984
|
+
- @robota-sdk/agent-core@3.0.0-beta.100
|
|
985
|
+
- @robota-sdk/agent-file-authority@3.0.0-beta.100
|
|
986
|
+
- @robota-sdk/agent-interface-execution@3.0.0-beta.100
|
|
987
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.100
|
|
988
|
+
|
|
989
|
+
## 3.0.0-beta.99
|
|
990
|
+
### Patch Changes
|
|
991
|
+
|
|
992
|
+
- @robota-sdk/agent-core@3.0.0-beta.99
|
|
993
|
+
- @robota-sdk/agent-file-authority@3.0.0-beta.99
|
|
994
|
+
- @robota-sdk/agent-interface-execution@3.0.0-beta.99
|
|
995
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.99
|
|
996
|
+
|
|
997
|
+
## 3.0.0-beta.98
|
|
998
|
+
### Patch Changes
|
|
999
|
+
|
|
1000
|
+
- @robota-sdk/agent-core@3.0.0-beta.98
|
|
1001
|
+
- @robota-sdk/agent-file-authority@3.0.0-beta.98
|
|
1002
|
+
- @robota-sdk/agent-interface-execution@3.0.0-beta.98
|
|
1003
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.98
|
|
1004
|
+
|
|
3
1005
|
## 3.0.0-beta.97
|
|
4
1006
|
### Patch Changes
|
|
5
1007
|
|
|
@@ -184,7 +1186,7 @@
|
|
|
184
1186
|
`gen_ai/dup` to emit both attribute sets while dashboards move. The default, `agent`, exports exactly
|
|
185
1187
|
the spans it did before. An unknown value, or the setting while neither traces nor metrics are exported,
|
|
186
1188
|
refuses startup. Console traces under a GenAI mode print the same projected spans.
|
|
187
|
-
|
|
1189
|
+
|
|
188
1190
|
The live prompt trace now carries each tool body's name (the session's tool-body observation includes it
|
|
189
1191
|
and the framework's live projection passes a label-safe name through); it reaches spans only under a
|
|
190
1192
|
GenAI mode and is never written to session history.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robota-sdk/agent-session",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Session for the Robota SDK: permission-gated tool execution, hooks, context tracking, compaction and persistence",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/node/index.js",
|
|
@@ -63,10 +63,10 @@
|
|
|
63
63
|
"node": ">=22.12.0"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@robota-sdk/agent-core": "3.0.
|
|
67
|
-
"@robota-sdk/agent-
|
|
68
|
-
"@robota-sdk/agent-interface-
|
|
69
|
-
"@robota-sdk/agent-
|
|
66
|
+
"@robota-sdk/agent-core": "3.0.1",
|
|
67
|
+
"@robota-sdk/agent-file-authority": "3.0.1",
|
|
68
|
+
"@robota-sdk/agent-interface-execution": "3.0.1",
|
|
69
|
+
"@robota-sdk/agent-interface-session": "3.0.1"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"eslint": "^8.57.1",
|