@robota-sdk/agent-cli 3.0.0-beta.86 → 3.0.0-beta.88

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 (38) hide show
  1. package/CHANGELOG.md +103 -48
  2. package/README.md +289 -39
  3. package/bin/{robota.cjs → agent.cjs} +1 -1
  4. package/dist/node/bin.js +23 -23
  5. package/dist/node/{bounded-output-Bgx2Cgkx-28em7iHq.js → bounded-output-BdWv9SX3-28em7iHq.js} +1 -1
  6. package/dist/node/{bounded-output-Bgx2Cgkx-28em7iHq.js.map → bounded-output-BdWv9SX3-28em7iHq.js.map} +1 -1
  7. package/dist/node/cli-core-CEWWF4d6.js +2917 -0
  8. package/dist/node/{command-executor-d6DdfUTD-DhFSDHvb.js → command-executor-D_UBZHxh-CVZipoOV.js} +2 -2
  9. package/dist/node/{command-executor-d6DdfUTD-DhFSDHvb.js.map → command-executor-D_UBZHxh-CVZipoOV.js.map} +1 -1
  10. package/dist/node/{command-executor-d6DdfUTD-3KEnsTnN.js → command-executor-D_UBZHxh-D7UrdAlN.js} +1 -1
  11. package/dist/node/headless.js +2 -2
  12. package/dist/node/index.d.ts +2423 -166
  13. package/dist/node/index.d.ts.map +1 -1
  14. package/dist/node/index.js +610 -580
  15. package/dist/node/index.js.map +1 -1
  16. package/dist/node/{node-BfqR16CD.js → node-BIdvEvlf.js} +1 -1
  17. package/dist/node/node-BUERQysz.js +15 -0
  18. package/dist/node/node-BWNobqRD.js +15 -0
  19. package/dist/node/node-BWNobqRD.js.map +1 -0
  20. package/dist/node/node-C4_FOvnk.js.map +1 -1
  21. package/dist/node/node-CEI_VczS.js +165 -0
  22. package/dist/node/node-CrspydTR.js +165 -0
  23. package/dist/node/node-CrspydTR.js.map +1 -0
  24. package/dist/node/node-DENLEmMT.js.map +1 -1
  25. package/dist/web/assets/index-BSxTg2Py.css +1 -0
  26. package/dist/web/assets/index-BpCxNRj_.js +309 -0
  27. package/dist/web/index.html +3 -3
  28. package/package.json +49 -44
  29. package/dist/node/cli-core-DOdGt9XE.js +0 -2891
  30. package/dist/node/node-B48couEE.js +0 -14
  31. package/dist/node/node-B48couEE.js.map +0 -1
  32. package/dist/node/node-CgLYctGm.js +0 -67
  33. package/dist/node/node-CgLYctGm.js.map +0 -1
  34. package/dist/node/node-DAOCaE6N.js +0 -67
  35. package/dist/node/node-DlLm_kxn.js +0 -14
  36. package/dist/web/assets/index-DSLs7oij.js +0 -309
  37. package/dist/web/assets/index-OqCBtMJ2.css +0 -1
  38. /package/dist/node/{bounded-output-Bgx2Cgkx-C4ucXwLR.js → bounded-output-BdWv9SX3-C4ucXwLR.js} +0 -0
package/README.md CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  # @robota-sdk/agent-cli
4
4
 
5
- `robota` is an AI coding assistant for the terminal, and the reference app of Robota — a collection
6
- of TypeScript libraries for building AI agents. It reads your project, edits files and runs commands
7
- under a permission system you control, and works with Anthropic, OpenAI, Gemini, DeepSeek, Qwen and
8
- local OpenAI-compatible models.
5
+ `robota` is the configurable terminal interface for Robota, assembled from the
6
+ same TypeScript libraries you can embed. It is a coding-capable agent within the environment whose
7
+ direction is [our agents developing and advancing our agents](../../VISION.md). Autonomous
8
+ self-evolution remains an ambition; the behavior below describes the current CLI. It reads your
9
+ project, edits files and runs commands under a permission system you control, and works with
10
+ Anthropic, OpenAI, Gemini, DeepSeek, Qwen and local OpenAI-compatible models.
9
11
 
10
12
  The CLI is assembled from the same packages you can use in your own app: `@robota-sdk/agent-framework`
11
13
  for the session, `@robota-sdk/agent-ui-terminal` for the terminal UI, and one package per model
@@ -15,8 +17,6 @@ provider. To build your own agent rather than use this one, start with the
15
17
  > **Beta.** Behavior may change before the stable release. Please
16
18
  > [report issues](https://github.com/woojubb/robota/issues).
17
19
 
18
- ![robota reading a project file and explaining its entry point in the terminal](./docs/demo.gif)
19
-
20
20
  ## Install
21
21
 
22
22
  Requires Node.js 22.12.0 or later.
@@ -35,7 +35,7 @@ Run `robota` inside a Git repository. It first asks whether to trust the folder:
35
35
  workspace can load the project's own settings, hooks, skills, plugins and MCP servers. Answer no and
36
36
  the session starts **Restricted**, with your user settings and the built-in tools only. The first
37
37
  time, it then walks you through choosing a provider and filling in its fields (model, base URL, API
38
- key), and saves the profile to `~/.robota/settings.json`.
38
+ key), and saves the profile to `<configured-user-state-dir>/settings.json`.
39
39
 
40
40
  ```bash
41
41
  cd my-project
@@ -53,9 +53,9 @@ robota --configure-provider anthropic --type anthropic --model claude-sonnet-4-6
53
53
  robota trust --yes
54
54
  ```
55
55
 
56
- `robota init` writes a starter `AGENTS.md` and `.robota/settings.json` for the current project.
56
+ `robota init` writes a starter `AGENTS.md` and `<configured-project-state-dir>/settings.json` for the current project.
57
57
  `robota --configure` reruns the interactive provider setup, and `robota --reset` deletes
58
- `~/.robota/settings.json`.
58
+ `<configured-user-state-dir>/settings.json`.
59
59
 
60
60
  ## What you can do
61
61
 
@@ -63,7 +63,7 @@ robota trust --yes
63
63
 
64
64
  `robota` starts an interactive session. Type a request, or `/` to open the command menu (`/help`
65
65
  lists every command). `Esc` stops the current response, `Ctrl+R` searches the prompts you have typed
66
- before, and every key can be rebound in `~/.robota/keybindings.json` (see the
66
+ before, and every key can be rebound in `<configured-user-state-dir>/keybindings.json` (see the
67
67
  [keybindings guide](../../content/guide/keybindings.md)).
68
68
 
69
69
  The agent works with file and shell tools (`Read`, `Write`, `Edit`, `Glob`, `Grep`, `Bash`),
@@ -154,7 +154,7 @@ any settings file:
154
154
  ```
155
155
 
156
156
  An `ask` rule asks even in `bypassPermissions`, and a few actions are never approved automatically
157
- in any mode: `rm` on the root, home or working directory, and writes into `.git`, `.robota`,
157
+ in any mode: `rm` on the root, home or working directory, and writes into `.git`, `<configured-project-state-dir>`,
158
158
  `.claude`, `.agents` or shell and tool configuration files.
159
159
 
160
160
  Shell commands can also run inside an OS sandbox (bubblewrap on Linux, Seatbelt on macOS): `/sandbox`
@@ -162,7 +162,7 @@ switches between `auto-allow`, `regular` and `off`, and the `sandbox` settings k
162
162
  When something misbehaves, `robota --safe-mode` starts with instruction files, skills, plugins, hooks
163
163
  and MCP servers all off.
164
164
 
165
- Workspace trust is granted per Git worktree and kept in `~/.robota/workspace-trust.json`:
165
+ Workspace trust is granted per Git worktree and kept in `<configured-user-state-dir>/workspace-trust.json`:
166
166
 
167
167
  ```bash
168
168
  robota trust status # is this workspace trusted, and what would trust load? (--json: one line)
@@ -207,11 +207,11 @@ when the first is overloaded, `--effort <level>` sets model effort, and `--advis
207
207
  lets the model consult a second model. See [Providers](../../content/guide/providers.md) and
208
208
  [Local LLM setup](../../content/guide/local-llm.md).
209
209
 
210
- ### Connect MCP servers, or serve Robota over MCP
210
+ ### Connect MCP servers, or serve the session over MCP
211
211
 
212
212
  Declare remote MCP servers under `mcpServers` in a settings file. A declared server is not connected
213
213
  until you approve it: `/mcp` shows each server's state, and `/mcp approve <server>` connects it in
214
- the running session and keeps the approval in `~/.robota/mcp-approvals.json` for later starts. A
214
+ the running session and keeps the approval in `<configured-user-state-dir>/mcp-approvals.json` for later starts. A
215
215
  server that uses OAuth also needs a sign-in — `/mcp login <server>` in a session, or
216
216
  `robota mcp login <server>` in a terminal — and connects once you sign in.
217
217
 
@@ -223,7 +223,44 @@ server that uses OAuth also needs a sign-in — `/mcp login <server>` in a sessi
223
223
  }
224
224
  ```
225
225
 
226
- `robota mcp serve` does the reverse: it serves one Robota session to an MCP host over stdio (or over
226
+ An HTTP or stdio definition can explicitly select `"protocolVersion": "2026-07-28"` for a stateless
227
+ peer. Omit that field for the legacy handshake. Changing it requires approval again; unsupported values
228
+ are refused rather than silently ignored. The selected version is visible in the definition details.
229
+ Startup reports unavailable client-input, subscription and extension capabilities. Selecting this
230
+ version does not enable distributed Skills or give the server any additional authority. Add
231
+ `"skills": true` to explicitly opt that definition into a peer's advertised Skills extension;
232
+ changing this selection requires fresh server approval. `/mcp skill-list <server>` reads metadata
233
+ only. A local terminal or app user can review `/mcp skill-inspect <server> <uri>`, then copy its
234
+ `/mcp skill-approve <server> <uri> <fingerprint>` command to consent to those exact instructions and
235
+ frontmatter. `/mcp skill-withdraw <server> <uri>` withdraws that consent. JSON string arrays also
236
+ accept opaque names and URIs containing spaces. Content decisions persist separately in
237
+ `<configured-user-state-dir>/mcp-skill-approvals.json`, bound to the host origin, workspace and URI.
238
+ Observed changed or removed manifests withdraw consent even if the peer later reverts them.
239
+ Discovery returns an exact invocation name for each supported skill, or a reason its profile is
240
+ unavailable. Invoke that name through the session's skill command. Instructions load only when
241
+ the actual turn starts, under current content consent; ending or cancelling that turn closes its
242
+ activation. A queued or cancelled request does not load instructions into another turn. Remote
243
+ shell expressions remain instruction text and do not execute as local preprocessing. During an
244
+ active execution, `/skill-read ["<invocation-name>", "<resource-uri>"]` reads a listed supporting
245
+ file with fresh verification. A fork can read only files from its own activations; a nested SKILL.md remains
246
+ supporting data until separately approved and activated.
247
+
248
+ MCP calls recheck approval and the current source immediately before dispatch. Removing or
249
+ replacing a definition, revoking approval, changing required workspace trust, or closing the
250
+ composition refuses new calls through retained tools. Calls already dispatched keep their real
251
+ outcome; revocation is not rollback. A changed configuration needs a fresh startup and approval
252
+ rather than silently substituting a new connection into an existing tool chain. The startup host
253
+ also exposes declarative contribution descriptors bound to configuration fingerprints, without
254
+ credential values or an invented server version.
255
+
256
+ | Surface | Verified coverage | Limit |
257
+ | ------------------------------------------ | -------------------------------------------------------------------------------------- | ------------------------------------------------------- |
258
+ | Linux interactive, serve and print startup | Real admitted stdio calls; changed settings refused before another call | Shared startup composition; not a rendered app UI trial |
259
+ | Installed Linux bundle in serve startup | Disable, uninstall and selected-revision update; durable approval rechecked at restart | Already dispatched calls settle; no rollback claim |
260
+ | Remote HTTP | Composition, transport admission and call routing regressions | No live account benchmark |
261
+ | macOS and Windows | Repository CI build routes | No external-plugin pilot on those OSes |
262
+
263
+ `robota mcp serve` does the reverse: it serves one agent session to an MCP host over stdio (or over
227
264
  authenticated HTTP with the `--http-*` and `--oauth-*` flags). Trust the project first, and give the
228
265
  host the absolute path of `robota` and the project directory:
229
266
 
@@ -243,10 +280,10 @@ See [MCP](../../content/guide/mcp.md).
243
280
 
244
281
  ### Add skills, commands, agents and plugins
245
282
 
246
- Skills and commands are Markdown files the CLI finds in `.robota/skills/`, `.claude/skills/`,
283
+ Skills and commands are Markdown files the CLI finds in `<configured-project-state-dir>/skills/`, `.claude/skills/`,
247
284
  `.claude/commands/` and `.agents/skills/` — in a trusted project and under your home directory. Each
248
285
  one becomes a slash command (`/<name>`), and `/skills` lists them. Agent definitions are read from
249
- `.robota/agents/`, `.agents/agents/` and `.claude/agents/`. Plugins bundle these together with hooks,
286
+ `<configured-project-state-dir>/agents/`, `.agents/agents/` and `.claude/agents/`. Plugins bundle these together with hooks,
250
287
  themes and MCP servers:
251
288
 
252
289
  ```text
@@ -259,11 +296,35 @@ See the [CLI guide](../../content/guide/cli.md) for skill frontmatter and plugin
259
296
 
260
297
  ### Use the graphical interface
261
298
 
262
- `robota --serve --open` starts a headless runtime for the current workspace, serves the Robota GUI
299
+ `robota --serve --open` starts a headless runtime for the current workspace, serves the configured GUI
263
300
  on `127.0.0.1` and opens it in your browser. The Electron desktop app in this repository
264
301
  ([`apps/agent-app`](../../apps/agent-app/docs/README.md)) shows the same GUI over the workspace
265
302
  daemon; it is not published to npm. See [The GUI and the Desktop App](../../content/guide/gui.md).
266
303
 
304
+ ### Let other apps and services use the agent
305
+
306
+ `robota --serve --http-port <port>` also serves the agent over HTTP on `127.0.0.1:<port>`, so another
307
+ application or service can use it without a Robota client library. Every request presents the
308
+ bearer you choose in `PRODUCT_HTTP_TOKEN` (at least 32 characters); the runtime removes it from its
309
+ environment, so the commands it runs do not inherit it. A command running as the same OS user can
310
+ still read the server's launch environment unless the OS sandbox confines it (or the server runs
311
+ as a separate user), so enable the sandbox on a server. Reach the API from elsewhere through your own
312
+ reverse proxy or tunnel: it binds loopback only.
313
+
314
+ ```bash
315
+ PRODUCT_HTTP_TOKEN="$(openssl rand -hex 32)" robota --serve --http-port 8787
316
+ ```
317
+
318
+ | Request | Does |
319
+ | --- | --- |
320
+ | `POST /submit` `{"prompt": "..."}` | Runs a turn and streams it as SSE (`text_delta`, `complete`, `error`); `409` while a turn is running |
321
+ | `POST /command` `{"name": "help", "args": ""}` | Runs a slash command |
322
+ | `POST /abort` · `POST /cancel-queue` | Aborts the running turn · drops the queued prompt |
323
+ | `GET /messages` · `/context` · `/executing` · `/pending` | Reads the conversation and its state |
324
+
325
+ No one can answer a permission prompt over HTTP, so a question no other client answers is denied;
326
+ choose the permission mode and rules the server runs under.
327
+
267
328
  ### Reach other sessions and your other devices
268
329
 
269
330
  `/peers` lists the other live `robota` sessions on this machine, and `/peers send <session-id>
@@ -289,26 +350,26 @@ robota eval <definition> # run an evals-as-code definition; exits 1 on a metric
289
350
  Settings are merged from these files, lowest priority first. The two user files always apply; the
290
351
  four project files apply only in a trusted workspace.
291
352
 
292
- | File | Scope |
293
- | ----------------------------- | --------------------------------------- |
294
- | `~/.robota/settings.json` | user |
295
- | `~/.claude/settings.json` | user (Claude Code-compatible) |
296
- | `.robota/settings.json` | project, committed |
297
- | `.robota/settings.local.json` | project, local to this machine |
298
- | `.claude/settings.json` | project (Claude Code-compatible) |
299
- | `.claude/settings.local.json` | project, local (Claude Code-compatible) |
300
-
301
- Other files the CLI keeps under `~/.robota/`:
302
-
303
- | Path | Contents |
304
- | ---------------------- | ----------------------------------------------------------------------- |
305
- | `workspace-trust.json` | workspaces you have trusted |
306
- | `sessions/` | saved sessions (a trusted project may keep them in `.robota/sessions/`) |
307
- | `history.jsonl` | prompts you typed, for `Ctrl+R` (`"promptHistory": false` turns it off) |
308
- | `keybindings.json` | key bindings |
309
- | `themes/` | your own `/theme` themes |
310
- | `plugins/` | installed plugins (a project may have its own `.robota/plugins/`) |
311
- | `mcp-credentials/` | OAuth tokens for MCP servers, readable by you only |
353
+ | File | Scope |
354
+ | ---------------------------------------------------- | --------------------------------------- |
355
+ | `<configured-user-state-dir>/settings.json` | user |
356
+ | `~/.claude/settings.json` | user (Claude Code-compatible) |
357
+ | `<configured-project-state-dir>/settings.json` | project, committed |
358
+ | `<configured-project-state-dir>/settings.local.json` | project, local to this machine |
359
+ | `.claude/settings.json` | project (Claude Code-compatible) |
360
+ | `.claude/settings.local.json` | project, local (Claude Code-compatible) |
361
+
362
+ Other files the CLI keeps under `<configured-user-state-dir>/`:
363
+
364
+ | Path | Contents |
365
+ | ---------------------- | ---------------------------------------------------------------------------------------------- |
366
+ | `workspace-trust.json` | workspaces you have trusted |
367
+ | `sessions/` | saved sessions (a trusted project may keep them in `<configured-project-state-dir>/sessions/`) |
368
+ | `history.jsonl` | prompts you typed, for `Ctrl+R` (`"promptHistory": false` turns it off) |
369
+ | `keybindings.json` | key bindings |
370
+ | `themes/` | your own `/theme` themes |
371
+ | `plugins/` | installed plugins (a project may have its own `<configured-project-state-dir>/plugins/`) |
372
+ | `mcp-credentials/` | OAuth tokens for MCP servers, readable by you only |
312
373
 
313
374
  ## Use it from code
314
375
 
@@ -320,6 +381,195 @@ A subagent runs in a child process that starts your entry script again with a wo
320
381
  settles (the worker ends the process). So call `startCli()` from the script Node started, and put
321
382
  nothing before it that must not run once per subagent.
322
383
 
384
+ ### Hosted task-worker execution
385
+
386
+ The library also exports `HostedOrganizationControl` and `createHostedOrganizationGateway` for
387
+ the owner-hosted company backend. This composition requires an anchored `OrganizationLedger`,
388
+ an `OrganizationAudit`, provider-backed worker inventory and named detection, containment,
389
+ assessment and recovery owners. The gateway serves the configured admission endpoints and
390
+ OpenAI model endpoints; its worker ingress has no policy, approval or recovery routes.
391
+ Each model-only grant has a distinct owner-issued task token and runtime binding. The logical task
392
+ root survives grant rotation; provider inventory and gateway admission pin its current binding. Issuer signing
393
+ capabilities, upstream credentials, policy, audit and private payload storage stay outside workers.
394
+ Sign task access with the exported `hostedWorkerAccessBytes` and provision E2B workers with an
395
+ `organization` binding; `createE2BOrganizationInventory` enumerates all pages, including paused
396
+ workers, and rechecks ownership before deletion. Owner stop methods persist withdrawal first,
397
+ abort affected broker calls, and wait for descendant provider cleanup; unresolved cleanup is
398
+ reported to the incident owners.
399
+
400
+ `HostedOrganizationPayloads` stores bounded request/response bytes in an owner-only directory;
401
+ the durable journal and audit retain digests rather than these bodies. Install
402
+ `createHostedOrganizationModelAction` with a fixed provider endpoint/model, owner-selected
403
+ reservation, input/output bounds and explicit upstream idempotency support. A mandatory trusted
404
+ `countInputTokens` port counts the exact immutable upstream input before dispatch; input plus the
405
+ owner-selected output cap must fit the token and conservative cost reservation.
406
+ `createHostedOrganizationInputTokenCounter` connects a fixed private counting endpoint through an
407
+ owner-selected encoder. A Responses counter does not establish exact Chat token counts; select a
408
+ verified counter for that surface. The counting service must be read-only and covered by the
409
+ owner's pricing assumptions. The gateway buffers
410
+ the upstream result until durable settlement, then returns JSON or OpenAI stream events.
411
+ Configure the worker's OpenAI profile with `options: { durableOperations: true }`: one operation
412
+ identity survives SDK retries, and missing identities are refused. Unknown provider outcomes
413
+ retain reservations and require asset-owner reconciliation; this adapter never retries upstream.
414
+ The private payload store retains digest-bound operation claims for recovery. Owner code may
415
+ load those claims with `operation` and call `reconcileExternalOperation` with confirmed asset-owner
416
+ evidence. Reconciliation charges the entire retained reservation and cannot restore revoked
417
+ authority; the worker gateway exposes no reconciliation route.
418
+ Broker supervisory token/cost observations report settled usage; pending reservations remain
419
+ enforced by the hierarchical ledger. Local process and wire tests establish these software paths,
420
+ while physical containment, provider billing and independent remote custody require deployment
421
+ measurements.
422
+
423
+ Hosted posture requires current signed admission for a separate task worker and company broker.
424
+ The installed E2B executor runs the stock CLI inside that worker. The runtime forwards arguments,
425
+ input and output and owns cancellation, credential expiry and provider deletion; it does not run
426
+ task tools against its own filesystem. Direct `--api-key` configuration is refused.
427
+
428
+ `PRODUCT_HOSTED_RUNTIME_CONFIG` selects an owner-controlled version-3 deployment file. It retains
429
+ `identity`, `epoch`, `worker`, `broker`, `snapshot`, `lifetimeMs` and `probeTimeoutMs`, and requires
430
+ `limits: { sessions, modelCalls, modelTokens, costMicros }` with nonnegative safe integer ceilings.
431
+ Version-2 deployments and proofs are refused because they cannot attest current accounting.
432
+ The worker and broker answer version-3 admission challenges with the configured limits; worker
433
+ proofs carry `usage: null`, while broker proofs carry cumulative usage in that same four-counter
434
+ shape. The broker measures registered sessions and provider calls outside the worker for the
435
+ current identity and epoch. Counts and micro-cost units come from its trusted instrumentation,
436
+ not CLI output, model messages or a checkpoint. Both attestations sign the fixed tuple returned by
437
+ the exported `hostedAdmissionBytes`, binding limits and accounting to the current challenge,
438
+ identity, root, epoch and checkpoint. The issuer must validate requested limits against owner policy
439
+ before attesting them.
440
+
441
+ `HostedRuntimeController.status()` exposes the latest verified observations to the owner. The
442
+ controller checks them before executor allocation, during admission refresh and after short
443
+ executions before reporting success. Invalid, regressing, unavailable or over-limit observations
444
+ refuse admission or stop and release owned execution. Runtime ceilings supervise observed totals;
445
+ the broker's per-request reservations must bound new model calls and their maximum token/cost
446
+ usage before dispatch. Supervisory polling does not replace those reservations or establish an
447
+ invoice hard cap. The broker must retain cumulative accounting across controller restart/resume;
448
+ recovered task content cannot reset it. Actual provider charges and cleanup remain separately
449
+ measured operational evidence.
450
+
451
+ The operator supplies `PRODUCT_E2B_API_KEY` to the runtime and sets
452
+ `PRODUCT_HOSTED_WORKER_EXECUTION_CONFIG` to an absolute owner-only JSON file. Its version-1 shape is:
453
+
454
+ ```json
455
+ {
456
+ "version": 1,
457
+ "templateId": "operator-pinned-template",
458
+ "nodeExecutable": "/usr/local/bin/node",
459
+ "workspaceRoot": "/workspace",
460
+ "entrypoint": { "path": "/opt/agent/cli.mjs", "digest": "<sha256>" },
461
+ "productConfig": { "path": "/opt/agent/product.env", "digest": "<sha256>" },
462
+ "access": {
463
+ "version": 1,
464
+ "identity": {
465
+ "tenant": "...",
466
+ "task": "...",
467
+ "rootTask": "...",
468
+ "actor": "...",
469
+ "runtime": "..."
470
+ },
471
+ "worker": "...",
472
+ "broker": "...",
473
+ "epoch": 1,
474
+ "endpoint": "https://company-broker.example/v1",
475
+ "token": "<task-scoped-broker-token>",
476
+ "issuedAt": 0,
477
+ "expiresAt": 0,
478
+ "signature": "<broker-ed25519-base64url-signature>"
479
+ }
480
+ }
481
+ ```
482
+
483
+ The broker signs the fixed tuple encoded by `hostedWorkerAccessBytes` in the implementation.
484
+ Access must match the admission's identity, resources and epoch, use the admitted broker origin,
485
+ and expire within both admission and sixty seconds of issuance. This executor ends execution at
486
+ expiry; it does not renew credentials. The template's CLI and product environment artifacts must
487
+ match their pinned digests. The product artifact accepts only CLI product metadata variables.
488
+ Provider metadata must match task ownership and the template, with public traffic disabled and
489
+ outbound access limited to the broker hostname and no attached volumes. The resource must already
490
+ be running, with timeout deletion and auto-resume disabled; connecting a paused memory image is
491
+ refused. These checks do not establish measured cloud containment.
492
+
493
+ An owner backend can call `provisionE2BTaskWorker` before issuing admission. It creates the
494
+ operator-selected clean template with closed networking, no worker credentials or volumes, and
495
+ an exclusive new workspace. It returns the resource, workspace root, operation ID, checkpoint
496
+ reference, optional conversation artifact receipt and an idempotent `release()` operation. The management capability stays in this
497
+ owner-side SDK composition. After the receipt, the owner signs current worker/broker admission
498
+ and task access for the returned resource and configures that workspace in the execution input.
499
+
500
+ For recovery, supply an owner-approved `{ id, digest }` reference and the matching UTF-8 JSON bytes
501
+ in `checkpoint`. The manifest has `version: 1`, `id`, the source `identity`, `epoch`, `worker`, and
502
+ `files: [{ path, base64 }]`. Paths are relative regular workspace files; private HOME/TMPDIR and link or memory records
503
+ are not accepted. Owner authority journals stay outside this workspace. The source must belong
504
+ to the same tenant/task/root and precede the current epoch and runtime. File bytes are read back
505
+ and compared before the owner receives a successful result. Treat recovered project content as
506
+ untrusted; current profile, policy and credentials come from the clean operator composition.
507
+ For conversation recovery, use manifest `version: 2` with the same fields and `session` containing
508
+ an existing versioned session-record envelope (`{ schemaVersion, record }`). Only the conversation,
509
+ name, selector and timestamps survive. Saved system messages/prompts, message metadata, tool schemas,
510
+ VM state, goals, background jobs, loops, memory references and branch pointers do not restore;
511
+ current composition rebuilds execution and authority. Display history is rebuilt from the projected
512
+ conversation; saved events are not imported. The record's cwd becomes the fresh workspace.
513
+ Pass the returned `resumeSession: { id, path, digest }` in the private worker execution input and
514
+ invoke `--resume <id>` (or `--continue`, optionally with `--fork-session`). The default executor
515
+ verifies the receipt and copies the projected record into the current product artifact's selected
516
+ private user session store before CLI startup. It refuses an existing destination, mismatched
517
+ selector/digest, malformed record or a user state root inside recovered project files. No saved
518
+ settings, trust decision or credential is imported. A resume without this receipt refuses execution.
519
+
520
+ The default executor accepts this filesystem checkpoint only when signed current admission and
521
+ provider metadata agree on its digest, fresh identity/epoch and source resource. It does not
522
+ restore provider memory snapshots or reconnect the checkpoint source. Setup failures delete
523
+ verified allocations; unknown creation/ownership and unresolved deletion return
524
+ `E2BWorkerProvisioningError` with reconciliation identifiers. The external owner must retain and
525
+ reconcile those outcomes rather than retry or report cleanup as successful.
526
+
527
+ The worker receives a closed environment with private HOME/TMPDIR, product configuration and the
528
+ scoped broker token through the OpenAI-compatible provider interface. Management and upstream
529
+ credentials stay outside the worker. Interactive input uses a PTY; input EOF requests the stock
530
+ CLI's Ctrl-C shutdown. For `--serve`, include `serve: { port, workerPort, token }` in the private
531
+ execution input. `port` selects the runtime's IPv4 loopback ingress (`0` assigns a free port);
532
+ `workerPort` selects the worker daemon port. Generate a fresh, unpredictable owner client `token`
533
+ of 32–256 URL-safe characters and connect to the printed runtime URL with `?token=<token>`.
534
+ The runtime validates the client token, Host and Origin, then carries binary WebSocket traffic
535
+ through an owned provider command to the pinned worker loopback listener. It replaces client
536
+ headers and credentials with a separate per-launch worker token. The owner token and provider
537
+ management capability stay in the runtime. Stopping execution closes the listener and clients
538
+ and deletes the worker. This local ingress does not provide remote desktop pairing or TLS.
539
+ Credential renewal requires broker integration; this executor still ends at its signed access expiry.
540
+
541
+ Embedders can override the installed executor with `hostedRuntimeExecutorFactory`. It receives
542
+ `(admission, signal, invocation)` with immutable captured arguments and returns `run`, `stop` and
543
+ `release`. The adapter owns authenticated communication, input/output and trusted usage reporting;
544
+ stop and release settle only after cleanup. Missing or invalid authority refuses execution without
545
+ entering local host adapters. CLI flags request behavior and never confer company authority.
546
+
547
+ ## Remote desktop owner composition
548
+
549
+ Use `HostedDesktopWorkerPort.connect({ endpoint, token, worker, control })` with the owner's private
550
+ runtime loopback ingress. It discovers and pins the actual worker session; retain this carrier across
551
+ desktop reconnects. Create `HostedDesktopAuthorization` with the public HTTPS URL, pinned issuer,
552
+ current `HostedOrganizationControl`, and owner-selected bindings containing `id`, `user`, `client`,
553
+ the discovered `session`, and the same `worker`. Give `HostedDesktopGateway` the owner's HTTPS server,
554
+ that authorization, a `HostedDesktopAuthorityStore` and a binding-ID-to-worker-port map. The store uses
555
+ a private existing directory and an independently held organization ledger anchor; `create: true` is only
556
+ for first creation. Store loss, rollback or an uncertain anchor acknowledgment refuses access.
557
+
558
+ The issuer signs a short-lived asymmetric JWT with exact `sub`, `client_id`, `tenant`, `task`, `session`,
559
+ `workload` and `epoch` binding claims, unique `jti`, `iat`, and `exp`. Ordinary credentials have the public
560
+ URL as audience and `desktop:pair desktop:drive` scopes. A separate operator credential has
561
+ `<publicUrl>/approval` as audience and `desktop:approve` scope. Credentials expire within two minutes;
562
+ pairing grants at most one minute and never extends current company authority. Reconnect uses new JWTs,
563
+ withdraws the previous connection and preserves the pinned worker session. Issuer/JWK outages and current
564
+ grant withdrawal close active access. `gateway.revoke(bindingId)` is an owner-only withdrawal API.
565
+
566
+ Remote traffic requires TLS and the pinned Host/Origin. TLS termination requires explicitly selected
567
+ proxy IPs with matching HTTPS forwarding headers; arbitrary proxy headers are refused. The renderer
568
+ cannot switch tasks/sessions, execute slash commands, change settings, or approve permissions. The
569
+ main-process operator channel binds Allow once/Deny to the currently pending operation digest and
570
+ rejects replay. Run all authority stores and gateway/control composition outside the task worker.
571
+ These local integration checks do not establish deployed cloud containment or operational latency.
572
+
323
573
  ## Work on the CLI in this repository
324
574
 
325
575
  ```bash
@@ -340,5 +590,5 @@ pnpm cli:trust # trust this repository for the source CLI
340
590
 
341
591
  ## License
342
592
 
343
- Robota is dual-licensed under the [GNU AGPL-3.0](../../LICENSE) or a
593
+ This package is dual-licensed under the [GNU AGPL-3.0](../../LICENSE) or a
344
594
  [commercial license](../../COMMERCIAL.md). See [LICENSING.md](../../LICENSING.md).
@@ -8,7 +8,7 @@ const REQUIRED_NODE_MAJOR = 22;
8
8
  const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
9
9
  if (nodeMajor < REQUIRED_NODE_MAJOR) {
10
10
  process.stderr.write(
11
- '\n Robota requires Node.js ' +
11
+ '\n This CLI requires Node.js ' +
12
12
  REQUIRED_NODE_MAJOR +
13
13
  ' or higher.\n' +
14
14
  ' Current version: ' +