@robota-sdk/agent-cli 3.0.0-beta.85 → 3.0.0-beta.87

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 +94 -48
  2. package/README.md +265 -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-6GIkeGqT.js +2911 -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 +605 -581
  15. package/dist/node/index.js.map +1 -1
  16. package/dist/node/node-BUERQysz.js +15 -0
  17. package/dist/node/node-BWNobqRD.js +15 -0
  18. package/dist/node/node-BWNobqRD.js.map +1 -0
  19. package/dist/node/{node-CpH9vRQt.js → node-BbADaLn-.js} +1 -1
  20. package/dist/node/node-C4_FOvnk.js.map +1 -1
  21. package/dist/node/node-Cjb1v2Jw.js +165 -0
  22. package/dist/node/node-DENLEmMT.js.map +1 -1
  23. package/dist/node/node-DGvY0f9e.js +165 -0
  24. package/dist/node/node-DGvY0f9e.js.map +1 -0
  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 +46 -44
  29. package/dist/node/cli-core-DjVnz1-_.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,7 +296,7 @@ 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).
@@ -289,26 +326,26 @@ robota eval <definition> # run an evals-as-code definition; exits 1 on a metric
289
326
  Settings are merged from these files, lowest priority first. The two user files always apply; the
290
327
  four project files apply only in a trusted workspace.
291
328
 
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 |
329
+ | File | Scope |
330
+ | ---------------------------------------------------- | --------------------------------------- |
331
+ | `<configured-user-state-dir>/settings.json` | user |
332
+ | `~/.claude/settings.json` | user (Claude Code-compatible) |
333
+ | `<configured-project-state-dir>/settings.json` | project, committed |
334
+ | `<configured-project-state-dir>/settings.local.json` | project, local to this machine |
335
+ | `.claude/settings.json` | project (Claude Code-compatible) |
336
+ | `.claude/settings.local.json` | project, local (Claude Code-compatible) |
337
+
338
+ Other files the CLI keeps under `<configured-user-state-dir>/`:
339
+
340
+ | Path | Contents |
341
+ | ---------------------- | ---------------------------------------------------------------------------------------------- |
342
+ | `workspace-trust.json` | workspaces you have trusted |
343
+ | `sessions/` | saved sessions (a trusted project may keep them in `<configured-project-state-dir>/sessions/`) |
344
+ | `history.jsonl` | prompts you typed, for `Ctrl+R` (`"promptHistory": false` turns it off) |
345
+ | `keybindings.json` | key bindings |
346
+ | `themes/` | your own `/theme` themes |
347
+ | `plugins/` | installed plugins (a project may have its own `<configured-project-state-dir>/plugins/`) |
348
+ | `mcp-credentials/` | OAuth tokens for MCP servers, readable by you only |
312
349
 
313
350
  ## Use it from code
314
351
 
@@ -320,6 +357,195 @@ A subagent runs in a child process that starts your entry script again with a wo
320
357
  settles (the worker ends the process). So call `startCli()` from the script Node started, and put
321
358
  nothing before it that must not run once per subagent.
322
359
 
360
+ ### Hosted task-worker execution
361
+
362
+ The library also exports `HostedOrganizationControl` and `createHostedOrganizationGateway` for
363
+ the owner-hosted company backend. This composition requires an anchored `OrganizationLedger`,
364
+ an `OrganizationAudit`, provider-backed worker inventory and named detection, containment,
365
+ assessment and recovery owners. The gateway serves the configured admission endpoints and
366
+ OpenAI model endpoints; its worker ingress has no policy, approval or recovery routes.
367
+ Each model-only grant has a distinct owner-issued task token and runtime binding. The logical task
368
+ root survives grant rotation; provider inventory and gateway admission pin its current binding. Issuer signing
369
+ capabilities, upstream credentials, policy, audit and private payload storage stay outside workers.
370
+ Sign task access with the exported `hostedWorkerAccessBytes` and provision E2B workers with an
371
+ `organization` binding; `createE2BOrganizationInventory` enumerates all pages, including paused
372
+ workers, and rechecks ownership before deletion. Owner stop methods persist withdrawal first,
373
+ abort affected broker calls, and wait for descendant provider cleanup; unresolved cleanup is
374
+ reported to the incident owners.
375
+
376
+ `HostedOrganizationPayloads` stores bounded request/response bytes in an owner-only directory;
377
+ the durable journal and audit retain digests rather than these bodies. Install
378
+ `createHostedOrganizationModelAction` with a fixed provider endpoint/model, owner-selected
379
+ reservation, input/output bounds and explicit upstream idempotency support. A mandatory trusted
380
+ `countInputTokens` port counts the exact immutable upstream input before dispatch; input plus the
381
+ owner-selected output cap must fit the token and conservative cost reservation.
382
+ `createHostedOrganizationInputTokenCounter` connects a fixed private counting endpoint through an
383
+ owner-selected encoder. A Responses counter does not establish exact Chat token counts; select a
384
+ verified counter for that surface. The counting service must be read-only and covered by the
385
+ owner's pricing assumptions. The gateway buffers
386
+ the upstream result until durable settlement, then returns JSON or OpenAI stream events.
387
+ Configure the worker's OpenAI profile with `options: { durableOperations: true }`: one operation
388
+ identity survives SDK retries, and missing identities are refused. Unknown provider outcomes
389
+ retain reservations and require asset-owner reconciliation; this adapter never retries upstream.
390
+ The private payload store retains digest-bound operation claims for recovery. Owner code may
391
+ load those claims with `operation` and call `reconcileExternalOperation` with confirmed asset-owner
392
+ evidence. Reconciliation charges the entire retained reservation and cannot restore revoked
393
+ authority; the worker gateway exposes no reconciliation route.
394
+ Broker supervisory token/cost observations report settled usage; pending reservations remain
395
+ enforced by the hierarchical ledger. Local process and wire tests establish these software paths,
396
+ while physical containment, provider billing and independent remote custody require deployment
397
+ measurements.
398
+
399
+ Hosted posture requires current signed admission for a separate task worker and company broker.
400
+ The installed E2B executor runs the stock CLI inside that worker. The runtime forwards arguments,
401
+ input and output and owns cancellation, credential expiry and provider deletion; it does not run
402
+ task tools against its own filesystem. Direct `--api-key` configuration is refused.
403
+
404
+ `PRODUCT_HOSTED_RUNTIME_CONFIG` selects an owner-controlled version-3 deployment file. It retains
405
+ `identity`, `epoch`, `worker`, `broker`, `snapshot`, `lifetimeMs` and `probeTimeoutMs`, and requires
406
+ `limits: { sessions, modelCalls, modelTokens, costMicros }` with nonnegative safe integer ceilings.
407
+ Version-2 deployments and proofs are refused because they cannot attest current accounting.
408
+ The worker and broker answer version-3 admission challenges with the configured limits; worker
409
+ proofs carry `usage: null`, while broker proofs carry cumulative usage in that same four-counter
410
+ shape. The broker measures registered sessions and provider calls outside the worker for the
411
+ current identity and epoch. Counts and micro-cost units come from its trusted instrumentation,
412
+ not CLI output, model messages or a checkpoint. Both attestations sign the fixed tuple returned by
413
+ the exported `hostedAdmissionBytes`, binding limits and accounting to the current challenge,
414
+ identity, root, epoch and checkpoint. The issuer must validate requested limits against owner policy
415
+ before attesting them.
416
+
417
+ `HostedRuntimeController.status()` exposes the latest verified observations to the owner. The
418
+ controller checks them before executor allocation, during admission refresh and after short
419
+ executions before reporting success. Invalid, regressing, unavailable or over-limit observations
420
+ refuse admission or stop and release owned execution. Runtime ceilings supervise observed totals;
421
+ the broker's per-request reservations must bound new model calls and their maximum token/cost
422
+ usage before dispatch. Supervisory polling does not replace those reservations or establish an
423
+ invoice hard cap. The broker must retain cumulative accounting across controller restart/resume;
424
+ recovered task content cannot reset it. Actual provider charges and cleanup remain separately
425
+ measured operational evidence.
426
+
427
+ The operator supplies `PRODUCT_E2B_API_KEY` to the runtime and sets
428
+ `PRODUCT_HOSTED_WORKER_EXECUTION_CONFIG` to an absolute owner-only JSON file. Its version-1 shape is:
429
+
430
+ ```json
431
+ {
432
+ "version": 1,
433
+ "templateId": "operator-pinned-template",
434
+ "nodeExecutable": "/usr/local/bin/node",
435
+ "workspaceRoot": "/workspace",
436
+ "entrypoint": { "path": "/opt/agent/cli.mjs", "digest": "<sha256>" },
437
+ "productConfig": { "path": "/opt/agent/product.env", "digest": "<sha256>" },
438
+ "access": {
439
+ "version": 1,
440
+ "identity": {
441
+ "tenant": "...",
442
+ "task": "...",
443
+ "rootTask": "...",
444
+ "actor": "...",
445
+ "runtime": "..."
446
+ },
447
+ "worker": "...",
448
+ "broker": "...",
449
+ "epoch": 1,
450
+ "endpoint": "https://company-broker.example/v1",
451
+ "token": "<task-scoped-broker-token>",
452
+ "issuedAt": 0,
453
+ "expiresAt": 0,
454
+ "signature": "<broker-ed25519-base64url-signature>"
455
+ }
456
+ }
457
+ ```
458
+
459
+ The broker signs the fixed tuple encoded by `hostedWorkerAccessBytes` in the implementation.
460
+ Access must match the admission's identity, resources and epoch, use the admitted broker origin,
461
+ and expire within both admission and sixty seconds of issuance. This executor ends execution at
462
+ expiry; it does not renew credentials. The template's CLI and product environment artifacts must
463
+ match their pinned digests. The product artifact accepts only CLI product metadata variables.
464
+ Provider metadata must match task ownership and the template, with public traffic disabled and
465
+ outbound access limited to the broker hostname and no attached volumes. The resource must already
466
+ be running, with timeout deletion and auto-resume disabled; connecting a paused memory image is
467
+ refused. These checks do not establish measured cloud containment.
468
+
469
+ An owner backend can call `provisionE2BTaskWorker` before issuing admission. It creates the
470
+ operator-selected clean template with closed networking, no worker credentials or volumes, and
471
+ an exclusive new workspace. It returns the resource, workspace root, operation ID, checkpoint
472
+ reference, optional conversation artifact receipt and an idempotent `release()` operation. The management capability stays in this
473
+ owner-side SDK composition. After the receipt, the owner signs current worker/broker admission
474
+ and task access for the returned resource and configures that workspace in the execution input.
475
+
476
+ For recovery, supply an owner-approved `{ id, digest }` reference and the matching UTF-8 JSON bytes
477
+ in `checkpoint`. The manifest has `version: 1`, `id`, the source `identity`, `epoch`, `worker`, and
478
+ `files: [{ path, base64 }]`. Paths are relative regular workspace files; private HOME/TMPDIR and link or memory records
479
+ are not accepted. Owner authority journals stay outside this workspace. The source must belong
480
+ to the same tenant/task/root and precede the current epoch and runtime. File bytes are read back
481
+ and compared before the owner receives a successful result. Treat recovered project content as
482
+ untrusted; current profile, policy and credentials come from the clean operator composition.
483
+ For conversation recovery, use manifest `version: 2` with the same fields and `session` containing
484
+ an existing versioned session-record envelope (`{ schemaVersion, record }`). Only the conversation,
485
+ name, selector and timestamps survive. Saved system messages/prompts, message metadata, tool schemas,
486
+ VM state, goals, background jobs, loops, memory references and branch pointers do not restore;
487
+ current composition rebuilds execution and authority. Display history is rebuilt from the projected
488
+ conversation; saved events are not imported. The record's cwd becomes the fresh workspace.
489
+ Pass the returned `resumeSession: { id, path, digest }` in the private worker execution input and
490
+ invoke `--resume <id>` (or `--continue`, optionally with `--fork-session`). The default executor
491
+ verifies the receipt and copies the projected record into the current product artifact's selected
492
+ private user session store before CLI startup. It refuses an existing destination, mismatched
493
+ selector/digest, malformed record or a user state root inside recovered project files. No saved
494
+ settings, trust decision or credential is imported. A resume without this receipt refuses execution.
495
+
496
+ The default executor accepts this filesystem checkpoint only when signed current admission and
497
+ provider metadata agree on its digest, fresh identity/epoch and source resource. It does not
498
+ restore provider memory snapshots or reconnect the checkpoint source. Setup failures delete
499
+ verified allocations; unknown creation/ownership and unresolved deletion return
500
+ `E2BWorkerProvisioningError` with reconciliation identifiers. The external owner must retain and
501
+ reconcile those outcomes rather than retry or report cleanup as successful.
502
+
503
+ The worker receives a closed environment with private HOME/TMPDIR, product configuration and the
504
+ scoped broker token through the OpenAI-compatible provider interface. Management and upstream
505
+ credentials stay outside the worker. Interactive input uses a PTY; input EOF requests the stock
506
+ CLI's Ctrl-C shutdown. For `--serve`, include `serve: { port, workerPort, token }` in the private
507
+ execution input. `port` selects the runtime's IPv4 loopback ingress (`0` assigns a free port);
508
+ `workerPort` selects the worker daemon port. Generate a fresh, unpredictable owner client `token`
509
+ of 32–256 URL-safe characters and connect to the printed runtime URL with `?token=<token>`.
510
+ The runtime validates the client token, Host and Origin, then carries binary WebSocket traffic
511
+ through an owned provider command to the pinned worker loopback listener. It replaces client
512
+ headers and credentials with a separate per-launch worker token. The owner token and provider
513
+ management capability stay in the runtime. Stopping execution closes the listener and clients
514
+ and deletes the worker. This local ingress does not provide remote desktop pairing or TLS.
515
+ Credential renewal requires broker integration; this executor still ends at its signed access expiry.
516
+
517
+ Embedders can override the installed executor with `hostedRuntimeExecutorFactory`. It receives
518
+ `(admission, signal, invocation)` with immutable captured arguments and returns `run`, `stop` and
519
+ `release`. The adapter owns authenticated communication, input/output and trusted usage reporting;
520
+ stop and release settle only after cleanup. Missing or invalid authority refuses execution without
521
+ entering local host adapters. CLI flags request behavior and never confer company authority.
522
+
523
+ ## Remote desktop owner composition
524
+
525
+ Use `HostedDesktopWorkerPort.connect({ endpoint, token, worker, control })` with the owner's private
526
+ runtime loopback ingress. It discovers and pins the actual worker session; retain this carrier across
527
+ desktop reconnects. Create `HostedDesktopAuthorization` with the public HTTPS URL, pinned issuer,
528
+ current `HostedOrganizationControl`, and owner-selected bindings containing `id`, `user`, `client`,
529
+ the discovered `session`, and the same `worker`. Give `HostedDesktopGateway` the owner's HTTPS server,
530
+ that authorization, a `HostedDesktopAuthorityStore` and a binding-ID-to-worker-port map. The store uses
531
+ a private existing directory and an independently held organization ledger anchor; `create: true` is only
532
+ for first creation. Store loss, rollback or an uncertain anchor acknowledgment refuses access.
533
+
534
+ The issuer signs a short-lived asymmetric JWT with exact `sub`, `client_id`, `tenant`, `task`, `session`,
535
+ `workload` and `epoch` binding claims, unique `jti`, `iat`, and `exp`. Ordinary credentials have the public
536
+ URL as audience and `desktop:pair desktop:drive` scopes. A separate operator credential has
537
+ `<publicUrl>/approval` as audience and `desktop:approve` scope. Credentials expire within two minutes;
538
+ pairing grants at most one minute and never extends current company authority. Reconnect uses new JWTs,
539
+ withdraws the previous connection and preserves the pinned worker session. Issuer/JWK outages and current
540
+ grant withdrawal close active access. `gateway.revoke(bindingId)` is an owner-only withdrawal API.
541
+
542
+ Remote traffic requires TLS and the pinned Host/Origin. TLS termination requires explicitly selected
543
+ proxy IPs with matching HTTPS forwarding headers; arbitrary proxy headers are refused. The renderer
544
+ cannot switch tasks/sessions, execute slash commands, change settings, or approve permissions. The
545
+ main-process operator channel binds Allow once/Deny to the currently pending operation digest and
546
+ rejects replay. Run all authority stores and gateway/control composition outside the task worker.
547
+ These local integration checks do not establish deployed cloud containment or operational latency.
548
+
323
549
  ## Work on the CLI in this repository
324
550
 
325
551
  ```bash
@@ -340,5 +566,5 @@ pnpm cli:trust # trust this repository for the source CLI
340
566
 
341
567
  ## License
342
568
 
343
- Robota is dual-licensed under the [GNU AGPL-3.0](../../LICENSE) or a
569
+ This package is dual-licensed under the [GNU AGPL-3.0](../../LICENSE) or a
344
570
  [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: ' +