agent-embassy 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/CONTRIBUTING.md +51 -47
  3. package/README.md +26 -9
  4. package/README.zh-CN.md +22 -9
  5. package/SECURITY.md +48 -35
  6. package/dist/src/gateway/acp-client.d.ts +12 -19
  7. package/dist/src/gateway/acp-client.js +90 -65
  8. package/dist/src/gateway/acp-client.js.map +1 -1
  9. package/dist/src/gateway/acp-provider.d.ts +11 -10
  10. package/dist/src/gateway/acp-provider.js +165 -53
  11. package/dist/src/gateway/acp-provider.js.map +1 -1
  12. package/dist/src/gateway/claude-helper-client.d.ts +2 -43
  13. package/dist/src/gateway/claude-helper-client.js +1 -211
  14. package/dist/src/gateway/claude-helper-client.js.map +1 -1
  15. package/dist/src/gateway/claude-helper-protocol.d.ts +16 -74
  16. package/dist/src/gateway/claude-helper-protocol.js +99 -378
  17. package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
  18. package/dist/src/gateway/claude-helper-supervisor.d.ts +56 -44
  19. package/dist/src/gateway/claude-helper-supervisor.js +265 -456
  20. package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
  21. package/dist/src/gateway/claude-helper.js +184 -210
  22. package/dist/src/gateway/claude-helper.js.map +1 -1
  23. package/dist/src/gateway/claude-peer.d.ts +20 -87
  24. package/dist/src/gateway/claude-peer.js +310 -1009
  25. package/dist/src/gateway/claude-peer.js.map +1 -1
  26. package/dist/src/gateway/claude-runtime.d.ts +13 -15
  27. package/dist/src/gateway/claude-runtime.js +14 -168
  28. package/dist/src/gateway/claude-runtime.js.map +1 -1
  29. package/dist/src/gateway/cli-copy.en.d.ts +2 -2
  30. package/dist/src/gateway/cli-copy.en.js +4 -2
  31. package/dist/src/gateway/cli-copy.en.js.map +1 -1
  32. package/dist/src/gateway/cli-copy.zh-CN.d.ts +2 -2
  33. package/dist/src/gateway/cli-copy.zh-CN.js +4 -2
  34. package/dist/src/gateway/cli-copy.zh-CN.js.map +1 -1
  35. package/dist/src/gateway/cli.d.ts +7 -2
  36. package/dist/src/gateway/cli.js +277 -575
  37. package/dist/src/gateway/cli.js.map +1 -1
  38. package/dist/src/gateway/codex-app-server.d.ts +5 -227
  39. package/dist/src/gateway/codex-app-server.js +18 -1463
  40. package/dist/src/gateway/codex-app-server.js.map +1 -1
  41. package/dist/src/gateway/codex-doctor.d.ts +36 -0
  42. package/dist/src/gateway/codex-doctor.js +127 -0
  43. package/dist/src/gateway/codex-doctor.js.map +1 -0
  44. package/dist/src/gateway/codex-local-transport.d.ts +6 -6
  45. package/dist/src/gateway/codex-local-transport.js +13 -10
  46. package/dist/src/gateway/codex-local-transport.js.map +1 -1
  47. package/dist/src/gateway/codex-stateless-transport.d.ts +114 -0
  48. package/dist/src/gateway/codex-stateless-transport.js +1108 -0
  49. package/dist/src/gateway/codex-stateless-transport.js.map +1 -0
  50. package/dist/src/gateway/config.d.ts +2 -9
  51. package/dist/src/gateway/config.js +62 -100
  52. package/dist/src/gateway/config.js.map +1 -1
  53. package/dist/src/gateway/control.d.ts +36 -90
  54. package/dist/src/gateway/control.js +465 -1123
  55. package/dist/src/gateway/control.js.map +1 -1
  56. package/dist/src/gateway/dashboard-copy.d.ts +1 -1
  57. package/dist/src/gateway/dashboard-copy.en.d.ts +8 -26
  58. package/dist/src/gateway/dashboard-copy.en.js +19 -37
  59. package/dist/src/gateway/dashboard-copy.en.js.map +1 -1
  60. package/dist/src/gateway/dashboard-copy.js +8 -26
  61. package/dist/src/gateway/dashboard-copy.js.map +1 -1
  62. package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +8 -26
  63. package/dist/src/gateway/dashboard-copy.zh-CN.js +19 -37
  64. package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -1
  65. package/dist/src/gateway/dashboard-model.d.ts +8 -22
  66. package/dist/src/gateway/dashboard-model.js +44 -130
  67. package/dist/src/gateway/dashboard-model.js.map +1 -1
  68. package/dist/src/gateway/dashboard.d.ts +0 -6
  69. package/dist/src/gateway/dashboard.js +3 -7
  70. package/dist/src/gateway/dashboard.js.map +1 -1
  71. package/dist/src/gateway/live-dashboard-app/app.js +20 -34
  72. package/dist/src/gateway/live-dashboard-command.js +2 -2
  73. package/dist/src/gateway/live-dashboard-command.js.map +1 -1
  74. package/dist/src/gateway/live-dashboard-http.d.ts +1 -1
  75. package/dist/src/gateway/live-dashboard-http.js +2 -2
  76. package/dist/src/gateway/live-dashboard-http.js.map +1 -1
  77. package/dist/src/gateway/progress-watch-machine.d.ts +1 -37
  78. package/dist/src/gateway/progress-watch-machine.js +4 -15
  79. package/dist/src/gateway/progress-watch-machine.js.map +1 -1
  80. package/dist/src/gateway/providers.d.ts +58 -191
  81. package/dist/src/gateway/providers.js +637 -2434
  82. package/dist/src/gateway/providers.js.map +1 -1
  83. package/dist/src/gateway/server.d.ts +16 -25
  84. package/dist/src/gateway/server.js +165 -240
  85. package/dist/src/gateway/server.js.map +1 -1
  86. package/dist/src/gateway/service.d.ts +149 -427
  87. package/dist/src/gateway/service.js +1639 -5662
  88. package/dist/src/gateway/service.js.map +1 -1
  89. package/dist/src/gateway/state-v2-to-v3.d.ts +23 -0
  90. package/dist/src/gateway/state-v2-to-v3.js +994 -0
  91. package/dist/src/gateway/state-v2-to-v3.js.map +1 -0
  92. package/dist/src/gateway/store.d.ts +54 -309
  93. package/dist/src/gateway/store.js +1560 -3510
  94. package/dist/src/gateway/store.js.map +1 -1
  95. package/dist/src/gateway/types.d.ts +181 -225
  96. package/dist/src/gateway/types.js +115 -251
  97. package/dist/src/gateway/types.js.map +1 -1
  98. package/docs/CONFIGURATION.md +22 -9
  99. package/docs/CONFIGURATION.zh-CN.md +21 -9
  100. package/docs/DASHBOARD.md +10 -10
  101. package/docs/DASHBOARD.zh-CN.md +3 -3
  102. package/docs/DELIVERY.md +4 -4
  103. package/docs/DELIVERY.zh-CN.md +4 -4
  104. package/docs/GATEWAY-ARCHITECTURE.md +158 -187
  105. package/package.json +1 -1
  106. package/skills/embassy-peer/SKILL.md +28 -22
  107. package/dist/src/gateway/codex-registration-generation.d.ts +0 -5
  108. package/dist/src/gateway/codex-registration-generation.js +0 -19
  109. package/dist/src/gateway/codex-registration-generation.js.map +0 -1
  110. package/dist/src/gateway/codex-registration-succession.d.ts +0 -209
  111. package/dist/src/gateway/codex-registration-succession.js +0 -588
  112. package/dist/src/gateway/codex-registration-succession.js.map +0 -1
  113. package/dist/src/gateway/compatibility.d.ts +0 -22
  114. package/dist/src/gateway/compatibility.js +0 -23
  115. package/dist/src/gateway/compatibility.js.map +0 -1
  116. package/dist/src/gateway/delivery-machine.d.ts +0 -235
  117. package/dist/src/gateway/delivery-machine.js +0 -540
  118. package/dist/src/gateway/delivery-machine.js.map +0 -1
@@ -31,8 +31,8 @@ either agent runtime.
31
31
 
32
32
  Provider versions are best-effort diagnostic metadata, never routing authority.
33
33
  An explicit pair plus the exact owned route and session identity authorizes an
34
- attempt; current connector, generation, strict wire, capability, and correlated
35
- operation facts decide its result. The release-owned offline support matrix is
34
+ attempt; the current per-operation transport, strict wire, capability, and
35
+ correlated operation facts decide its result. The release-owned offline support matrix is
36
36
  the tested-artifact record and is never imported by runtime. Unsafe OS evidence
37
37
  for Embassy-owned or executed artifacts and Embassy callback, control, or state
38
38
  paths refuses broker startup; unsafe UID or mode evidence on Claude's external
@@ -66,7 +66,7 @@ Claude–Codex address book the user wants.
66
66
 
67
67
  The repo-shipped skill is the lightweight user/agent interface. A persistent
68
68
  local broker is still required to own the private control socket, retain
69
- transient reply correlation, watch endpoint generations, queue while a Codex
69
+ transient reply correlation, publish native advertisements, queue while a Codex
70
70
  task is busy, and regenerate the dashboard between agent turns. The skill does
71
71
  not wrap, replace, or recreate either provider.
72
72
 
@@ -169,17 +169,17 @@ The status below is intentionally narrower than the target architecture.
169
169
 
170
170
  | Component | Current evidence |
171
171
  | --- | --- |
172
- | Neutral gateway types, metadata store, route fencing, bounded queues, dedupe, rate limits, and public projection | **Implemented**, deterministic tests; message bodies persist under bounded retention |
172
+ | Neutral gateway types, native-v3 metadata store, bounded attempt state machine, queues, dedupe, rate limits, and public projection | **Implemented**, deterministic tests; message bodies persist under bounded retention |
173
173
  | Private JSONL control protocol over a controller-owned UDS | **Implemented**, deterministic synthetic tests; no provider connection required |
174
174
  | Static metadata-only dashboard renderer and atomic publisher | **Implemented**, deterministic security tests; the static renderer requires no browser or HTTP server |
175
175
  | Opt-in live dashboard companion (`embassy dashboard --live`) | **Implemented**, deterministic tests over the stable loopback listener, direct multi-browser access, projection, request guards, and four bounded route actions; it is a separate foreground process, never part of `embassy serve` |
176
176
  | Claude registry/peer adapter with strict peer protocol 1 and per-operation validation | **Implemented** and live-tested through Claude Code 2.1.227, including discovery, native status frames, cancellation, and accessible-workspace validation |
177
- | Claude binary/runtime metadata | **Implemented**; validates the exact owned path and records launcher-leaf metadata without granting it routing authority |
178
- | Allowlisted Codex App Server connector with bounded busy behavior | **Implemented** and live-tested against App Server 0.147.0 for external busy observation, registered-route reachability across settings changes, and an automatically started queued turn; exact `STEER:` boundary behavior is covered deterministically |
177
+ | Claude current-user runtime roots | **Implemented**; derives the registry and callback roots from the verified OS user without inspecting a launcher or configuration file |
178
+ | Stateless allowlisted Codex App Server transport with bounded busy behavior | **Implemented**; every operation opens and attests its own transport, and the conformance suite covers idle gating, exact `STEER:` behavior, clean retry, and ambiguous no-replay settlement |
179
179
  | Attach-only local Codex proxy transport and exact-owned cleanup | **Implemented**, five deterministic tests; no live App Server connection in routine tests |
180
180
  | Local provider adapters | **Implemented**, focused synthetic tests cover Claude discovery, exact Codex ownership, and lazy ACP-backed DeepSeek and Grok routes with provider-local degradation and cleanup; remote adapters remain disabled |
181
- | Gateway service composition | **Implemented**, including private control-server startup, adapter lifecycle, synthetic cross-provider selection/dispatch/reply correlation, metadata-only publication, and clean-restart abandonment tests |
182
- | Delivery receipt/status lifecycle | **Implemented**, deterministic synthetic tests cover stable-UUID native receipt re-resolution, the merged/verbose/quiet Claude notice policy, one bounded stall notice with pending age where enabled, opaque memory-only correlation handles, the closed status/terminal schema, and one-shot/bounded-wait CLI behavior |
181
+ | Gateway service composition | **Implemented**, including private control-server startup, synthetic cross-provider selection/dispatch/reply correlation, metadata-only publication, and restart attempt-phase tests |
182
+ | Delivery receipt/status lifecycle | **Implemented**, deterministic synthetic tests cover stable-UUID native receipt re-resolution, the merged/verbose/quiet Claude notice policy, one bounded stall notice with pending age where enabled, opaque private-v3 correlation handles, restart continuity, the closed status/terminal schema, and one-shot/bounded-wait CLI behavior |
183
183
  | Broker-owned cross-provider provenance framing | **Implemented**, deterministic tests cover exact Codex and Claude wire shapes, bounded long-alias attribution, recipient reply hints, reserved-tag neutralization, single wrapping across clean retries, and pre-write failure |
184
184
  | Operator/agent client CLI and package binary | **Implemented**, deterministic private-UDS tests cover the closed command family, inherited provider identity, bounded stdin-only bodies, normalized output, and ambiguous no-retry behavior |
185
185
  | Repo-shipped cross-provider skill | **Implemented** as a repo-scoped workflow over the client CLI; it is not installed into either provider's global configuration |
@@ -213,41 +213,27 @@ are replaceable delivery coordinates, not identity, and are refreshed from the
213
213
  live registry before a write. The gateway rejects duplicate current names and
214
214
  duplicate live UUIDs rather than choosing between them.
215
215
 
216
- The controller binds a Codex route to an exact host, App Server endpoint
217
- generation, thread ID, and owner lease. Other provider-native IDs, PIDs,
216
+ The controller binds a Codex route to an exact host, thread ID, and owner
217
+ lease. Other provider-native IDs, PIDs,
218
218
  working directories, endpoint paths, and socket paths are never public
219
219
  selectors or output fields. A Claude UUID may be supplied explicitly as a
220
220
  destination, but the gateway never prints or invents one for the caller.
221
221
 
222
222
  Codex registration is explicit. A task registers its own alias and
223
223
  authoritative `CODEX_THREAD_ID`; the gateway does not enumerate global Codex
224
- history to invent routes. A route becomes usable only after the matching host
225
- connector positively observes that exact task on the current endpoint
226
- generation.
227
-
228
- An App Server endpoint-generation change is a fenced route transition, not a
229
- new registration. Embassy stops dispatch through the old connector and creates
230
- a replacement connector. That exact generation must negotiate its current
231
- interface and pass `thread/loaded/list` validation, where every retained
232
- private thread ID must occur exactly once. The connector resumes that exact
233
- task with history excluded; only then may the controller activate writes and
234
- atomically re-anchor the same alias, owner lease, and pair edges to the new
235
- endpoint generation. Each successful re-anchor is recorded in a bounded
236
- private journal; neither thread IDs nor endpoint generations enter public
237
- activity, diagnostics, logs, or CLI output. An incompatible endpoint, a
238
- missing or duplicate exact task, or an unclean transition leaves the route
239
- stale and write-disabled. No body, callback, receipt handle, reply capability,
240
- conversation capability, delivery token, or ambiguous write crosses this
241
- boundary by replay.
242
-
243
- The live dashboard exposes one bounded recovery operation for an orphaned
244
- Codex registration. It carries only the public canonical `codex-*` alias and
245
- requires explicit operator confirmation. The broker revalidates that the
246
- route is stale and its owning endpoint generation is dead, then quiesces that
247
- exact route before removing it and its incident pair edges and appending the
248
- private recovery journal. Ready, merely offline, current-generation, and
249
- ambiguous cases fail closed; the browser cannot name a thread ID or endpoint
250
- generation.
224
+ history to invent routes. Registration performs no provider I/O. Each delivery
225
+ opens a fresh attested App Server transport, initializes it, resumes the exact
226
+ private task with history excluded, and authorizes one body write. Endpoint or
227
+ Desktop restart is therefore a transport fact, not a logical route transition;
228
+ an unavailable or duplicate exact task fails that operation with a safe code
229
+ without retargeting the alias or replaying an ambiguous write.
230
+
231
+ The live dashboard exposes the bounded `remove_codex_registration` operation.
232
+ It carries only one public canonical `codex-*` alias and requires explicit
233
+ operator confirmation of the consequences. In one fencing commit the broker
234
+ cancels queued or reserved work, settles armed work ambiguous and accepted work
235
+ unconfirmed, removes that registration and its incident consent edges, and
236
+ never replays uncertain work. The browser cannot name a thread ID.
251
237
 
252
238
  Claude discovery is passive and limited to currently advertised genuine
253
239
  Claude session records. Only a validated native record bearing the supported
@@ -275,26 +261,17 @@ invented additional owner or mode rule. A current name resolves to a UUID but
275
261
  never substitutes for it.
276
262
 
277
263
  A selected Claude UUID remains the durable route identity until explicit
278
- unselection. Startup performs a bounded read-only Claude registry scan solely
279
- for connector-level schema, rejection, and empty-since-boot evidence. It
280
- publishes no candidates and does not select, connect to, or adopt the identity
281
- of any peer; every restored route begins stale. The stateful probe may replace
282
- memory-only validated target bindings, including native IDs and socket-derived
283
- binding evidence, until a later scan or close. Those bindings are neither
284
- public nor persisted and confer no candidate publication, selection, or route
285
- authority. A later, separately authorized discovery operation may reactivate
286
- the selection only when the full bounded scan contains exactly one compatible
287
- interactive peer with the byte-identical UUID on the same provider, host, and
288
- ownership lease. The adapter revalidates the current workspace and provider
289
- selection before the store atomically adopts the current endpoint generation
290
- and latest name. An incomplete scan, duplicate name or UUID, changed UUID,
291
- workspace failure, or store collision leaves the route stale and releases any
292
- provider selection acquired by the failed attempt. A name alone never restores
293
- or retargets a durable selection.
264
+ unselection. Discovery publishes only bounded sanitized candidates and current
265
+ lookup aliases; it never changes the selected UUID. Immediately before a
266
+ Claude-bound write, Embassy performs a fresh bounded registry scan, resolves
267
+ that byte-identical UUID exactly once, and revalidates its current workspace,
268
+ process, socket, and used-artifact generation. An incomplete scan, duplicate
269
+ name or UUID, changed UUID, or unsafe current coordinate fails that operation
270
+ closed. A name alone never restores or retargets a durable selection.
294
271
 
295
272
  The dashboard is the single pane for the human. It shows both sanitized
296
273
  available/selected Claude aliases and explicitly registered Codex aliases,
297
- including their host, compatibility, state, last-seen age, and queue depth.
274
+ including their host, current state, last-seen age, and queue depth.
298
275
  The thin skill/CLI exposes the same safe alias list to either provider.
299
276
 
300
277
  ## Message flows
@@ -340,9 +317,9 @@ Claude's native `SendMessage`, starts an App Server turn, and returns the
340
317
  final reply.
341
318
 
342
319
  1. The gateway advertises one process-owned `codex-*` record per registered
343
- task in Claude's native registry. A supervised helper process owns each
344
- advertisement's registry record and callback socket; the broker remains
345
- the sole owner of state, queues, and dispatch.
320
+ task in Claude's native registry. The broker owns the advertisement,
321
+ callback socket, state, queue, and dispatch; provider process lifecycle is
322
+ not persisted as route authority.
346
323
  2. A real Claude session uses native `ListAgents` and `SendMessage`; the
347
324
  gateway validates that exact live registry/socket generation and treats the
348
325
  text as untrusted user-role input. This inbound observation grants only a
@@ -364,13 +341,13 @@ final reply.
364
341
  4. The gateway resolves the Codex alias to its private exact-thread binding.
365
342
  5. The resumed task retains its existing native permissions. The gateway does
366
343
  not supply policy overrides.
367
- 6. App Server status notifications atomically refresh the advertised native
368
- peer record to `idle`, `busy`, or `waiting`.
369
- 7. Immediately before `turn/start` or `turn/steer`, the owning connector wraps
344
+ 6. Immediately before `turn/start` or `turn/steer`, the delivery attempt wraps
370
345
  the raw body once in Embassy's authoritative Codex-bound
371
- `cross-session-message` frame. If the task is idle, the connector starts one dedicated turn.
372
- Ordinary messages received while it is active or awaiting approval queue
373
- internally. An exact leading `STEER:` body in this direction is marked as a
346
+ `cross-session-message` frame. It opens and attests a fresh App Server
347
+ transport, resumes the exact task with history excluded, and starts one
348
+ dedicated turn only if the task is idle. Ordinary messages received while
349
+ it is active or awaiting approval remain queued. An exact leading `STEER:`
350
+ body in this direction is marked as a
374
351
  steering message. If the connector has a positively observed active turn
375
352
  and no RPC already in flight, it sends the closed `turn/steer` request with
376
353
  that exact ID as `expectedTurnId`; App Server admits the input at the next
@@ -379,7 +356,7 @@ final reply.
379
356
  boundary silently returns the same body to the normal queue. It does not
380
357
  emit Claude's approval-specific native `held` control frame for ordinary
381
358
  queueing.
382
- 8. In `merged` and `verbose` notice modes, if the delivery remains pending for
359
+ 7. In `merged` and `verbose` notice modes, if the delivery remains pending for
383
360
  exactly `min(floor(messageDeadlineMs / 2), 120_000)` milliseconds, the
384
361
  gateway may send the originating Claude session at most one nonterminal
385
362
  `<gateway-delivery-stall>` user frame for that receipt. It contains only an
@@ -389,13 +366,13 @@ final reply.
389
366
  default four-hour deadline the notice fires at two minutes, not two hours.
390
367
  `quiet` suppresses this gateway-authored frame without changing native status
391
368
  or dashboard state.
392
- 9. When the task becomes idle, the connector refreshes the exact task state and
393
- starts the held message. A route retains at most three queued steering
369
+ 8. A later bounded attempt opens a new transport and starts the held message
370
+ after it observes the exact task idle. A route retains at most three queued steering
394
371
  messages; accepting a fourth atomically cancels the oldest with safe code
395
372
  `STEER_QUEUE_SUPERSEDED`, a normal terminal receipt, and a `STEER`-marked
396
373
  journal event. Explicit registration is sufficient authorization; Embassy
397
374
  does not run an additional workspace or policy classifier.
398
- 10. Successful App Server acceptance returns Claude's native `delivered`
375
+ 9. Successful App Server acceptance returns Claude's native `delivered`
399
376
  receipt. A route or delivery error returns native `expired` with one safe
400
377
  error code retained in its `reason` field. The default `merged` mode omits
401
378
  the duplicate terminal user frame; `verbose` additionally sends a static
@@ -406,7 +383,7 @@ final reply.
406
383
  reserved for an actual user or policy refusal.
407
384
  A transient clean pre-dispatch failure returns the same message to the queue
408
385
  instead of terminally failing it.
409
- 11. Completion is summarized into bounded normalized state and the correlated
386
+ 10. Completion is summarized into bounded normalized state and the correlated
410
387
  reply is returned only to the same originating Claude session UUID after
411
388
  its current coordinates are uniquely re-resolved and revalidated.
412
389
 
@@ -415,7 +392,7 @@ its mutable name, PID, registry record, or socket. Before every stall or
415
392
  terminal receipt write, the adapter performs bounded discovery and revalidates
416
393
  the UUID's current exact coordinates. This permits a receipt to follow ordinary
417
394
  process/socket rotation without writing to a stale generation. If the UUID is
418
- not uniquely and compatibly re-observed, the write fails closed. A terminal
395
+ not uniquely re-observed with peer protocol 1, the write fails closed. A terminal
419
396
  write whose outcome is ambiguous is never replayed; only a proven pre-write
420
397
  failure may be retried while the bounded in-memory receipt remains live. The
421
398
  receipt correlation does not add the UUID or receipt handle to public output
@@ -448,8 +425,8 @@ limits, deduplicates, and queues before presentation framing.
448
425
  The store therefore retains only the raw unframed body, never the composed
449
426
  envelope. A pure composer runs at the final semantic provider-write boundary so
450
427
  a clean retry produces the same bytes with exactly one authoritative outer
451
- wrapper. Automatic provider
452
- startup and endpoint-generation validation, receipt frames, and diagnostics do
428
+ wrapper. Provider connection setup and per-operation artifact validation,
429
+ receipt frames, and diagnostics do
453
430
  not use this path.
454
431
 
455
432
  Both provider directions use Claude-compatible textual framing with a
@@ -500,19 +477,20 @@ on by default and exact `0` disables classification. The tested 0.147.0 schema
500
477
  requires `expectedTurnId`, rejects a nonmatching active turn, reports a clean
501
478
  `activeTurnNotSteerable` condition, and returns the accepted turn ID. Embassy
502
479
  validates all of those temporal correlations before settlement. `turn/interrupt`
503
- is permitted only for a turn that the same connector started and positively
504
- observed; steering never authorizes it, and there is no generic App Server RPC
505
- escape hatch.
480
+ is never called or exposed, and there is no generic App Server RPC escape
481
+ hatch.
506
482
 
507
483
  ### Delivery status and bounded waits
508
484
 
509
485
  Every accepted control-plane `send_to_claude`, `send_to_codex`, or `reply`
510
- result contains both its conversation ID and a fresh opaque, memory-only
511
- delivery correlation handle called a delivery token.
486
+ result contains both its conversation ID and a fresh opaque delivery
487
+ correlation handle called a delivery token.
512
488
  The token has the closed form `dlv_` followed by exactly 24 base64url
513
489
  characters (`A-Z`, `a-z`, `0-9`, `_`, or `-`). It addresses one bounded
514
- in-memory delivery tracker and is not a provider receipt handle or a provider
515
- native identifier.
490
+ private-v3 message/status row and is not a provider receipt handle or a
491
+ provider native identifier. It is stored only in the mode-0600 broker state
492
+ and never appears in a public snapshot, normal log, provider receipt, or
493
+ dashboard.
516
494
 
517
495
  The read-only `delivery_status` method accepts only that token and returns one
518
496
  of these closed results:
@@ -557,21 +535,20 @@ never displaced to admit a new send. A pressure-evicted handle returns
557
535
  ### Replies and process restarts
558
536
 
559
537
  Conversation IDs correlate replies, and callback addresses exist only in
560
- memory, but queued message bodies are durable. After a gateway restart, each
561
- queued message is triaged: one already past its deadline settles `expired`; one
562
- whose target authority was transient, or whose target route is gone, settles
563
- `abandoned` with `CONTROLLER_RESTARTED`; every other queued body is restored and
564
- re-armed for dispatch, then sent exactly once after its exact route is
565
- re-observed. A message in flight at the moment of the restart or crash settles
566
- `ambiguous` with `CONTROLLER_RESTARTED` and is never replayed.
567
-
568
- The prior Claude binding remains stored but stale. After authorized live
569
- discovery, one exact UUID-bound selection may be reactivated under its latest
570
- name. No pending reply, callback, native receipt handle, delivery token/status
571
- tracker, or conversation capability survives the restart; a prior token
572
- therefore returns `found: false`. A stale or offline selection can be explicitly
573
- removed by its stored alias or a user-supplied UUID without requiring discovery
574
- first.
538
+ memory, but message bodies and their bounded attempt phase are durable. After a
539
+ gateway restart, queued or reserved work may resume once against the same
540
+ logical route and consent edge. Work that crossed the armed boundary settles
541
+ `ambiguous`; provider-accepted work without terminal evidence settles
542
+ `unconfirmed`. Neither is replayed. Work already past its deadline settles
543
+ `expired`.
544
+
545
+ The delivery token and status of each retained message survive the restart: a
546
+ queued or reserved attempt remains inspectable while it resumes, and armed or
547
+ accepted work remains inspectable after it settles ambiguous or unconfirmed.
548
+ Pending replies, callbacks, native receipt handles, and conversation
549
+ capabilities do not survive. Logical registrations, Claude selections, and
550
+ consent edges remain, while each subsequent provider operation must attest its
551
+ own current transport facts.
575
552
 
576
553
  ## Gateway control plane
577
554
 
@@ -584,37 +561,49 @@ The closed version 1 method family is exactly these sixteen methods:
584
561
 
585
562
  - `health` and `list_snapshot`, a safe public snapshot;
586
563
  - `observe_snapshot`, a read-only projection that may settle already-due
587
- lifecycle deliveries before projecting;
564
+ delivery deadlines before projecting;
588
565
  - `register_codex`, `unregister_codex`, and
589
- `remove_stale_codex_registration` — explicit Codex registration and
590
- succession, unregister, and broker-guarded removal of a stale orphan whose
591
- owning endpoint generation is dead;
566
+ `remove_codex_registration` — explicit Codex registration and atomic
567
+ `--succeeds` replacement, owner unregister, and confirmed operator removal;
592
568
  - `select_claude` and `unselect_claude`, from the current sanitized
593
569
  available-peer inventory;
594
570
  - `pair` and `unpair`, the two-endpoint permission edge;
595
- - `delivery_status`, a lookup by opaque, memory-only correlation handle;
571
+ - `delivery_status`, a lookup by an opaque correlation handle retained only in
572
+ bounded private v3 state;
596
573
  - `untrack`, which closes one active progress watch by conversation token;
597
574
  - `send_to_claude` and `send_to_codex`, the provider-specific sends;
598
575
  - `reply`, the correlated reply operation; and
599
- - `refresh_dashboard`, which refreshes Claude discovery and republishes.
576
+ - `refresh_dashboard`, which refreshes provider discovery and republishes.
600
577
 
601
578
  The live dashboard companion calls `observe_snapshot` for every read; its
602
579
  mutation route additionally calls `pair`, `unpair`,
603
- `remove_stale_codex_registration`, and `refresh_dashboard`, and nothing else.
580
+ `remove_codex_registration`, and `refresh_dashboard`, and nothing else.
604
581
 
605
582
  The installed binary is `embassy`, and it is the only installed binary. Its
606
- seventeen implemented commands are
583
+ eighteen implemented commands are
607
584
  `serve`, `health`, `status`, `delivery-status`, `wait-delivery`, `untrack`,
608
585
  `refresh-dashboard`, `dashboard`, `register-codex`, `unregister-codex`,
609
586
  `select-claude`, `unselect-claude`, `pair`, `unpair`, `send-to-claude`,
610
- `send-to-codex`, and `reply`. `dashboard` requires `--live` and accepts an
587
+ `send-to-codex`, `reply`, and `convert-state-v2-to-v3`. `dashboard` requires `--live` and accepts an
611
588
  optional `--lang en|zh-CN` and `--port <n>`; it starts the companion process
612
589
  rather than issuing a single control request. Message bodies are non-empty
613
590
  UTF-8 from standard input only, with a 16 KiB ceiling; they are never accepted
614
591
  in an argument or file. The client emits one bounded normalized JSON line and
615
592
  never returns a thread ID, provider-native ID, path, address, or message body.
616
593
  These commands require the foreground broker, except that `serve` starts it in
617
- the current terminal. It never daemonizes itself.
594
+ the current terminal and `convert-state-v2-to-v3` is an offline one-shot
595
+ upgrade. The converter uses only the normally resolved state directory,
596
+ requires the controller lock, installs strict native schema 3 through an
597
+ fsynced atomic replacement, and reports only success plus the verified v2
598
+ backup basename. It starts no provider, helper, listener, discovery, or control
599
+ socket. The launcher never daemonizes itself.
600
+
601
+ `register-codex --alias <new> --succeeds <current>` is one atomic logical-route
602
+ transaction. It verifies the inherited identity of the replacement task,
603
+ settles the outgoing route's work according to recorded write phase, removes
604
+ its incident consent edges and transient capabilities, and publishes only the
605
+ new registration. There is no prepared, activated, endpoint-generation, or
606
+ manual-recovery state.
618
607
 
619
608
  `select-claude --alias <current-name@host>` and
620
609
  `select-claude --session <uuid>` select the same logical session.
@@ -733,35 +722,31 @@ any NVM-managed `codex` on the user's `PATH` (for example
733
722
  it, and does not edit a shell profile. The two installations therefore do not
734
723
  conflict.
735
724
 
736
- The connector has a fixed App Server method allowlist. A connector
737
- may initialize, observe loaded tasks, resume/unsubscribe the exact registered
738
- task, start a dedicated turn, and interrupt only its own confirmed turn. Archive,
725
+ The stateless transport has a fixed App Server method allowlist. One attempt
726
+ may initialize, resume the exact registered task, start a dedicated turn, or
727
+ steer the exact positively observed active turn. Loaded-task enumeration,
728
+ unsubscribe, interrupt, archive,
739
729
  delete, history, shell, configuration, authentication, plugin,
740
730
  approval-response, and generic RPC methods remain excluded everywhere.
741
731
 
742
732
  The App Server capability first tested with 0.147.0 gates the privacy-preserving
743
733
  `thread/resume.excludeTurns` field behind initialization capability
744
- `experimentalApi: true`. The connector therefore hard-codes that one
745
- non-configurable capability solely to suppress history retrieval. Both initial
746
- resume and the immediate pre-start refresh send exactly `threadId` plus
747
- `excludeTurns: true`, then require an exact empty `thread.turns` array.
734
+ `experimentalApi: true`. The transport therefore hard-codes that one
735
+ non-configurable capability solely to suppress history retrieval. The attempt's
736
+ resume sends exactly `threadId` plus `excludeTurns: true`, then requires an
737
+ exact empty `thread.turns` array.
748
738
  Missing, malformed, or nonempty turns fail closed and are never emitted or
749
739
  persisted. The capability does not add an experimental client method or change
750
740
  the closed RPC allowlist.
751
741
 
752
- Generation validation and controller activation are distinct gates. A
753
- replacement connector may initialize, list, resume, and expose normalized
754
- status before the controller activates it. No Claude-initiated turn can start
755
- until that exact endpoint generation has negotiated its current interface,
756
- re-observed the registered task, and established explicit route ownership.
757
- Version metadata does not participate in that decision.
758
-
759
- Registration resumes the exact task and establishes reachability. Embassy does
760
- not read or retain reported working-directory or policy fields. Before
761
- `turn/start`, including a queued drain, the connector refreshes that exact task
762
- on the same live connection, requires it to be idle, and starts the turn with
763
- no policy overrides. Settings notifications cannot make an explicitly
764
- registered live route unreachable or discard its accepted queue.
742
+ Registration records the exact inherited task identity and establishes logical
743
+ reachability without provider I/O. For each `turn/start`, including a queued
744
+ drain, Embassy opens and attests a fresh transport, initializes the closed
745
+ interface, resumes that exact task with history excluded, requires it to be
746
+ idle, and authorizes one body write with no policy overrides. Version metadata
747
+ does not participate. Embassy does not read or retain reported
748
+ working-directory or policy fields, and a transport failure cannot discard the
749
+ registration or its accepted queue.
765
750
 
766
751
  Version 1 never changes or independently classifies a Codex task's approval or
767
752
  sandbox policy. Offline `TurnStartParams` schema evidence from tested App
@@ -770,7 +755,7 @@ policy overrides persist for the current and subsequent turns, so using them
770
755
  as per-message restrictions would silently mutate the native task. Embassy
771
756
  therefore starts the turn without overrides and leaves approval, sandbox, and
772
757
  tool enforcement to the registered task's native Codex configuration. Explicit
773
- `codex-*` registration plus exact live thread/generation validation is the
758
+ `codex-*` registration plus exact per-operation task and transport validation is the
774
759
  gateway reachability boundary; native task policy remains Codex's concern.
775
760
 
776
761
  A remote connector never starts, stops, replaces, signals, or unlinks a
@@ -860,7 +845,7 @@ Each page assembles seven sections:
860
845
 
861
846
  - **Exchange** — aggregate gateway health plus every explicit cross-provider
862
847
  pair and its per-edge counters.
863
- - **Attention** — allowlisted alerts such as stale route, protocol mismatch,
848
+ - **Attention** — allowlisted alerts such as an unavailable route, protocol mismatch,
864
849
  queue full, or ambiguous delivery.
865
850
  - **Transit** — queued-message depth and bytes in flight.
866
851
  - **Progress supervision** — active progress watches and their state.
@@ -910,15 +895,16 @@ gateway as unavailable when nothing is serving.
910
895
  and cross-origin reads are unavailable.
911
896
  - **Projection and actions.** The companion observes through
912
897
  `observe_snapshot`. Its only mutations are exact two-endpoint `pair`,
913
- `unpair`, `refresh_dashboard`, and `remove_stale_codex_registration`
914
- control calls behind one closed `/action` route. The recovery
915
- action carries only a canonical public `codex-*` alias and is accepted only
916
- after the broker revalidates that the route is stale and its endpoint
917
- generation is dead; native task and generation IDs never enter the browser
918
- contract. The browser shows the consequence and requires
898
+ `unpair`, `refresh_dashboard`, and `remove_codex_registration`
899
+ control calls behind one closed `/action` route. The removal action carries
900
+ only a canonical public `codex-*` alias; native task IDs never enter the
901
+ browser contract. In one fencing commit it cancels queued or reserved work,
902
+ settles armed work ambiguous and accepted work unconfirmed, removes the
903
+ route's consent edges, and never replays uncertain work. The browser shows
904
+ the consequence and requires
919
905
  explicit confirmation; the server rejects bodies over 1 KiB and limits the
920
906
  companion to six actions per minute. It cannot create a registration,
921
- unregister a live route, succeed,
907
+ succeed,
922
908
  send, reply, approve, interrupt, change settings, or invoke a generic/provider
923
909
  method. Each mutation touches only the edge it names: adding an edge never
924
910
  retires another, and removing one settles its accepted work before the
@@ -939,11 +925,13 @@ the static pair, which is always written in both languages.
939
925
 
940
926
  The private store may retain:
941
927
 
942
- - aliases, enabled state, ownership leases, and exact provider-native route
943
- handles inside the closed controller-private binding schema;
944
- - endpoint-generation and compatibility markers;
945
- - bounded durable queue-ledger and normalized delivery metadata used for
946
- accounting and dashboard projection;
928
+ - schema-3 logical registrations with aliases, registration IDs, and exact
929
+ provider-native route handles inside the closed private binding schema;
930
+ - consent edges tied to exact registration IDs, so alias reuse cannot inherit
931
+ permission;
932
+ - bounded messages with explicit `queued`, `reserved`, `armed`, `accepted`, or
933
+ `terminal` attempt phase and normalized activity used for accounting and
934
+ dashboard projection;
947
935
  - timestamps, counters, dedupe/rate-limit records, and safe error codes.
948
936
 
949
937
  It also retains message bodies under bounded caps — the queued body of every
@@ -952,23 +940,22 @@ oldest-first against a 1 MiB budget by default. It must never retain provider
952
940
  output, tool input/output, raw App Server or Claude frames, stderr, histories,
953
941
  credentials, Claude registry payloads, or callback/socket paths. The public
954
942
  snapshot is a
955
- strict projection that also removes private route handles and endpoint
956
- generations. The state directory is mode 0700 and binding state is mode 0600;
943
+ strict projection that removes private route handles, registration IDs, and
944
+ operation-local endpoint evidence. The state directory is mode 0700 and state is mode 0600;
957
945
  provider-native identifiers never enter normalized events, public snapshots,
958
946
  the dashboard, CLI arguments/output, aliases, logs, or error text. On restart,
959
- every restored route begins stale and unusable. An authorized discovery may
960
- reactivate only the byte-identical durable Claude UUID after the current
961
- provider endpoint, workspace, complete unique discovery, and ownership lease
962
- all revalidate. The public `selected` bit flips only after that atomic private
963
- rebind succeeds. Queued bodies are restored and re-armed for dispatch; no
964
- callback, receipt handle, conversation, or reply capability is restored, and no
965
- delivery token or status tracker is reconstructed.
966
-
967
- The delivery-token mapping, queryable status tracker, native receipt handle,
968
- and one-stall-notice state are always memory-only. Durable delivery metadata
969
- does not contain enough information to reconstruct any of those capabilities.
970
- A restored body is re-sent exactly once and only into its own exact
971
- re-observed route; it is never replayed into a reconstructed capability.
947
+ logical routes and consent edges remain unchanged. Queued and reserved bodies
948
+ may resume once only after their exact registration and consent authority is
949
+ rechecked; armed and accepted work settles without replay. Callback, native
950
+ receipt, conversation, and reply capabilities are not reconstructed.
951
+
952
+ Private schema 3 is the binary's only native store format; the bounded public
953
+ snapshot deliberately remains schema version 2. Upgrading an existing schema-2
954
+ state requires the offline `embassy convert-state-v2-to-v3` command before
955
+ starting the broker. It verifies and retains a byte-identical v2 backup, then
956
+ atomically installs and reads back strict v3 state. The runtime performs no
957
+ in-binary migration or best-effort rewrite; any non-v3 state produces the
958
+ ordinary strict corrupt-state error.
972
959
 
973
960
  ## Minimum filesystem and process access
974
961
 
@@ -1001,8 +988,7 @@ paths, peers, and transports:
1001
988
 
1002
989
  | Path/capability | Minimum purpose |
1003
990
  | --- | --- |
1004
- | `~/.local/bin/claude` (or the absolute `EMBASSY_CLAUDE_BIN` override) and its derived current target under `~/.local/share/claude/versions/` | Stat the owned launcher/path components and read/execute only the resolved version target for bounded `--version`; `PATH` and interactive shell profiles are never searched; live launcher validation succeeded |
1005
- | `~/.claude/sessions` | Read/enumerate only live registry JSON during the separately authorized passive-discovery gate |
991
+ | `~/.claude/sessions` | Derive from the verified current OS user's normalized home; read/enumerate only live registry JSON during the separately authorized passive-discovery gate, and validate exact records, PIDs, workspaces, and peer sockets used by the current operation. An absent or unsafe root degrades only Claude |
1006
992
  | `/tmp/cc-socks` | At foreground startup, validate the private directory and create/remove only `/tmp/cc-socks/<gateway-pid>.sock` after inode/generation checks; search/stat genuine peers at passive discovery and connect one validated target only at the separately authorized send gate |
1007
993
  | `~/.local/state/agent-embassy/.agent-embassy-state` | Validate or establish the exact ownership marker before creating the fixed host lease; an existing non-empty unmarked root is rejected without mutation |
1008
994
  | `/usr/bin/lockf` and `/bin/cat` | Hold one fixed, non-waiting macOS advisory lease for the foreground controller; the helper receives no shell text, provider data, or model-supplied argument |
@@ -1033,48 +1019,33 @@ the preferred least-context setup, but it is not mandatory.
1033
1019
  Embassy-owned or executed artifacts and Embassy callback, control, or state
1034
1020
  paths refuses broker startup. Unsafe UID or mode evidence on Claude's
1035
1021
  external sessions registry root quarantines only Claude. A malformed message version, required App Server response
1036
- shape, or endpoint generation fails closed on its affected route.
1022
+ shape, or used-artifact generation fails closed on its current operation.
1037
1023
  - Alias collisions, stale ownership leases, PID/socket races, unsafe
1038
1024
  gateway-owned state, unexpected paths, queue overflow, deadline expiry, and ambiguous writes are
1039
1025
  normalized failures, never raw diagnostics.
1040
- - Provider disconnect invalidates every route on that endpoint generation.
1041
- - Explicit `register-codex` replaces a closed or faulted App Server connector;
1042
- if the recovered route is idle, held undispatched work is woken. Ambiguous
1043
- writes are not retried.
1026
+ - A provider disconnect fails or defers only the current operation. The next
1027
+ eligible attempt opens and attests a new transport; logical registration and
1028
+ consent do not depend on a connector lifecycle.
1044
1029
  - The first successful Codex registration locks its exact alias, task, and host
1045
- until that registration is explicitly succeeded. Exact re-registration and
1046
- connector recovery remain valid; unregister removes reachability but does not
1047
- by itself release the identity.
1030
+ until that registration is explicitly replaced or unregistered. Exact
1031
+ re-registration remains idempotent.
1048
1032
  - `register-codex --alias <new> --succeeds <current>`, issued from inside the
1049
1033
  successor task on the same host with its own inherited `CODEX_THREAD_ID`, is
1050
- the one path that changes the registered Codex identity without a restart. A
1051
- successor must name the exact active registration, on the same host, with a
1052
- different alias and a different thread; anything else is
1053
- `CODEX_SUCCESSION_OWNER_MISMATCH`. The journaled machine freezes the outgoing
1054
- generation's ingress and dispatch, drains callbacks and receipt writes, and
1055
- requires a clean quiescence barrier before it prepares anything durable.
1056
- Nothing transfers: conversations, pending reply capabilities, queued bodies,
1057
- and delivery tokens belong to the retired identity and are purged rather than
1058
- inherited, and the successor is published on a fresh listener generation.
1059
- Publication arming is the irreversible boundary — before it, a failure rolls
1060
- back to the old registration; at or after an armed, published, or unknown
1061
- observation, the old registration is never restored, and an incomplete
1062
- succession leaves registration offline and pinned until manual recovery
1063
- rather than leaving two live registrations.
1064
- - A failed reactivation of a retained route, or any fresh-registration rollback
1065
- whose cleanup cannot be fully confirmed, pins that exact identity
1066
- fail-closed. Only exact retry is permitted until the old route is confirmed
1067
- unregistered and the controller is restarted.
1034
+ the one atomic transaction that changes the registered Codex identity. A
1035
+ replacement must name the exact current registration on the same host, with
1036
+ a different alias and thread. Embassy settles the outgoing registration's
1037
+ work from its durable attempt phase, removes its consent edges and transient
1038
+ capabilities, and publishes only the replacement. No conversation, reply
1039
+ capability, queued body, or permission transfers to the new identity, and no
1040
+ intermediate generation or manual-recovery state exists.
1068
1041
  - No ambiguous mutation is retried automatically.
1069
- - A queued body survives process loss under bounded retention and is re-sent
1070
- exactly once after its exact route is re-observed. A message in flight when
1071
- the process was lost settles `ambiguous` with `CONTROLLER_RESTARTED`; a
1072
- message whose target authority was transient, or whose target route no longer
1073
- exists, settles `abandoned` with the same code.
1042
+ - A queued or reserved body survives process loss under bounded retention and
1043
+ may resume once after exact logical authority is rechecked. Armed or accepted
1044
+ work settles `ambiguous` or `unconfirmed` and is never replayed.
1074
1045
  - A provider or Desktop update that changes an internal interface degrades its
1075
1046
  responsible route while the broker and other providers remain available. A
1076
- Claude record outside peer protocol 1 is rejected per record, and retained
1077
- state never activates an unvalidated replacement endpoint generation.
1047
+ Claude record outside peer protocol 1 is rejected per record, and every
1048
+ current provider artifact used for an operation is re-attested before effect.
1078
1049
 
1079
1050
  ## Validation boundary
1080
1051
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-embassy",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "A local gateway for bidirectional messaging between Claude Code sessions and Codex tasks.",
5
5
  "license": "MIT",
6
6
  "repository": {