agent-embassy 1.3.0 → 1.4.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 (75) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/CONTRIBUTING.md +25 -9
  3. package/README.md +39 -15
  4. package/README.zh-CN.md +34 -13
  5. package/SECURITY.md +13 -7
  6. package/dist/src/gateway/claude-helper-protocol.d.ts +1 -0
  7. package/dist/src/gateway/claude-helper-protocol.js +4 -2
  8. package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
  9. package/dist/src/gateway/claude-helper-supervisor.d.ts +1 -0
  10. package/dist/src/gateway/claude-helper-supervisor.js +3 -0
  11. package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
  12. package/dist/src/gateway/cli-copy.d.ts +1 -1
  13. package/dist/src/gateway/cli-copy.en.d.ts +3 -2
  14. package/dist/src/gateway/cli-copy.en.js +3 -1
  15. package/dist/src/gateway/cli-copy.en.js.map +1 -1
  16. package/dist/src/gateway/cli-copy.js +1 -0
  17. package/dist/src/gateway/cli-copy.js.map +1 -1
  18. package/dist/src/gateway/cli-copy.zh-CN.d.ts +3 -2
  19. package/dist/src/gateway/cli-copy.zh-CN.js +3 -1
  20. package/dist/src/gateway/cli-copy.zh-CN.js.map +1 -1
  21. package/dist/src/gateway/cli.d.ts +1 -1
  22. package/dist/src/gateway/cli.js +12 -1
  23. package/dist/src/gateway/cli.js.map +1 -1
  24. package/dist/src/gateway/codex-app-server.d.ts +1 -1
  25. package/dist/src/gateway/codex-app-server.js +32 -11
  26. package/dist/src/gateway/codex-app-server.js.map +1 -1
  27. package/dist/src/gateway/config.d.ts +1 -1
  28. package/dist/src/gateway/control.d.ts +1 -1
  29. package/dist/src/gateway/control.js +5 -3
  30. package/dist/src/gateway/control.js.map +1 -1
  31. package/dist/src/gateway/dashboard-copy.d.ts +1 -1
  32. package/dist/src/gateway/dashboard-copy.en.d.ts +15 -1
  33. package/dist/src/gateway/dashboard-copy.en.js +25 -11
  34. package/dist/src/gateway/dashboard-copy.en.js.map +1 -1
  35. package/dist/src/gateway/dashboard-copy.js +15 -1
  36. package/dist/src/gateway/dashboard-copy.js.map +1 -1
  37. package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +15 -1
  38. package/dist/src/gateway/dashboard-copy.zh-CN.js +25 -11
  39. package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -1
  40. package/dist/src/gateway/dashboard-model.d.ts +2 -3
  41. package/dist/src/gateway/dashboard-model.js +118 -30
  42. package/dist/src/gateway/dashboard-model.js.map +1 -1
  43. package/dist/src/gateway/dashboard.js +22 -9
  44. package/dist/src/gateway/dashboard.js.map +1 -1
  45. package/dist/src/gateway/instance-lease.d.ts +2 -3
  46. package/dist/src/gateway/instance-lease.js +17 -140
  47. package/dist/src/gateway/instance-lease.js.map +1 -1
  48. package/dist/src/gateway/live-dashboard-app/app.js +45 -18
  49. package/dist/src/gateway/live-dashboard-http.js +1 -1
  50. package/dist/src/gateway/live-dashboard-http.js.map +1 -1
  51. package/dist/src/gateway/progress-watch-machine.d.ts +6 -5
  52. package/dist/src/gateway/progress-watch-machine.js +6 -6
  53. package/dist/src/gateway/progress-watch-machine.js.map +1 -1
  54. package/dist/src/gateway/provenance-envelope.d.ts +1 -0
  55. package/dist/src/gateway/provenance-envelope.js +10 -3
  56. package/dist/src/gateway/provenance-envelope.js.map +1 -1
  57. package/dist/src/gateway/providers.d.ts +1 -0
  58. package/dist/src/gateway/providers.js +6 -0
  59. package/dist/src/gateway/providers.js.map +1 -1
  60. package/dist/src/gateway/service.d.ts +2 -0
  61. package/dist/src/gateway/service.js +25 -8
  62. package/dist/src/gateway/service.js.map +1 -1
  63. package/dist/src/gateway/store.d.ts +9 -21
  64. package/dist/src/gateway/store.js +284 -122
  65. package/dist/src/gateway/store.js.map +1 -1
  66. package/dist/src/gateway/types.d.ts +1 -2
  67. package/dist/src/gateway/types.js.map +1 -1
  68. package/docs/CONFIGURATION.md +102 -0
  69. package/docs/CONFIGURATION.zh-CN.md +72 -0
  70. package/docs/DASHBOARD.md +103 -0
  71. package/docs/DASHBOARD.zh-CN.md +47 -0
  72. package/docs/DELIVERY.md +84 -0
  73. package/docs/DELIVERY.zh-CN.md +53 -0
  74. package/docs/GATEWAY-ARCHITECTURE.md +177 -93
  75. package/package.json +8 -4
@@ -18,16 +18,21 @@ queued turn after idle, and delivered the exact final reply back to Claude.
18
18
  ## Purpose and boundary
19
19
 
20
20
  The gateway lets already-running Claude Code sessions and explicitly
21
- registered native Codex tasks address one another by short aliases. Outbound
22
- Codex-to-Claude sends require an explicitly selected Claude route; inbound
23
- native Claude messages may come from any exact compatible live same-UID
24
- session without making that session outbound-selected. It provides a single
25
- private operational view across the two products without rebuilding either
26
- agent runtime.
21
+ registered native Codex tasks address one another by short aliases. Both
22
+ directions require an explicit permission edge. The broker runs in `paired`
23
+ inbound mode by default, so an inbound native Claude message is admitted only
24
+ from a session that already holds a pair edge to the addressed Codex task;
25
+ `embassy serve --inbound open` is the explicit opt-out that restores any exact
26
+ compatible live same-UID session as an inbound sender without making it
27
+ outbound-selected. Outbound Codex-to-Claude sends likewise require the pair
28
+ edge, created by `pair` or the one-task `select-claude` shorthand. It provides
29
+ a single private operational view across the two products without rebuilding
30
+ either agent runtime.
27
31
 
28
32
  Its exact Claude Code 2.1.226 runtime/peer-protocol pin is fail-closed.
29
- Still-running 2.1.224 sessions remain compatible during a patch upgrade
30
- because their registry records use the same reviewed peer protocol 1 shape.
33
+ Still-running 2.1.224 and 2.1.225 sessions remain compatible during a patch
34
+ upgrade because their registry records use the same reviewed peer protocol 1
35
+ shape.
31
36
 
32
37
  It is deliberately:
33
38
 
@@ -129,7 +134,7 @@ probed by this project.
129
134
  │ │ callback replies
130
135
  ▼ ▼
131
136
  ┌──────────────────────── local singleton gateway ───────────────────────┐
132
- │ private control UDS │ transient bodies │ metadata state │ static HTML │
137
+ │ private control UDS │ retained bodies │ metadata state │ static HTML │
133
138
  └──────────┬──────────┴──────────────────┴────────────────┴──────────────┘
134
139
 
135
140
  ├─ local Codex App Server ─ registered native local tasks
@@ -153,7 +158,7 @@ The status below is intentionally narrower than the target architecture.
153
158
 
154
159
  | Component | Current evidence |
155
160
  | --- | --- |
156
- | Neutral gateway types, metadata store, route fencing, bounded queues, dedupe, rate limits, and public projection | **Implemented**, deterministic tests; message bodies persist under bounded retention (CO #36) |
161
+ | Neutral gateway types, metadata store, route fencing, bounded queues, dedupe, rate limits, and public projection | **Implemented**, deterministic tests; message bodies persist under bounded retention |
157
162
  | Private JSONL control protocol over a controller-owned UDS | **Implemented**, deterministic synthetic tests; no provider connection required |
158
163
  | Static metadata-only dashboard renderer and atomic publisher | **Implemented**, deterministic security tests; the static renderer requires no browser or HTTP server |
159
164
  | 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` |
@@ -312,9 +317,13 @@ final reply.
312
317
  text as untrusted user-role input. This inbound observation grants only a
313
318
  transient, in-memory capability for the correlated reply. It does not add a
314
319
  Claude route, flip `selected`, or authorize a later unsolicited send.
315
- In paired mode, a sender without the exact permission edge is refused before
316
- message admission with `SENDER_NOT_PAIRED`; because no message was accepted,
317
- that refusal is not added to the delivery journal.
320
+ In paired mode — the default — a sender without the exact permission edge is
321
+ refused before message admission with `SENDER_NOT_PAIRED`. No message is
322
+ accepted, so no delivery is created, but the refusal is not silent: the
323
+ broker records it as a `rejected` journal event carrying the direction, both
324
+ aliases, the byte count, and the safe error code, and increments the
325
+ rejected counters on the accounting, the source route, and any matching
326
+ pair.
318
327
  3. The Claude process's inherited messaging-socket value may be accepted as a
319
328
  transient reply address after strict validation. Claude Code exports
320
329
  `CLAUDE_CODE_MESSAGING_SOCKET` as a raw absolute socket path; the CLI
@@ -340,12 +349,15 @@ final reply.
340
349
  emit Claude's approval-specific native `held` control frame for ordinary
341
350
  queueing.
342
351
  8. In `merged` and `verbose` notice modes, if the delivery remains pending for
343
- exactly `floor(messageDeadlineMs / 2)`, the gateway may send the
344
- originating Claude session at most one nonterminal
352
+ exactly `min(floor(messageDeadlineMs / 2), 120_000)` milliseconds, the
353
+ gateway may send the originating Claude session at most one nonterminal
345
354
  `<gateway-delivery-stall>` user frame for that receipt. It contains only an
346
355
  allowlisted reason and a bounded `queued-for-ms` age; it is not a native
347
- `held` receipt and does not settle the delivery. `quiet` suppresses this
348
- gateway-authored frame without changing native status or dashboard state.
356
+ `held` receipt and does not settle the delivery. The two-minute ceiling is
357
+ deliberate: stall visibility must not scale with the deadline, so under the
358
+ default four-hour deadline the notice fires at two minutes, not two hours.
359
+ `quiet` suppresses this gateway-authored frame without changing native status
360
+ or dashboard state.
349
361
  9. When the task becomes idle, the connector refreshes the exact task state and
350
362
  starts the held message. A route retains at most three queued steering
351
363
  messages; accepting a fourth atomically cancels the oldest with safe code
@@ -402,9 +414,10 @@ next-tool-call-boundary rules above.
402
414
 
403
415
  The broker classifies `STEER:`, `TRACK:`, and `DONE:`, enforces raw-byte body
404
416
  limits, deduplicates, and queues before presentation framing.
405
- The store therefore retains only the raw transient body. A pure composer runs
406
- at the final semantic provider-write boundary so a clean retry produces the
407
- same bytes with exactly one authoritative outer wrapper. Automatic provider
417
+ The store therefore retains only the raw unframed body, never the composed
418
+ envelope. A pure composer runs at the final semantic provider-write boundary so
419
+ a clean retry produces the same bytes with exactly one authoritative outer
420
+ wrapper. Automatic provider
408
421
  startup and endpoint-generation validation, receipt frames, and diagnostics do
409
422
  not use this path.
410
423
 
@@ -425,15 +438,15 @@ broker-owned `cross-session-message` outer element and an
425
438
  `embassy reply --conversation ... --alias ...` instruction and the statement
426
439
  that caller, conversation, and route policy are rechecked.
427
440
 
428
- Embassy does not synthesize `from`, `from-session`, `hop-chain`, or `from-mode`
429
- attributes: those names have provider-native meanings the broker cannot
430
- truthfully claim. Native socket addresses, Codex thread IDs, Claude session
431
- UUIDs, endpoint generations, and route handles never enter the content frame.
441
+ Embassy does not synthesize `from`, `from-session`, or `from-mode` attributes:
442
+ those names have provider-native meanings the broker cannot truthfully claim.
443
+ Native socket addresses, Codex thread IDs, Claude session UUIDs, endpoint
444
+ generations, and route handles never enter the content frame.
432
445
 
433
446
  The outer structure and hint come only from validated broker metadata. Before
434
447
  composition, the untrusted body case-insensitively neutralizes boundary-shaped
435
- opening or closing occurrences of `cross-session-message` and
436
- `embassy-reply-hint` by inserting `\` immediately after the leading `<`.
448
+ opening or closing occurrences of Embassy's reserved framing tags by inserting
449
+ `\` immediately after the leading `<`.
437
450
  Everything else remains raw text. This is not general XML, cryptographic
438
451
  authentication, or proof that the message content is trustworthy; it is a
439
452
  consistent structural provenance marker at the model input boundary. A native
@@ -512,16 +525,22 @@ never displaced to admit a new send. A pressure-evicted handle returns
512
525
 
513
526
  ### Replies and process restarts
514
527
 
515
- Conversation IDs correlate replies, but callback addresses and message bodies
516
- exist only in memory. After a gateway restart, previously queued or in-flight
517
- metadata is marked abandoned; bodies are not recoverable and are never
518
- replayed. The prior Claude binding remains stored but stale. After authorized
519
- live discovery, one exact UUID-bound selection may be reactivated under its
520
- latest name. No queued text, pending reply, callback, native receipt handle,
521
- delivery token/status tracker, or conversation capability survives the
522
- restart; a prior token therefore returns `found: false`. A stale or offline
523
- selection can be explicitly removed by its stored alias or a user-supplied UUID
524
- without requiring discovery first.
528
+ Conversation IDs correlate replies, and callback addresses exist only in
529
+ memory, but queued message bodies are durable. After a gateway restart, each
530
+ queued message is triaged: one already past its deadline settles `expired`; one
531
+ whose target authority was transient, or whose target route is gone, settles
532
+ `abandoned` with `CONTROLLER_RESTARTED`; every other queued body is restored and
533
+ re-armed for dispatch, then sent exactly once after its exact route is
534
+ re-observed. A message in flight at the moment of the restart or crash settles
535
+ `ambiguous` with `CONTROLLER_RESTARTED` and is never replayed.
536
+
537
+ The prior Claude binding remains stored but stale. After authorized live
538
+ discovery, one exact UUID-bound selection may be reactivated under its latest
539
+ name. No pending reply, callback, native receipt handle, delivery token/status
540
+ tracker, or conversation capability survives the restart; a prior token
541
+ therefore returns `found: false`. A stale or offline selection can be explicitly
542
+ removed by its stored alias or a user-supplied UUID without requiring discovery
543
+ first.
525
544
 
526
545
  ## Gateway control plane
527
546
 
@@ -530,33 +549,41 @@ controller-owned mode-0700 state directory. The socket and state files are
530
549
  mode 0600. Frames are size-bounded and closed against unknown keys, methods,
531
550
  versions, and enum values.
532
551
 
533
- The small version 1 method family covers:
552
+ The closed version 1 method family is exactly these sixteen methods:
534
553
 
535
- - health and a safe public snapshot;
536
- - a read-only `observe_snapshot` projection, which may settle already-due
537
- lifecycle deliveries before projecting and is the only method the live
538
- dashboard companion calls;
539
- - explicit Codex registration, succession, and unregister;
540
- - explicit Claude selection and unselection from the current sanitized
554
+ - `health` and `list_snapshot`, a safe public snapshot;
555
+ - `observe_snapshot`, a read-only projection that may settle already-due
556
+ lifecycle deliveries before projecting;
557
+ - `register_codex`, `unregister_codex`, and
558
+ `remove_stale_codex_registration` explicit Codex registration and
559
+ succession, unregister, and broker-guarded removal of a stale orphan whose
560
+ owning endpoint generation is dead;
561
+ - `select_claude` and `unselect_claude`, from the current sanitized
541
562
  available-peer inventory;
542
- - delivery-status lookup by an opaque, memory-only correlation handle;
543
- - provider-specific send operations;
544
- - a correlated reply operation; and
545
- - dashboard refresh.
546
-
547
- The installed binary is `embassy` (`claude-codex-gateway` is a one-release
548
- deprecated alias). Its implemented commands are
549
- `serve`, `health`, `status`, `delivery-status`, `wait-delivery`,
563
+ - `pair` and `unpair`, the two-endpoint permission edge;
564
+ - `delivery_status`, a lookup by opaque, memory-only correlation handle;
565
+ - `untrack`, which closes one active progress watch by conversation token;
566
+ - `send_to_claude` and `send_to_codex`, the provider-specific sends;
567
+ - `reply`, the correlated reply operation; and
568
+ - `refresh_dashboard`, which refreshes Claude discovery and republishes.
569
+
570
+ The live dashboard companion calls `observe_snapshot` for every read; its
571
+ mutation route additionally calls `pair`, `unpair`,
572
+ `remove_stale_codex_registration`, and `refresh_dashboard`, and nothing else.
573
+
574
+ The installed binary is `embassy`, and it is the only installed binary. Its
575
+ seventeen implemented commands are
576
+ `serve`, `health`, `status`, `delivery-status`, `wait-delivery`, `untrack`,
550
577
  `refresh-dashboard`, `dashboard`, `register-codex`, `unregister-codex`,
551
- `select-claude`, `unselect-claude`, `send-to-claude`, `send-to-codex`, and
552
- `reply`. `dashboard` requires `--live` and accepts an optional
553
- `--lang en|zh-CN`; it starts the companion process rather than issuing a single
554
- control request. Message bodies are non-empty UTF-8 from standard input only,
555
- with a 16 KiB ceiling; they are never accepted in an argument or file. The
556
- client emits one bounded normalized JSON line and never returns a thread ID,
557
- provider-native ID, path, address, or message body. These commands require the
558
- foreground broker, except that `serve` starts it in the current terminal. It
559
- never daemonizes itself.
578
+ `select-claude`, `unselect-claude`, `pair`, `unpair`, `send-to-claude`,
579
+ `send-to-codex`, and `reply`. `dashboard` requires `--live` and accepts an
580
+ optional `--lang en|zh-CN` and `--port <n>`; it starts the companion process
581
+ rather than issuing a single control request. Message bodies are non-empty
582
+ UTF-8 from standard input only, with a 16 KiB ceiling; they are never accepted
583
+ in an argument or file. The client emits one bounded normalized JSON line and
584
+ never returns a thread ID, provider-native ID, path, address, or message body.
585
+ These commands require the foreground broker, except that `serve` starts it in
586
+ the current terminal. It never daemonizes itself.
560
587
 
561
588
  `select-claude --alias <current-name@host>` and
562
589
  `select-claude --session <uuid>` select the same logical session.
@@ -569,9 +596,19 @@ Codex registration, unregister, and Codex-to-Claude send require only a valid
569
596
  `CODEX_THREAD_ID`; they fail if a non-empty Claude messaging socket is also
570
597
  inherited. Claude-to-Codex send requires only the raw inherited Claude socket
571
598
  path and fails if a non-empty Codex thread ID is also present. `reply` likewise
572
- fails with both identities or neither. The operator-only health, status,
573
- dashboard refresh, live dashboard, select, unselect, and serve commands ignore
574
- provider identities.
599
+ fails with both identities or neither. `pair` and `unpair` also require an
600
+ exclusive inherited `CODEX_THREAD_ID`: naming both endpoints does not make them
601
+ operator commands, and they fail `CODEX_IDENTITY_REQUIRED` from a plain terminal
602
+ or `CALLER_IDENTITY_CONFLICT` from inside a Claude session.
603
+
604
+ `select-claude` and `unselect-claude` are the operator-runnable shorthand.
605
+ They do not ignore provider identity — they consume an inherited
606
+ `CODEX_THREAD_ID` when one is present, to resolve the Codex end, and reject a
607
+ malformed one — but they tolerate its absence and then resolve the Codex end
608
+ from the sole registered task. Selection is not a weaker operation than
609
+ `pair`: it atomically creates the same permission edge. The health, status,
610
+ dashboard refresh, live dashboard, untrack, and serve commands are the ones
611
+ that genuinely ignore provider identities.
575
612
 
576
613
  The foreground command is:
577
614
 
@@ -579,16 +616,22 @@ The foreground command is:
579
616
  embassy serve
580
617
  ```
581
618
 
619
+ `--inbound open` is the one security-relevant option:
620
+
621
+ ```text
622
+ embassy serve --inbound open
623
+ ```
624
+
625
+ The default is `paired`. `open` is the explicit opt-out that lets any exact
626
+ compatible live same-UID Claude session send inbound without a pair edge; it is
627
+ never implied and cannot be set through an environment variable.
628
+
582
629
  Before provider validation, listener creation, or App Server attachment, the
583
630
  launcher acquires one fixed host-wide crash-reclaimable owner lease under the
584
631
  verified login home. The lease is independent of `EMBASSY_STATE_DIR`, so two foreground
585
632
  controllers cannot be started for the same login account by choosing different
586
- state roots. During the one-release prototype transition, a recognized exact
587
- legacy default root is also controller-locked while Embassy runs. Any
588
- pre-existing legacy lock blocks startup as `GATEWAY_INSTANCE_IN_USE` and is
589
- preserved; the operator may remove that exact stale lock only after confirming
590
- no prototype process remains. Missing, unsafe, or unrecognized legacy roots
591
- are not created, imported, or mutated.
633
+ state roots. It is the only instance lock Embassy takes: the pre-rename
634
+ prototype state root is no longer read, locked, or mutated.
592
635
 
593
636
  It emits one normalized ready line, publishes the private dashboard, and
594
637
  holds the process until `SIGINT` or `SIGTERM`, when exact-owned resources are
@@ -606,6 +649,27 @@ Same-UID socket access is a local containment boundary, not proof of a trusted
606
649
  agent process. Every mutation additionally checks route ownership, exact
607
650
  thread/session generation, source alias, bounds, and conversation state.
608
651
 
652
+ ### Progress watches
653
+
654
+ `send-to-claude`, `send-to-codex`, and `reply` each accept an opt-in `--track`
655
+ flag that opens one progress watch over the resulting conversation, plus an
656
+ optional `--idle-minutes <n>` that sets how long the watched thread may sit idle
657
+ before the watch reports a stall. `n` is an integer from 1 through 1440 and
658
+ defaults to 5; supplying it without `--track` is an argument error. A body with
659
+ an exact leading `TRACK:` prefix opens the same watch at the default idle window
660
+ without the flag.
661
+
662
+ `untrack --conversation conv_<token>` closes an active watch explicitly, and a
663
+ body with an exact leading `DONE:` prefix closes it as completed; one message
664
+ may not both open and complete a watch (`PROGRESS_WATCH_SIGNAL_CONFLICT`). The
665
+ global `EMBASSY_TRACKING_ENABLED` switch is on by default and exact `0` disables
666
+ the surface; `EMBASSY_MAX_WATCHES` bounds concurrent watches at 32 by default
667
+ (hard cap 256).
668
+
669
+ A watch is independent evidence about thread activity, not delivery evidence. It
670
+ may outlive an opener whose own delivery expired, so check the opener's
671
+ `delivery-status` separately before assuming the assignment text arrived.
672
+
609
673
  ## Codex connectors and remote hosts
610
674
 
611
675
  In the target multi-host design, each allowlisted execution host has a separate
@@ -747,22 +811,34 @@ A static page is a point-in-time snapshot and never refreshes itself: it emits
747
811
  no meta refresh and the page tells the operator to re-run
748
812
  `embassy refresh-dashboard` and reload, or to use `embassy dashboard --live`.
749
813
 
750
- It shows only:
751
-
752
- - aggregate gateway and per-host connector health;
753
- - available/selected Claude aliases, registered Codex aliases, provider, host,
754
- compatibility, state, and queue depth;
755
- - normalized message direction and delivery state, timestamp, latency, byte
756
- count, and a short opaque message-ID suffix; and
757
- - allowlisted alerts such as stale route, protocol mismatch, queue full, or
758
- ambiguous delivery.
814
+ Each page assembles seven sections:
815
+
816
+ - **Exchange** — aggregate gateway health plus the pair graph: every explicit
817
+ Claude↔Codex edge and its per-edge counters.
818
+ - **Attention** — allowlisted alerts such as stale route, protocol mismatch,
819
+ queue full, or ambiguous delivery.
820
+ - **Transit** queued-message depth and bytes in flight.
821
+ - **Progress supervision** active progress watches and their state.
822
+ - **Operator activity** — the broker's bounded public journal of accepted
823
+ operator actions.
824
+ - **Sessions** — available/selected Claude aliases, registered Codex aliases,
825
+ provider, host, compatibility, state, and queue depth.
826
+ - **Diagnostics** — compatibility attestations per provider, per-host connector
827
+ health and protocol, deadline-pressure buckets, accounting totals, and the
828
+ omission counters below. Normalized message direction and delivery state,
829
+ timestamp, latency, byte count, and a short opaque message-ID suffix appear
830
+ with the delivery rows.
759
831
 
760
832
  It never shows message content, prompts, replies, transcripts, titles, working
761
833
  directories, native IDs, PIDs, socket paths, endpoint paths, tool data, raw
762
- events, stderr, credentials, or configuration contents. This is a
763
- controller-owned UI artifact, not a shared task file. The public snapshot has
764
- a 240 KiB projection budget and reports explicit omission counters if bounded
765
- connector, peer, route, message, or alert rows are truncated.
834
+ events, stderr, credentials, or configuration contents retained bodies reach
835
+ only the live companion, never these files. This is a controller-owned UI
836
+ artifact, not a shared task file. The public snapshot has a 240 KiB projection
837
+ budget and reports explicit omission counters whenever bounded rows are
838
+ truncated: connectors, available peers, routes, pairs, progress watches,
839
+ upstream and projected progress-watch events, upstream message events, message
840
+ groups, message events, upstream alerts, attention items, and upstream and
841
+ projected activity events.
766
842
 
767
843
  ### Live dashboard companion
768
844
 
@@ -825,9 +901,12 @@ The private store may retain:
825
901
  accounting and dashboard projection;
826
902
  - timestamps, counters, dedupe/rate-limit records, and safe error codes.
827
903
 
828
- It must never retain message bodies, provider output, prompts, replies, tool
829
- input/output, raw App Server or Claude frames, stderr, histories, credentials,
830
- Claude registry payloads, or callback/socket paths. The public snapshot is a
904
+ It also retains message bodies under bounded caps the queued body of every
905
+ undelivered message and the recent delivery ledger's retained bodies, evicted
906
+ oldest-first against a 1 MiB budget by default. It must never retain provider
907
+ output, tool input/output, raw App Server or Claude frames, stderr, histories,
908
+ credentials, Claude registry payloads, or callback/socket paths. The public
909
+ snapshot is a
831
910
  strict projection that also removes private route handles and endpoint
832
911
  generations. The state directory is mode 0700 and binding state is mode 0600;
833
912
  provider-native identifiers never enter normalized events, public snapshots,
@@ -836,14 +915,15 @@ every restored route begins stale and unusable. An authorized discovery may
836
915
  reactivate only the byte-identical durable Claude UUID after the current
837
916
  provider endpoint, workspace, complete unique discovery, and ownership lease
838
917
  all revalidate. The public `selected` bit flips only after that atomic private
839
- rebind succeeds. No queued body, callback, receipt handle, conversation, or
840
- reply capability is restored, and no delivery token or status tracker is
841
- reconstructed.
918
+ rebind succeeds. Queued bodies are restored and re-armed for dispatch; no
919
+ callback, receipt handle, conversation, or reply capability is restored, and no
920
+ delivery token or status tracker is reconstructed.
842
921
 
843
922
  The delivery-token mapping, queryable status tracker, native receipt handle,
844
923
  and one-stall-notice state are always memory-only. Durable delivery metadata
845
- does not contain enough information to reconstruct any of those capabilities
846
- or replay a body after restart.
924
+ does not contain enough information to reconstruct any of those capabilities.
925
+ A restored body is re-sent exactly once and only into its own exact
926
+ re-observed route; it is never replayed into a reconstructed capability.
847
927
 
848
928
  ## Minimum filesystem and process access
849
929
 
@@ -876,13 +956,13 @@ paths, peers, and transports:
876
956
 
877
957
  | Path/capability | Minimum purpose |
878
958
  | --- | --- |
879
- | `~/.local/bin/claude` and derived expected target `~/.local/share/claude/versions/2.1.226` | Stat the owned launcher/path components and read/execute only the resolved pinned target for bounded `--version`; live launcher validation succeeded |
959
+ | `~/.local/bin/claude` (or the absolute `EMBASSY_CLAUDE_BIN` override) and derived expected target `~/.local/share/claude/versions/2.1.226` | Stat the owned launcher/path components and read/execute only the resolved pinned target for bounded `--version`; `PATH` and interactive shell profiles are never searched; live launcher validation succeeded |
880
960
  | `~/.claude/sessions` | Read/enumerate only live registry JSON during the separately authorized passive-discovery gate |
881
961
  | `/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 |
882
962
  | `~/.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 |
883
963
  | `/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 |
964
+ | `/usr/bin/open` | Executed only by the opt-in `embassy dashboard --live` companion, to open the loopback dashboard URL in the operator's browser; no shell, a scrubbed fixed environment, and a bounded timeout and output cap |
884
965
  | `~/.local/state/agent-embassy/.gateway-host.lock` | Fixed per-login kernel-held lease acquired before provider setup; it remains here even when `EMBASSY_STATE_DIR` is overridden. Its bounded PID/token record is exact-cleanup metadata, not a path-only stale-lock authority; a crash releases the kernel lock and the next foreground process may acquire the existing file |
885
- | `~/.local/state/claude-agent-bridge/gateway/.claude-codex-gateway-state` and `.gateway-controller.lock` | For one release, bounded-read the exact legacy ownership marker and lock record; create and hold the lock only when absent, preserve any pre-existing lock, and read no other legacy state or message data |
886
966
  | `~/.local/state/agent-embassy` (or explicit `EMBASSY_STATE_DIR`) | Default controller-owned store, control UDS, state lock, and static dashboard; an explicit absolute configuration may replace only these state surfaces |
887
967
  | `~/.codex/packages/standalone` and `~/.codex/app-server-control/app-server-control.sock` | Resolve the pinned managed Codex binary and attach to the already-running private local App Server; never bootstrap or unlink it |
888
968
 
@@ -933,7 +1013,11 @@ the preferred least-context setup, but it is not mandatory.
933
1013
  fail-closed. Only exact retry is permitted until the old route is confirmed
934
1014
  unregistered and the controller is restarted.
935
1015
  - No ambiguous mutation is retried automatically.
936
- - No queued body survives process loss.
1016
+ - A queued body survives process loss under bounded retention and is re-sent
1017
+ exactly once after its exact route is re-observed. A message in flight when
1018
+ the process was lost settles `ambiguous` with `CONTROLLER_RESTARTED`; a
1019
+ message whose target authority was transient, or whose target route no longer
1020
+ exists, settles `abandoned` with the same code.
937
1021
  - A provider or Desktop update outside the release's exact reviewed pins fails
938
1022
  closed; retained state never admits an unreviewed version or activates an
939
1023
  unvalidated replacement endpoint generation.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-embassy",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "A local gateway for bidirectional messaging between Claude Code sessions and Codex tasks.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,8 +24,7 @@
24
24
  "darwin"
25
25
  ],
26
26
  "bin": {
27
- "embassy": "dist/src/gateway/cli.js",
28
- "claude-codex-gateway": "dist/src/gateway/cli.js"
27
+ "embassy": "dist/src/gateway/cli.js"
29
28
  },
30
29
  "files": [
31
30
  "dist/src/gateway",
@@ -39,7 +38,12 @@
39
38
  "assets/mark.svg",
40
39
  "assets/social-preview.png",
41
40
  "skills/embassy-peer",
42
- "docs/DESIGN.md",
41
+ "docs/CONFIGURATION.md",
42
+ "docs/CONFIGURATION.zh-CN.md",
43
+ "docs/DASHBOARD.md",
44
+ "docs/DASHBOARD.zh-CN.md",
45
+ "docs/DELIVERY.md",
46
+ "docs/DELIVERY.zh-CN.md",
43
47
  "docs/GATEWAY-ARCHITECTURE.md",
44
48
  "CHANGELOG.md",
45
49
  "CONTRIBUTING.md",