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 +49 -2
- package/docs/agent-insights-cli.md +123 -0
- package/docs/agent-work-cli.md +72 -0
- package/docs/session-handoff-transport.md +68 -0
- package/package.json +5 -3
- package/skills/warpmetal/SKILL.md +47 -0
- package/skills/warpmetal/references/cli-reference.md +54 -2
- package/skills/warpmetal/references/runtime.md +19 -0
- package/src/agent-insights.js +638 -0
- package/src/agent-management.js +146 -0
- package/src/agent-work.js +800 -0
- package/src/api.js +6 -4
- package/src/cli.js +90 -6
- package/src/contracts/agent-manager-control-v1.schema.json +565 -0
- package/src/contracts/http.js +3 -0
- package/src/contracts/insights.js +133 -0
- package/src/contracts/manager.js +86 -0
- package/src/contracts/session-handoff.js +49 -0
- package/src/contracts/sources.json +12 -0
- package/src/contracts/work-handoff.js +73 -0
- package/src/contracts/work.js +310 -0
- package/src/installer.js +5 -5
- package/src/session-handoff.js +915 -0
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
|
|
370
|
-
|
|
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.
|
|
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
|