warpmetal 0.9.0 → 0.9.2

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/README.md CHANGED
@@ -353,6 +353,47 @@ checkout requires the private WarpMetal owner token, so x402api authorizes and
353
353
  WarpMetal submits. WarpMetal keeps `--payment-signature-file` for another
354
354
  compatible external signer.
355
355
 
356
+ ## Retained Work and Insights
357
+
358
+ The positional management commands use an existing server owner or SSH login:
359
+
360
+ ```sh
361
+ warpmetal work list SERVER BOX --json
362
+ warpmetal work show SERVER BOX WORK --json
363
+ warpmetal work checkpoint SERVER BOX WORK --file checkpoint.json --json
364
+ warpmetal work status SERVER BOX WORK --kind checkpoint --request REQUEST --json
365
+ warpmetal insights list SERVER BOX --json
366
+ warpmetal insights manager settings SERVER BOX --json
367
+ warpmetal work open SERVER BOX WORK --connection-file sandbox.json --identity /path/to/sandbox-key
368
+ warpmetal insights review SERVER BOX RUN --connection-file sandbox.json --identity /path/to/sandbox-key
369
+ ```
370
+
371
+ Mutations require a closed JSON request with its request ID and exact revision
372
+ fences. The private intent journal saves only opaque IDs, a body digest and
373
+ progress before sending the mutation. Repeating the same intent uses GET-only
374
+ reconciliation; a different body on the same route and request ID is refused.
375
+ Pending operations exit 8, conflicts and terminal failures exit 5. A completed
376
+ continuation or handoff receipt proves task admission; the task outcome remains
377
+ pending.
378
+
379
+ Metadata output excludes private Work content. `work content` deliberately
380
+ returns that text, and `work create`/`update` accept it through the explicit
381
+ input file. Redirects, oversized responses and malformed or foreign authority
382
+ are refused before output. Account login does not authorize these endpoints;
383
+ use `server login` or an existing scoped owner token.
384
+
385
+ `open` and `review` fetch a fresh exact session descriptor. With `--json`, they
386
+ return the descriptor without attaching. Interactive access requires a sandbox
387
+ SSH grant, its pinned connection profile, matching sandbox identity, and local
388
+ OpenCode 2.0.14. The fixed SSH bridge launches no initial prompt or new session.
389
+ Manager review remains read-only; recommendation mode cannot automatically
390
+ steer a worker. Protected takeover and Resume are explicit, revision-checked
391
+ operations.
392
+
393
+ See the complete [Work commands](docs/agent-work-cli.md),
394
+ [Insights and manager commands](docs/agent-insights-cli.md), and
395
+ [session transport](docs/session-handoff-transport.md).
396
+
356
397
  ## Security boundary
357
398
 
358
399
  - Order and access tokens are never printed; they are written to
@@ -366,8 +407,9 @@ compatible external signer.
366
407
  - The CLI writes x402api-compatible request envelopes and accepts validated
367
408
  x402api payment artifacts or a compatible external `PAYMENT-SIGNATURE` file.
368
409
  Wallet key management and signing remain outside this package.
369
- - Destructive or state-changing commands require explicit confirmations and
370
- generate idempotency keys by default.
410
+ - Destructive lifecycle commands require explicit confirmations and generate
411
+ idempotency keys by default. Work and Insights mutations require an explicit
412
+ closed request file with its request ID and revision fences.
371
413
  - Runtime bootstrap credentials remain memory-only. Manual installation asks
372
414
  for one only after the first host key has been pinned and strictly reverified;
373
415
  automatic reload bootstrap is rendered directly into provider-bound
@@ -383,6 +425,11 @@ compatible external signer.
383
425
  production image. It briefly disconnects active sessions but preserves the
384
426
  external workspace, lifetime, and start time. The wait completes only when
385
427
  the observed digest and generation both match the accepted target.
428
+ - `sandbox action --action patch_image --confirm patch_image --image-digest <image@sha256:digest> --wait`
429
+ selects an approved immutable image for one sandbox while retaining its
430
+ incarnation generation. It briefly disconnects active sessions; the wait
431
+ requires the exact requested image digest even when the old container is
432
+ still running at that generation. Other actions refuse `--image-digest`.
386
433
  - Guarded reload powers the server off first. Runtime-enabled reload requires a
387
434
  second acknowledgment because all sandbox workspaces are erased. WarpMetal
388
435
  places the approved signed Runtime bootstrap in reload cloud-init
@@ -0,0 +1,123 @@
1
+ # Agent Insights CLI contract
2
+
3
+ Status: implemented in the reviewed CLI 0.9.1 candidate; publication and deployed qualification remain pending. Commands below use
4
+ the closed, versioned owner contracts in `src/contracts/`; historical
5
+ qualification evidence is retained separately from this source checkpoint.
6
+
7
+ Agent Insights uses the existing server-owner token from `warpmetal server
8
+ login`. It does not fall back to account login, create a sandbox grant, submit
9
+ a prompt, start a provider call, or change manager/takeover state while reading
10
+ or opening a session.
11
+
12
+ ## Commands
13
+
14
+ All resource identifiers are positional. Mutations read a bounded, closed JSON
15
+ object from `--file`; `--idempotency-key` defaults to that object's `requestId`.
16
+
17
+ ```text
18
+ warpmetal insights summary SERVER [--limit N] [--cursor BOX]
19
+ warpmetal insights status SERVER BOX
20
+ warpmetal insights enable SERVER BOX --file FILE [--idempotency-key KEY]
21
+ warpmetal insights disable SERVER BOX --file FILE [--idempotency-key KEY]
22
+ warpmetal insights list SERVER BOX [--limit N] [--cursor FINDING]
23
+ [--state open|resolved] [--session SESSION] [--severity warning]
24
+ [--attention unacknowledged|acknowledged|snoozed|dismissed]
25
+ [--rule RULE] [--recent-hours 1|24|168|720]
26
+ warpmetal insights show SERVER BOX FINDING
27
+ warpmetal insights acknowledge SERVER BOX FINDING --file FILE [--idempotency-key KEY]
28
+ warpmetal insights snooze SERVER BOX FINDING --file FILE [--idempotency-key KEY]
29
+ warpmetal insights dismiss SERVER BOX FINDING --file FILE [--idempotency-key KEY]
30
+ warpmetal insights open SERVER BOX FINDING [--takeover OPERATION]
31
+ [--connection-file FILE] [--identity PATH]
32
+
33
+ warpmetal insights manager settings SERVER BOX [--file FILE] [--idempotency-key KEY]
34
+ warpmetal insights manager activity SERVER BOX [--limit N] [--cursor RUN]
35
+ [--finding FINDING]
36
+ warpmetal insights manager run SERVER BOX RUN
37
+ warpmetal insights manager target SERVER BOX FINDING
38
+ warpmetal insights manager recheck SERVER BOX FINDING --file FILE [--idempotency-key KEY]
39
+ warpmetal insights manager status SERVER BOX FINDING --request REQUEST
40
+
41
+ warpmetal insights takeover list SERVER BOX FINDING [--limit N] [--cursor OPERATION]
42
+ warpmetal insights takeover SERVER BOX FINDING --file FILE [--idempotency-key KEY]
43
+ warpmetal insights takeover status SERVER BOX FINDING --operation OPERATION
44
+ warpmetal insights takeover resume SERVER BOX FINDING --operation OPERATION
45
+ --file FILE [--idempotency-key KEY]
46
+
47
+ warpmetal insights review SERVER BOX RUN [--connection-file FILE] [--identity PATH]
48
+ ```
49
+
50
+ The settings `enable` and `disable` verbs require the canonical Insights
51
+ settings mutation and require `enabled` to match the verb. Finding action verbs
52
+ likewise require the canonical action to match the verb. Manager settings with
53
+ no file is a GET; with a file it is a PATCH. Manager status and takeover status
54
+ are GET-only recovery lookups using the explicit `--request` or `--operation`.
55
+
56
+ The `open --takeover OPERATION` option verifies an existing exact ready pause
57
+ operation. It never creates one. The finding, source, member, policy and hold
58
+ must still be current, and a superseding resume makes the proof stale. If the
59
+ owner API cannot establish those facts, the CLI refuses the handoff. Ordinary
60
+ `open` remains a read-only exact-session lookup.
61
+
62
+ With `--json`, `open` and `review` print only the freshly validated canonical
63
+ session-handoff descriptor and never claim that SSH or a native client opened.
64
+ Without `--json`, they pass that descriptor to the separately owned fixed
65
+ session-handoff launcher. Merely listing, showing, copying, opening or reviewing
66
+ never acknowledges a finding, starts a review, pauses a member, resumes a hold,
67
+ creates a session, or sends a prompt.
68
+
69
+ ## Transport and replay
70
+
71
+ The handler exports:
72
+
73
+ ```text
74
+ async handleInsights(positionals, options, services): number
75
+ ```
76
+
77
+ `services` contains `client`, `store`, `context`, `requireServerToken`, `emit`,
78
+ `readInput`, `runMutation`, and `openSessionHandoff`. Authentication calls
79
+ `requireServerToken(store, serverId, options, context.env)`. HTTP uses
80
+ `client.request(method, path, {token, body, idempotencyKey})`.
81
+
82
+ Every mutation calls `runMutation` with the fixed exact API `path`, server and
83
+ sandbox scope, mutation kind, validated request ID/body, and submit/reconcile
84
+ callbacks. The journal is written before the first request. Repeating the same
85
+ intent performs recovery with GET only; a changed body conflicts locally.
86
+ Settings and finding actions reconcile through their read projections. Manager
87
+ Recheck uses the request lookup route. Takeover and Resume use the exact
88
+ operation lookup routes.
89
+
90
+ Mutable-resource reconciliation succeeds only when the current GET still
91
+ proves the saved request. Settings and attention require exactly the requested
92
+ revision plus one and the requested state. Manager policy additionally requires
93
+ the requested rules and limits; its public expiry and update timestamps must
94
+ still prove the requested authorization duration. Runtime policy acknowledgement
95
+ and manifest renewal can change those timestamps without changing the policy
96
+ revision, so a later GET may conservatively return an unknown outcome even when
97
+ the currently displayed policy is otherwise useful. `manager settings` without
98
+ `--file` remains the way to inspect that current state without claiming recovery
99
+ of an older mutation.
100
+
101
+ A snooze deadline is derived from the backend clock and its public finding does
102
+ not include the mutation timestamp. The initial POST receipt can prove the next
103
+ attention revision, snoozed state and presence of the server-derived deadline;
104
+ a GET-only retry cannot prove the originally requested duration and therefore
105
+ fails closed. The CLI never retries the POST. Recheck, Takeover and Resume bind
106
+ their public receipts to every request-carried revision, source, target and
107
+ budget field that the backend projects. Resume also reads and verifies the exact
108
+ predecessor policy and hold tuple before submission or recovery.
109
+
110
+ The handler consumes unchanged owner projection modules:
111
+
112
+ - `src/contracts/insights.js`: Insights query, mutation and response projectors;
113
+ - `src/contracts/manager.js`: manager query, mutation and response projectors;
114
+ - `src/contracts/session-handoff.js`: exact-session envelope projector.
115
+
116
+ Unknown options, surplus positionals, malformed files, action/verb mismatch and
117
+ invalid queries fail before any network request. Unknown or expanded server
118
+ responses fail closed and are never printed. Input files are capped at the
119
+ shared mutation limit and raw provider text, credentials, prompts and paths do
120
+ not appear in output.
121
+
122
+ Accepted/pending work exits 8. A terminal recommendation or `no_action` exits 0; `no_action` preserves its actual closed state and safe outcome instead of claiming a recommendation. Terminal failures exit 5. A completed review that requires owner attention retains `needs_owner`; opening its exact valid proposal-bearing session remains read-only. Authentication failures retain exit 4, request/transport/contract
123
+ failures retain the shared CLI codes, and syntax/input errors exit 2.
@@ -0,0 +1,72 @@
1
+ # Retained Work CLI
2
+
3
+ The Work commands use the existing server owner or short-lived owner SSH token.
4
+ They never fall back to the account session and never accept a bearer token on
5
+ the command line.
6
+
7
+ ## Command grammar
8
+
9
+ Commands are positional:
10
+
11
+ ```text
12
+ warpmetal work list SERVER BOX [--cursor CURSOR] [--limit N]
13
+ warpmetal work show SERVER BOX WORK
14
+ warpmetal work content SERVER BOX WORK [--revision N]
15
+ warpmetal work sources SERVER BOX [--cursor CURSOR] [--limit N]
16
+ warpmetal work policy SERVER BOX
17
+ warpmetal work create SERVER BOX --file REQUEST.json [--idempotency-key KEY]
18
+ warpmetal work update SERVER BOX WORK --file REQUEST.json [--idempotency-key KEY]
19
+ warpmetal work enable SERVER BOX WORK --file REQUEST.json [--idempotency-key KEY]
20
+ warpmetal work checkpoint SERVER BOX WORK --file REQUEST.json [--idempotency-key KEY]
21
+ warpmetal work continue SERVER BOX WORK --file REQUEST.json [--idempotency-key KEY]
22
+ warpmetal work restore SERVER BOX WORK --file REQUEST.json [--idempotency-key KEY]
23
+ warpmetal work handoff SERVER BOX WORK --file REQUEST.json [--idempotency-key KEY]
24
+ warpmetal work targets SERVER BOX WORK
25
+ warpmetal work status SERVER BOX WORK --kind checkpoint|continue|restore|handoff
26
+ (--request REQUEST | --operation OPERATION)
27
+ warpmetal work open SERVER BOX WORK [--connection-file FILE] [--identity FILE]
28
+ ```
29
+
30
+ The common `--base-url`, `--state-dir`, `--token-file`, `--json`, and `--help`
31
+ options retain their existing meanings. `open --json` fetches and prints a
32
+ fresh validated handoff descriptor without starting SSH. Non-JSON `open`
33
+ passes the same validated descriptor to the existing grant-bound session
34
+ bridge; it cannot choose a path, shell command, native project, or session.
35
+
36
+ ## Closed input and output
37
+
38
+ Every mutation reads one explicit JSON object from `--file`. Create and update
39
+ permit at most 2 MiB of encoded JSON because those are the only deliberate
40
+ private Work-content writes. Other mutation inputs are limited to 64 KiB. The
41
+ canonical projectors reject unknown fields, invalid identifiers, stale or
42
+ missing revision fences, invalid task pairs, unsupported target modes, and
43
+ oversized content before a network request.
44
+
45
+ The idempotency key defaults to the request body's exact `requestId`. An
46
+ explicit `--idempotency-key` changes only the HTTP idempotency header. The
47
+ private mutation journal binds origin, fixed API path, request ID, and body
48
+ digest before the first POST. An ambiguous later invocation of the same intent
49
+ uses only the command's fixed GET reconciliation route. It never resubmits the
50
+ mutation or changes its body. A changed body under the same origin, fixed path,
51
+ and saved request ID is a local conflict. A distinct fixed path has its own
52
+ journal record.
53
+
54
+ Metadata commands never print objective, constraints, context, credentials,
55
+ host paths, native workspace directories, or raw Runtime evidence. `content`
56
+ is the deliberate private-text read and `create`/`update` are the deliberate
57
+ private-text writes. All server responses pass the reviewed closed Work,
58
+ continuation, handoff, or session-handoff projector before output.
59
+
60
+ ## Operation outcomes
61
+
62
+ A pending, reported, or outcome-unknown operation exits 8. Failed or
63
+ superseded operations exit 5. A validated terminal `accepted` checkpoint exits
64
+ 0 only with its checkpoint receipt; restore exits 0 only with its materialized
65
+ target; continuation and handoff exit 0 only with their admitted task/baseline
66
+ receipt. Human output says `checkpoint saved`, `workspace restored`,
67
+ `continuation admitted; task outcome pending`, or
68
+ `handoff admitted; task outcome pending`; admission never claims the task
69
+ finished.
70
+
71
+ `status` is GET-only and requires exactly one saved request ID or operation ID.
72
+ It cannot create or replay an operation.
@@ -0,0 +1,68 @@
1
+ # Exact managed-session handoff transport
2
+
3
+ Status: bounded transport implementation complete locally on 2026-09-27. Domain CLI dispatch and live native proof remain separate gates.
4
+
5
+ The reusable Agent Kit boundary is:
6
+
7
+ ```js
8
+ openSessionHandoff(descriptor, {
9
+ connectionFile,
10
+ identityPath,
11
+ context,
12
+ clientPath,
13
+ }) -> Promise<number>
14
+ ```
15
+
16
+ `descriptor` is the complete backend `AgentSessionHandoffEnvelope`. The function returns the local native client's numeric exit code. It throws a `CliError` with a closed `session_handoff_*` code when validation, SSH, framing, handshake, or relay setup fails. `connectionFile` and `identityPath` are explicit first-release prerequisites. `clientPath` defaults to `opencode`. `context` supplies bounded clock/environment/process seams for the real child-process journey; production defaults use the current clock, process environment, Node child processes, and cryptographic randomness.
17
+
18
+ The domain CLI owns API lookup and command dispatch. Its JSON mode validates and prints the fresh backend descriptor without calling this function, starting SSH, or claiming a native attachment. A successful `HELLO_ACK` proves only that the bridge accepted the exact target. The connector does not claim that the native TUI rendered or attached to the conversation.
19
+
20
+ ## Validation and transport
21
+
22
+ The connector accepts only the closed v1 envelope with `capability: exact_session`, `reason: null`, all three required access booleans true, `transport: wm-team-control/1`, and a non-null closed handoff target. The target must be fresh at the local clock and its issuance window must be no longer than 120 seconds. Its server and sandbox IDs must exactly match the version-1 sandbox connection profile. Unknown fields, raw path/URL/credential overrides, malformed IDs, invalid revisions, mixed task/Work shapes, expired descriptors, and profile mismatches fail before SSH starts.
23
+
24
+ The sandbox identity must be an explicit regular private file. The connector never reads or exports its contents. It writes a private temporary `known_hosts` file from the validated connection profile and executes only:
25
+
26
+ ```text
27
+ ssh -T -i <identity> \
28
+ -F <private empty config> \
29
+ -o BatchMode=yes -o PasswordAuthentication=no \
30
+ -o KbdInteractiveAuthentication=no \
31
+ -o IdentitiesOnly=yes \
32
+ -o StrictHostKeyChecking=yes \
33
+ -o UserKnownHostsFile=<private temporary file> \
34
+ -o ClearAllForwardings=yes -o ForwardAgent=no -o ForwardX11=no \
35
+ -o PermitLocalCommand=no -o RequestTTY=no \
36
+ -o ControlMaster=no -o ProxyCommand=none -o ProxyJump=none \
37
+ -p <profile port> warpmetal-sandbox@<profile host> \
38
+ warpmetal-team-control
39
+ ```
40
+
41
+ There is no shell, arbitrary remote command, PTY fallback, management identity, agent forwarding, X11 forwarding, or SSH port forwarding.
42
+
43
+ The first frame is the bounded `wm-team-control/1` `HELLO`:
44
+
45
+ ```json
46
+ {"v":1,"protocol":"wm-team-control/1","handoff":"<exact descriptor.handoff>"}
47
+ ```
48
+
49
+ No local native client starts until a bounded-time `HELLO_ACK` repeats the protocol and exact handoff receipt/target byte-equivalently as parsed JSON. The acknowledgement's box identity must also repeat the target server, sandbox, sandbox generation and instance; its host-key fingerprint must be one of the explicit profile pins. Runtime derives the private bridge grant ID as `"grt_" + sha256(profile.grantId + NUL + handoffId)[0:24 hex]`; the connector independently derives and requires that exact value, preserving the distinction between the public `grant_*` access-grant ID and the private `grt_*` bridge grant. The acknowledgement's engine fields are advisory and are never used to open a direct connection. A changed target, box, derived grant, host key, missing receipt, error, close, malformed frame, oversized frame, EOF, timeout, or SSH exit fails without launching OpenCode.
50
+
51
+ ## Local native client
52
+
53
+ After the exact acknowledgement, the connector binds one ephemeral HTTP server to `127.0.0.1`. It generates a process-local password, requires HTTP Basic `opencode:<password>` on every request, removes that credential before framing, and passes it only to the owned native child as `OPENCODE_PASSWORD`. This is the pinned 2.0.14 client-side credential variable; `OPENCODE_SERVER_PASSWORD` is for the server and is removed from the local client environment. WarpMetal owner/access tokens and credential variables are also removed. It sets `OPENCODE_DISABLE_AUTOUPDATE=1` and launches exactly:
54
+
55
+ ```text
56
+ opencode --server http://127.0.0.1:<ephemeral-port> \
57
+ --session <descriptor.handoff.source.nativeSessionId>
58
+ ```
59
+
60
+ It adds no project path, prompt, continue, role, agent, provider, standalone, or model argument. The bridge owns native service authentication; neither native credentials nor the local relay password cross SSH.
61
+
62
+ The relay supports bounded HTTP and SSE over request, response-head, data, end, cancel, ping/pong, error, and close frames. It rejects upgrades, non-loopback Host/Origin values, bodies over 1 MiB, more than 32 in-flight requests, response frames over 8 MiB, and unrecognized frames. Manager-review mutation remains denied by the trusted sandbox bridge; the connector preserves the bridge's `team_helper_manager_review_read_only` refusal as an HTTP 403. Ordinary worker owner input can pass only as an explicit request made by the native client and remains subject to the bridge's exact-session revalidation. Opening itself emits no prompt or session creation request.
63
+
64
+ When the native client exits, the connector sends one close frame, closes its relay, ends the owned SSH process, removes temporary host pins, clears its local password reference, and returns the client exit code. SSH loss tears down only connector-owned processes. It never stops or restarts the managed service or worker.
65
+
66
+ ## Test boundary
67
+
68
+ `test/session-handoff-e2e.test.js` uses the unchanged backend fixture at `test/fixtures/agent-session-handoff-v1.backend-wire.fixture.json` (SHA-256 `75934b615896344032bc50157756264030bae87be5de6ceafd299d95f703aa56`). It starts actual fake SSH and native-client child processes plus the connector's real loopback HTTP/SSE listener. The journey proves exact HELLO/ACK order, strict SSH arguments and host pins, local Basic authentication, credential stripping, exact native arguments, GET/SSE transport, zero open-time prompt/create requests, pre-SSH target refusals, changed protocol/target/box/derived-grant/host-key refusal before client launch, bridge-loss cleanup, and manager-review mutation refusal.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "warpmetal",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Agent-safe CLI and skill for purchasing, renewing, and managing WarpMetal VPS servers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,13 +10,15 @@
10
10
  "bin/",
11
11
  "src/",
12
12
  "skills/warpmetal/",
13
- "README.md"
13
+ "README.md",
14
+ "docs/agent-*.md",
15
+ "docs/session-handoff-transport.md"
14
16
  ],
15
17
  "engines": {
16
18
  "node": ">=20.0.0"
17
19
  },
18
20
  "scripts": {
19
- "check": "node --check bin/warpmetal.js && node --check src/account-gateway.js && node --check src/account-session.js && node --check src/api.js && node --check src/args.js && node --check src/cli.js && node --check src/connection.js && node --check src/customer-auth.js && node --check src/errors.js && node --check src/host-trust.js && node --check src/install-skill.js && node --check src/installer.js && node --check src/models.js && node --check src/payment.js && node --check src/runtime.js && node --check src/ssh-alias.js && node --check src/ssh.js && node --check src/state.js && node --check src/version.js",
21
+ "check": "node --check bin/warpmetal.js && node --check src/account-gateway.js && node --check src/account-session.js && node --check src/api.js && node --check src/args.js && node --check src/cli.js && node --check src/connection.js && node --check src/customer-auth.js && node --check src/errors.js && node --check src/host-trust.js && node --check src/install-skill.js && node --check src/installer.js && node --check src/models.js && node --check src/payment.js && node --check src/runtime.js && node --check src/ssh-alias.js && node --check src/ssh.js && node --check src/state.js && node --check src/version.js && node --check src/agent-management.js && node --check src/agent-work.js && node --check src/agent-insights.js && node --check src/session-handoff.js && node --check src/contracts/work.js && node --check src/contracts/work-handoff.js && node --check src/contracts/insights.js && node --check src/contracts/manager.js && node --check src/contracts/session-handoff.js",
20
22
  "plugin:check": "node --test test/plugin.test.js",
21
23
  "test": "node --test",
22
24
  "prepack": "npm run check && npm test"
@@ -356,3 +356,50 @@ If the installed CLI lacks a required runtime command, stop, explain the
356
356
  version limitation, and ask before upgrading the official npm package. Do not
357
357
  reconstruct runtime changes with raw HTTP, ad hoc SSH, Podman, Docker, or host
358
358
  configuration commands.
359
+
360
+ ## Retained Work and Insights
361
+
362
+ The source candidate adds positional Work, Insights, manager and exact-session
363
+ commands. Confirm their presence with `warpmetal --help` before use. They
364
+ require an existing server owner or SSH login; the account login session does
365
+ not authorize these routes. Keep `--json` for agent-driven metadata and status
366
+ commands.
367
+
368
+ ```sh
369
+ warpmetal work list SERVER BOX --json
370
+ warpmetal work show SERVER BOX WORK --json
371
+ warpmetal work status SERVER BOX WORK --kind checkpoint --request REQUEST --json
372
+ warpmetal insights list SERVER BOX --json
373
+ warpmetal insights manager settings SERVER BOX --json
374
+ warpmetal insights manager status SERVER BOX FINDING --request REQUEST --json
375
+ ```
376
+
377
+ Mutation requests use `--file` with a closed JSON object containing its saved
378
+ request ID and exact revision fences. Persist the owner's intended request
379
+ before invoking a mutation. The CLI journals only opaque IDs and a body digest
380
+ before dispatch. Repeating the same origin, route and request ID performs
381
+ GET-only reconciliation. Never replace the request ID to work around an
382
+ ambiguous response, stale revision or conflict. Pending operations exit 8;
383
+ conflicts or terminal failures exit 5. Accepted continuation/handoff proves
384
+ admission, while the task outcome remains pending.
385
+
386
+ Ordinary output is metadata. `work content` deliberately reads private owner
387
+ text; `work create` and `work update` deliberately write it from the explicit
388
+ request file. Do not copy private content into evidence, logs or unrelated
389
+ commands.
390
+
391
+ `work open SERVER BOX WORK --json`, `insights open SERVER BOX FINDING --json`
392
+ and `insights review SERVER BOX RUN --json` return a fresh validated descriptor
393
+ without attaching. Human interactive mode needs `--connection-file` plus
394
+ `--identity` for the selected sandbox grant, and local OpenCode 2.0.14. It uses a
395
+ fixed, pinned SSH bridge and exact session ID, creates no session and sends no
396
+ initial prompt. Do not substitute the VPS owner identity, raw host paths or a
397
+ fallback session. Manager review is read-only. Recommend mode has no automatic
398
+ worker steering; protected takeover and explicit Resume require the current
399
+ operation, policy and hold revisions.
400
+
401
+ Complete schemas and grammar accompany the candidate package in
402
+ `docs/agent-work-cli.md`, `docs/agent-insights-cli.md` and
403
+ `docs/session-handoff-transport.md`. These commands require the matching
404
+ candidate control plane, Runtime and Sandbox; CLI availability alone does not
405
+ prove that the server supports them.
@@ -10,6 +10,7 @@
10
10
  - Server management
11
11
  - Agent Runtime and sandboxes
12
12
  - Per-agent access
13
+ - Retained Work and Insights (source candidate)
13
14
  - Skill installation and state
14
15
  - Exit codes
15
16
 
@@ -342,8 +343,8 @@ warpmetal sandbox list --server <serverId> --json
342
343
  warpmetal sandbox get --server <serverId> --sandbox <sandboxId> [--wait] --json
343
344
  warpmetal sandbox action \
344
345
  --server <serverId> --sandbox <sandboxId> \
345
- --action <start|stop|restart|make_persistent|refresh_image> --confirm <same-action> \
346
- [--wait] --json
346
+ --action <start|stop|restart|make_persistent|refresh_image|patch_image> --confirm <same-action> \
347
+ [--image-digest <image@sha256:digest>] [--wait] --json
347
348
  warpmetal sandbox delete \
348
349
  --server <serverId> --sandbox <sandboxId> --confirm DELETE [--wait] --json
349
350
 
@@ -411,6 +412,57 @@ Every selection must reference a sandbox name in the same file. Unknown fields,
411
412
  including URL, shell, command, argv, environment, or artifact overrides, are
412
413
  rejected before an API request.
413
414
 
415
+ ## Retained Work and Insights (source candidate)
416
+
417
+ Discover command availability with `warpmetal --help`; these routes require
418
+ the corresponding candidate control plane, Runtime and Sandbox.
419
+
420
+ ```sh
421
+ warpmetal work list|sources|policy SERVER BOX --json
422
+ warpmetal work show|content|targets SERVER BOX WORK --json
423
+ warpmetal work create SERVER BOX --file request.json --json
424
+ warpmetal work update|enable|checkpoint|continue|restore|handoff SERVER BOX WORK --file request.json --json
425
+ warpmetal work status SERVER BOX WORK --kind checkpoint|continue|restore|handoff \
426
+ (--request REQUEST | --operation OPERATION) --json
427
+ warpmetal work open SERVER BOX WORK --json
428
+ warpmetal insights summary SERVER --json
429
+ warpmetal insights status|list SERVER BOX --json
430
+ warpmetal insights enable|disable SERVER BOX --file request.json --json
431
+ warpmetal insights show SERVER BOX FINDING --json
432
+ warpmetal insights acknowledge|snooze|dismiss SERVER BOX FINDING --file request.json --json
433
+ warpmetal insights open SERVER BOX FINDING [--takeover OPERATION] --json
434
+ warpmetal insights manager settings SERVER BOX [--file request.json] --json
435
+ warpmetal insights manager activity SERVER BOX [--finding FINDING] --json
436
+ warpmetal insights manager run SERVER BOX RUN --json
437
+ warpmetal insights manager target SERVER BOX FINDING --json
438
+ warpmetal insights manager recheck SERVER BOX FINDING --file request.json --json
439
+ warpmetal insights manager status SERVER BOX FINDING --request REQUEST --json
440
+ warpmetal insights takeover list SERVER BOX FINDING --json
441
+ warpmetal insights takeover SERVER BOX FINDING --file request.json --json
442
+ warpmetal insights takeover status SERVER BOX FINDING --operation OPERATION --json
443
+ warpmetal insights takeover resume SERVER BOX FINDING --operation OPERATION --file request.json --json
444
+ warpmetal insights review SERVER BOX RUN --json
445
+ ```
446
+
447
+ Use the server's existing scoped owner/SSH credential. The account session is
448
+ not a fallback. `--file` mutations validate a closed request with an explicit
449
+ request ID and revision fences before HTTP. Saved same-intent retries are
450
+ GET-only. Preserve the original request after a lost reply; never generate a
451
+ new request to bypass an uncertain outcome. Pending operations exit 8, terminal
452
+ failures/conflicts exit 5. Accepted Continue/handoff means task admission, not
453
+ completion. Check task state separately.
454
+
455
+ Only `work content` deliberately prints private content. Metadata and receipts
456
+ are closed projections. Do not log private request files or content output.
457
+
458
+ `open`/`review --json` returns a fresh descriptor without SSH. For deliberate
459
+ human interactive access, omit `--json` and supply absolute
460
+ `--connection-file` and `--identity` paths for the exact sandbox grant. Local
461
+ OpenCode 2.0.14 connects through the fixed pinned bridge to the exact existing
462
+ session. The command creates no session and sends no initial prompt. Manager
463
+ review cannot mutate the worker. Takeover checks the current protected hold;
464
+ Resume requires its saved operation, predecessor and revision tuple.
465
+
414
466
  ## Per-agent access
415
467
 
416
468
  ```sh
@@ -183,6 +183,25 @@ workspace, sandbox lifetime, and original start time. `--wait` requires both
183
183
  the observed digest and generation to match the accepted target. A change to
184
184
  the global production image does not refresh existing sandboxes implicitly.
185
185
 
186
+ To replace one sandbox's image while keeping its incarnation generation, use
187
+ an explicitly approved digest-pinned image reference:
188
+
189
+ ```sh
190
+ warpmetal sandbox action \
191
+ --server <serverId> \
192
+ --sandbox <sandboxId> \
193
+ --action patch_image \
194
+ --confirm patch_image \
195
+ --image-digest <registry/image@sha256:64-lowercase-hex-digest> \
196
+ --wait \
197
+ --json
198
+ ```
199
+
200
+ This also briefly disconnects active sessions. The reference must contain an
201
+ immutable SHA-256 digest; tags alone are refused. `--image-digest` is supported
202
+ only for `patch_image`. Its wait requires the exact requested observed digest
203
+ and the accepted generation, so a still-running old container is not completion.
204
+
186
205
  Manual deletion is irreversible:
187
206
 
188
207
  ```sh