warpmetal 0.8.13 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -4
- 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 +54 -1
- package/skills/warpmetal/references/cli-reference.md +150 -2
- package/skills/warpmetal/references/runtime.md +19 -0
- package/src/account-gateway.js +217 -0
- package/src/account-session.js +209 -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 +10 -4
- package/src/cli.js +398 -11
- package/src/contracts/agent-manager-control-v1.schema.json +563 -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/customer-auth.js +329 -0
- package/src/installer.js +5 -5
- package/src/models.js +85 -0
- package/src/runtime.js +76 -3
- package/src/session-handoff.js +915 -0
- package/src/state.js +20 -4
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ integration.
|
|
|
62
62
|
|
|
63
63
|
The repository also contains a skills-only WarpMetal plugin for the public
|
|
64
64
|
Plugins Directory shared by Codex and ChatGPT. The plugin remains a separate
|
|
65
|
-
artifact from the npm CLI and requires `warpmetal` CLI version 0.
|
|
65
|
+
artifact from the npm CLI and requires `warpmetal` CLI version 0.9.0 or newer.
|
|
66
66
|
|
|
67
67
|
To test the repository marketplace after the plugin lands on `main`:
|
|
68
68
|
|
|
@@ -84,9 +84,13 @@ the repo marketplace is only for development, testing, and direct distribution.
|
|
|
84
84
|
## First commands
|
|
85
85
|
|
|
86
86
|
```sh
|
|
87
|
+
warpmetal login
|
|
88
|
+
warpmetal auth status
|
|
87
89
|
warpmetal health
|
|
88
90
|
warpmetal catalog
|
|
91
|
+
warpmetal models --auth-mode chatgpt_subscription
|
|
89
92
|
warpmetal order prepare \
|
|
93
|
+
--account --without-agent-boxes \
|
|
90
94
|
--plan agent \
|
|
91
95
|
--hostname codex-workspace \
|
|
92
96
|
--os '<exact name from warpmetal catalog>' \
|
|
@@ -94,11 +98,54 @@ warpmetal order prepare \
|
|
|
94
98
|
--json
|
|
95
99
|
```
|
|
96
100
|
|
|
101
|
+
`warpmetal login` starts WarpMetal account authorization before any SSH key or
|
|
102
|
+
order configuration is needed. It opens the browser approval page by default;
|
|
103
|
+
use `--no-browser` to print the page and user code without opening it. The
|
|
104
|
+
default session requests `cli:read cli:write`; `--read-only` requests only
|
|
105
|
+
`cli:read`. Check the current account with `warpmetal auth status` and revoke
|
|
106
|
+
the CLI session with `warpmetal logout`.
|
|
107
|
+
|
|
108
|
+
The CLI stores only its own rotating account session under the private
|
|
109
|
+
WarpMetal state directory. Account sessions are separate from browser sessions,
|
|
110
|
+
legacy order owner tokens and SSH credentials, and are bound to the exact
|
|
111
|
+
Identity and account origins that issued them. `--identity-url` and
|
|
112
|
+
`--account-url` accept explicit origins for testing or alternate deployments;
|
|
113
|
+
credentials are never sent after an HTTP redirect or reused for other origins.
|
|
114
|
+
If a refresh response is lost, the CLI clears the local session and requires a
|
|
115
|
+
new login rather than risking reuse of a rotated credential. Logout always
|
|
116
|
+
clears the matching local session, and reports when remote revocation could not
|
|
117
|
+
be confirmed.
|
|
118
|
+
|
|
119
|
+
Use `warpmetal account orders` and `warpmetal account devices` for your account's
|
|
120
|
+
order history and active servers; add `--task <id>` or `--server <id>` for one
|
|
121
|
+
item. `order prepare --account` binds the unpaid order to the signed-in account
|
|
122
|
+
and uses its verified contact. It never issues an owner token. Login and account
|
|
123
|
+
creation do not require SSH keys or order configuration.
|
|
124
|
+
|
|
125
|
+
New account preparation defaults to Agent Boxes and an Agent team. Supply the
|
|
126
|
+
selected team in `--runtime-file`; no provider or model is silently selected.
|
|
127
|
+
Use `--without-team` for a single small persistent box, or
|
|
128
|
+
`--without-agent-boxes` for a VPS without boxes. An explicit runtime file keeps
|
|
129
|
+
its existing selections. `warpmetal models --json` supplies published model and
|
|
130
|
+
authentication choices; server readiness checks still apply.
|
|
131
|
+
|
|
132
|
+
Without `--account`, order preparation retains the existing owner-token
|
|
133
|
+
workflow, including payment and SSH automation. This release adds account order
|
|
134
|
+
preparation and inventory, not a new account payment or runtime-execution grant.
|
|
135
|
+
The account scopes do not authorize charges, renewals, deletion or Fleet access.
|
|
136
|
+
|
|
137
|
+
`warpmetal catalog` lists VPS plans. `warpmetal models` reads the separate,
|
|
138
|
+
public Agent Teams model catalog. It never signs in to a model provider or
|
|
139
|
+
creates an order. Use `--provider` or `--auth-mode api_key|chatgpt_subscription`
|
|
140
|
+
to filter published entries; JSON preserves the catalog snapshot, provenance,
|
|
141
|
+
freshness state and published authentication modes.
|
|
142
|
+
|
|
97
143
|
The generated key defaults to
|
|
98
144
|
`${WARPMETAL_HOME:-~/.config/warpmetal}/ssh/warpmetal-codex-workspace`. A
|
|
99
145
|
collision receives a random suffix; existing keys are never overwritten. Once
|
|
100
146
|
checkout returns `serverId`, the CLI binds that ID to the identity so
|
|
101
|
-
`warpmetal server login`
|
|
147
|
+
`warpmetal server login` is the distinct SSH challenge flow for one server.
|
|
148
|
+
It and `warpmetal runtime install` can select the generated identity without
|
|
102
149
|
an `--identity` flag. Use `--ssh-public-key-file` instead when supplying a
|
|
103
150
|
user-managed public key.
|
|
104
151
|
|
|
@@ -306,6 +353,47 @@ checkout requires the private WarpMetal owner token, so x402api authorizes and
|
|
|
306
353
|
WarpMetal submits. WarpMetal keeps `--payment-signature-file` for another
|
|
307
354
|
compatible external signer.
|
|
308
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
|
+
|
|
309
397
|
## Security boundary
|
|
310
398
|
|
|
311
399
|
- Order and access tokens are never printed; they are written to
|
|
@@ -319,8 +407,9 @@ compatible external signer.
|
|
|
319
407
|
- The CLI writes x402api-compatible request envelopes and accepts validated
|
|
320
408
|
x402api payment artifacts or a compatible external `PAYMENT-SIGNATURE` file.
|
|
321
409
|
Wallet key management and signing remain outside this package.
|
|
322
|
-
- Destructive
|
|
323
|
-
|
|
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.
|
|
324
413
|
- Runtime bootstrap credentials remain memory-only. Manual installation asks
|
|
325
414
|
for one only after the first host key has been pinned and strictly reverified;
|
|
326
415
|
automatic reload bootstrap is rendered directly into provider-bound
|
|
@@ -336,6 +425,11 @@ compatible external signer.
|
|
|
336
425
|
production image. It briefly disconnects active sessions but preserves the
|
|
337
426
|
external workspace, lifetime, and start time. The wait completes only when
|
|
338
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`.
|
|
339
433
|
- Guarded reload powers the server off first. Runtime-enabled reload requires a
|
|
340
434
|
second acknowledgment because all sandbox workspaces are erased. WarpMetal
|
|
341
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.
|
|
3
|
+
"version": "0.9.1",
|
|
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/api.js && node --check src/args.js && node --check src/cli.js && node --check src/connection.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/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"
|
|
@@ -11,7 +11,7 @@ with ad hoc HTTP commands.
|
|
|
11
11
|
|
|
12
12
|
## Start safely
|
|
13
13
|
|
|
14
|
-
1. Run `warpmetal --version` and require version `0.
|
|
14
|
+
1. Run `warpmetal --version` and require version `0.9.0` or newer for this
|
|
15
15
|
plugin. If it is missing or older, explain the compatibility requirement,
|
|
16
16
|
ask before installing or upgrading software, and use only the official npm
|
|
17
17
|
package from `https://www.npmjs.com/package/warpmetal`.
|
|
@@ -44,8 +44,14 @@ Run:
|
|
|
44
44
|
```sh
|
|
45
45
|
warpmetal health --json
|
|
46
46
|
warpmetal catalog --json
|
|
47
|
+
warpmetal models --json
|
|
47
48
|
```
|
|
48
49
|
|
|
50
|
+
`catalog` is the live VPS plan catalog. `models` is the separate public Agent
|
|
51
|
+
Teams model catalog; it makes one read-only request and never infers model
|
|
52
|
+
availability, credentials, entitlement, or provider support. Filter only with
|
|
53
|
+
published `--provider` or `--auth-mode api_key|chatgpt_subscription` values.
|
|
54
|
+
|
|
49
55
|
Stop the current purchase if `purchasingReady` is false. In unattended
|
|
50
56
|
scheduling, recheck after 60 seconds, then double the delay after each failed
|
|
51
57
|
check up to 15 minutes and honor a longer `Retry-After`; never hot-loop. In an
|
|
@@ -350,3 +356,50 @@ If the installed CLI lacks a required runtime command, stop, explain the
|
|
|
350
356
|
version limitation, and ask before upgrading the official npm package. Do not
|
|
351
357
|
reconstruct runtime changes with raw HTTP, ad hoc SSH, Podman, Docker, or host
|
|
352
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.
|