agents-can-communicate 0.8.0 → 0.8.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/bin/entrypoints/acc-mcp.mjs +0 -1
- package/bin/entrypoints/acc.mjs +0 -1
- package/docs/ADAPTER_AUTHORING.md +36 -17
- package/docs/ARCHITECTURE.md +9 -6
- package/docs/CAPABILITIES.md +23 -13
- package/docs/CLI.md +6 -3
- package/docs/PROTOCOL.md +2 -2
- package/docs/RELEASING.md +6 -4
- package/docs/TROUBLESHOOTING.md +2 -2
- package/node_modules/@agents-can-communicate/adapter-antigravity/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-antigravity/plugin/plugin.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-antigravity/src/adapter.mjs +2 -3
- package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/adapter.mjs +5 -4
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/inbox-delivery.mjs +12 -17
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/inbox-endpoint.mjs +2 -2
- package/node_modules/@agents-can-communicate/adapter-codex/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/src/adapter.mjs +3 -4
- package/node_modules/@agents-can-communicate/adapter-codex/src/app-server-client.mjs +10 -13
- package/node_modules/@agents-can-communicate/adapter-codex/src/live-permissions.mjs +5 -5
- package/node_modules/@agents-can-communicate/adapter-codex/src/maintenance-host.mjs +46 -10
- package/node_modules/@agents-can-communicate/adapter-codex/src/maintenance.mjs +5 -7
- package/node_modules/@agents-can-communicate/adapter-codex/src/native-delivery.mjs +16 -12
- package/node_modules/@agents-can-communicate/adapter-codex/src/native-endpoint.mjs +12 -2
- package/node_modules/@agents-can-communicate/adapter-codex/src/service-setup.mjs +26 -9
- package/node_modules/@agents-can-communicate/adapter-codex/src/ws-json-rpc.mjs +12 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/extension/gemini-extension.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-grok/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-kimi/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-sdk/src/certification.mjs +2 -2
- package/node_modules/@agents-can-communicate/adapter-sdk/src/index.mjs +3 -2
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-delivery.mjs +85 -77
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-vocabulary.mjs +4 -4
- package/node_modules/@agents-can-communicate/cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/cli/src/main.mjs +5 -6
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/maintenance.mjs +9 -15
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/pins.mjs +13 -4
- package/node_modules/@agents-can-communicate/core/package.json +1 -1
- package/node_modules/@agents-can-communicate/core/src/receipts.mjs +17 -2
- package/node_modules/@agents-can-communicate/core/src/status.mjs +14 -2
- package/node_modules/@agents-can-communicate/delivery-router/package.json +1 -1
- package/node_modules/@agents-can-communicate/delivery-router/src/refresh-binding.mjs +2 -2
- package/node_modules/@agents-can-communicate/delivery-router/src/router.mjs +9 -41
- package/node_modules/@agents-can-communicate/hook-runner/package.json +1 -1
- package/node_modules/@agents-can-communicate/hook-runner/src/client-pid.mjs +30 -1
- package/node_modules/@agents-can-communicate/hook-runner/src/native-binding.mjs +3 -4
- package/node_modules/@agents-can-communicate/hook-runner/src/process-table.mjs +30 -14
- package/node_modules/@agents-can-communicate/hook-runner/src/runner.mjs +3 -1
- package/node_modules/@agents-can-communicate/installer/package.json +1 -1
- package/node_modules/@agents-can-communicate/installer/src/delivery-diagnostics.mjs +0 -2
- package/node_modules/@agents-can-communicate/installer/src/detect.mjs +3 -4
- package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
- package/node_modules/@agents-can-communicate/protocol/package.json +1 -1
- package/node_modules/@agents-can-communicate/storage-filesystem/package.json +1 -1
- package/package.json +1 -1
|
@@ -54,7 +54,6 @@ await serve({
|
|
|
54
54
|
context: {
|
|
55
55
|
service,
|
|
56
56
|
deliveryRouter: createDeliveryRouter({ service, adapters, clock,
|
|
57
|
-
platform: `${process.platform}-${process.arch}`,
|
|
58
57
|
readLivePolicy: ({ adapter }) => readInstalledLivePolicy({ dataHome,
|
|
59
58
|
adapterId: adapter.id }) }),
|
|
60
59
|
workspaceId: descriptor.id,
|
package/bin/entrypoints/acc.mjs
CHANGED
|
@@ -25,7 +25,6 @@ const runtime = {
|
|
|
25
25
|
ids: { next: kind => createId(kind, randomBytes) },
|
|
26
26
|
createDeliveryRouter: ({ service, clock, dataHome }) =>
|
|
27
27
|
createDeliveryRouter({ service, adapters, clock,
|
|
28
|
-
platform: `${process.platform}-${process.arch}`,
|
|
29
28
|
readLivePolicy: ({ adapter }) => readInstalledLivePolicy({ dataHome,
|
|
30
29
|
adapterId: adapter.id }) }),
|
|
31
30
|
// Asked only by `acc config init`, and only when stdout is a terminal. There
|
|
@@ -81,10 +81,15 @@ is not evidence for compaction. Counts add no ids to `offeredMessageIds` or
|
|
|
81
81
|
|
|
82
82
|
`client.command` does double duty. `detect.mjs` uses it as the version-probe binary, and
|
|
83
83
|
presence liveness separately walks the hook's process ancestry for the first ancestor whose
|
|
84
|
-
executable basename matches it, to learn the client's own pid.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
executable basename matches it, to learn the client's own pid. A client shipped as a node
|
|
85
|
+
script, such as Gemini CLI, runs as `node`; for a `node` ancestor the walk also matches the
|
|
86
|
+
script it runs: the first argument after any interpreter options, by basename, with a
|
|
87
|
+
`.js`, `.mjs` or `.cjs` extension ignored. A word right after an option written without `=`
|
|
88
|
+
may be that option's value (`--require ./preload.cjs`), so it is also tried and the script
|
|
89
|
+
after it still counts. Declare the name the client actually runs as. A
|
|
90
|
+
command that matches neither resolves nothing, and the failure is silent: the session gets
|
|
91
|
+
`pid: null`, falls back to reading presence by age alone, and is not pinned to its
|
|
92
|
+
generation, with nothing telling you why.
|
|
88
93
|
|
|
89
94
|
## Declare only proven capabilities
|
|
90
95
|
|
|
@@ -148,9 +153,8 @@ to its capabilities:
|
|
|
148
153
|
|
|
149
154
|
```js
|
|
150
155
|
nativeDelivery: {
|
|
151
|
-
|
|
152
|
-
anchors: [{
|
|
153
|
-
protocolContract: "claude-code-inbox-socket-v1" }],
|
|
156
|
+
minimum: "2.1.282",
|
|
157
|
+
anchors: [{ version: "2.1.282", protocolContract: "claude-code-inbox-socket-v1" }],
|
|
154
158
|
knownBad: [],
|
|
155
159
|
activationKinds: ["native-service"],
|
|
156
160
|
offerKind: "wake",
|
|
@@ -159,17 +163,32 @@ nativeDelivery: {
|
|
|
159
163
|
|
|
160
164
|
The rules `defineAdapter` enforces, and the ones the runtime applies:
|
|
161
165
|
|
|
162
|
-
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
`
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
-
|
|
166
|
+
- The minimum is the **first passing capture**, never a guessed first vendor release. Every
|
|
167
|
+
anchor must have passing `delivery.livePush` evidence for the same client and version, and
|
|
168
|
+
the minimum must itself be an anchor. The platform an evidence row names is where the
|
|
169
|
+
capture was taken; it is provenance, and the anchor names no platform.
|
|
170
|
+
- There is intentionally **no maximum version** and **no platform**. A newer release is
|
|
171
|
+
admitted, on every platform, only when a current read-only feature probe
|
|
172
|
+
(`probeNativeDelivery()`) and a per-session handshake (`bindNativeSession()`) both report
|
|
173
|
+
the anchored `protocolContract`; `evaluateNativeEligibility()` and
|
|
174
|
+
`validateNativeHandshake()` are those two checks.
|
|
175
|
+
- A prerelease is judged by its release triple, as hook capabilities are: `2.2.0-beta.1` is
|
|
176
|
+
`2.2.0` against the minimum and `knownBad`. `knownBad` names exact versions or inclusive
|
|
177
|
+
intervals. A version that cannot be read at all is `version_unavailable`.
|
|
178
|
+
- Certification evidence still governs every non-native capability;
|
|
172
179
|
`effectiveCapabilities()` is unchanged. The native rule is used for live delivery alone.
|
|
180
|
+
|
|
181
|
+
What may refuse live delivery is exactly three things:
|
|
182
|
+
|
|
183
|
+
- the machine's own probe or per-session handshake failing;
|
|
184
|
+
- a client older than the first passing capture;
|
|
185
|
+
- a regression recorded in `knownBad`.
|
|
186
|
+
|
|
187
|
+
Nothing else: not the platform, not a prerelease suffix, not where the evidence was taken. A
|
|
188
|
+
transport that is technically different on some platform is the probe's fact to report, as
|
|
189
|
+
`native_delivery_unsupported`, never a gap in the captures. The 0.8.0 shape,
|
|
190
|
+
`minimumByPlatform` with a platform on each anchor, is refused by name; the design record is
|
|
191
|
+
`docs/design/2026-09-26-native-delivery-across-platforms.md` in the repository.
|
|
173
192
|
- Native methods return closed facts (`validateNativeActivationPlan()` closes the
|
|
174
193
|
activation plan) and never put vendor data - endpoints, sockets, raw errors - into core.
|
|
175
194
|
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -107,8 +107,9 @@ turn.
|
|
|
107
107
|
|
|
108
108
|
The Claude Code inbox wake has installed-client evidence on 2.1.282. Codex LocalDaemon
|
|
109
109
|
has it on 0.152.1 and 0.153.4. Antigravity CLI has it on 1.2.7 and later through a relay
|
|
110
|
-
the agent starts in its own shell. All three use a captured
|
|
111
|
-
current probe and exact session handshake
|
|
110
|
+
the agent starts in its own shell. All three use a captured minimum that applies on every
|
|
111
|
+
platform, a current probe and an exact session handshake; a prerelease is judged by its
|
|
112
|
+
release triple. Claude Code and Codex preserve the ordinary
|
|
112
113
|
client launch. Claude Code verifies its session registry, and Codex verifies its registered
|
|
113
114
|
thread and workspace. Native delivery owns no vendor daemon lifecycle. Gemini CLI and Kimi
|
|
114
115
|
Code have exact-version next-turn evidence only. Grok and generic MCP use inbox polling.
|
|
@@ -226,10 +227,12 @@ modes. It preserves explicit declarations and does not rewrite live leases. Miss
|
|
|
226
227
|
changed generation files and unknown native bindings remain unverified holds.
|
|
227
228
|
A session is also pinned to the generation
|
|
228
229
|
it started with: its hooks delegate to that generation while the pin declares the same
|
|
229
|
-
store contract this one does, and run the active generation otherwise.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
230
|
+
store contract this one does, and run the active generation otherwise. Apart from
|
|
231
|
+
`SessionEnd`, only the client's confirmed death releases a pin, so a session whose client
|
|
232
|
+
process cannot be found is not pinned, and a pin that names no client is reaped. A
|
|
233
|
+
generation directory is reclaimed once no control pointer, live lease, live pin or staging
|
|
234
|
+
hold names it. Parsed help, version, and update recovery remain available when workspace
|
|
235
|
+
admission is unavailable.
|
|
233
236
|
|
|
234
237
|
An independent worker downloads npm packages with lifecycle scripts disabled, checks exact
|
|
235
238
|
stable package identity and the discovered integrity, and health-checks the staged runtime.
|
package/docs/CAPABILITIES.md
CHANGED
|
@@ -20,9 +20,9 @@ Capability honesty separates four questions that are easy to collapse:
|
|
|
20
20
|
A source method or vendor documentation is not certification. A capture applies forward: from
|
|
21
21
|
the version that recorded it until a later capture changes that capability, and to every
|
|
22
22
|
platform until one of them records something of its own. A client older than every capture
|
|
23
|
-
degrades to false, and so does a capability a capture recorded as failing. Native
|
|
24
|
-
minimum
|
|
25
|
-
stronger peer's capability.
|
|
23
|
+
degrades to false, and so does a capability a capture recorded as failing. Native live
|
|
24
|
+
delivery reads its captured minimum the same way: on every platform, with a prerelease judged
|
|
25
|
+
by its release triple. No weaker session inherits a stronger peer's capability.
|
|
26
26
|
|
|
27
27
|
Run `acc doctor` in the project when observed behavior differs from this page. It reports
|
|
28
28
|
the installed client version, platform, effective capability, and fallback instead of
|
|
@@ -53,9 +53,18 @@ The native rows are `no` at the hook versions this matrix names.
|
|
|
53
53
|
Separate installed-client captures establish Codex `livePush` on 0.152.1 and
|
|
54
54
|
0.153.4, Claude Code `livePush` on 2.1.282 through the session inbox, and
|
|
55
55
|
Antigravity CLI `livePush` on 1.2.7 and later through a relay the agent starts in its own shell.
|
|
56
|
-
Native eligibility uses the captured
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
Native eligibility uses the captured minimum, a current feature probe, and an exact
|
|
57
|
+
per-session handshake. It is experimental and requires recipient opt-in.
|
|
58
|
+
|
|
59
|
+
What may refuse live delivery is exactly three things:
|
|
60
|
+
|
|
61
|
+
- the machine's own probe or per-session handshake failing;
|
|
62
|
+
- a client older than the first passing capture;
|
|
63
|
+
- a regression recorded in `knownBad`.
|
|
64
|
+
|
|
65
|
+
Nothing else: not the platform, not a prerelease suffix, not where the evidence was taken.
|
|
66
|
+
A platform whose transport is technically different is refused by the probe as a fact about
|
|
67
|
+
that transport, which is what Claude Code's named pipe on native Windows is. Codex, Claude Code and Antigravity CLI reply through `acc reply`. Their native
|
|
59
68
|
`replyRoute` remains false.
|
|
60
69
|
|
|
61
70
|
The limitations belong next to the adapters they affect:
|
|
@@ -64,7 +73,7 @@ The limitations belong next to the adapters they affect:
|
|
|
64
73
|
|---|---|
|
|
65
74
|
| Antigravity CLI | 1.2.7 on darwin-arm64, captured in print mode, and every later release by the forward rule. Only `SessionStart`, `PreInvocation`, `PostInvocation` and `Stop` load; `SessionEnd`, `PreToolUse` and `PostToolUse` are accepted into the config file and silently dropped, so there is no tool guard and no session-end deregistration - a session goes offline by presence age or an explicit `acc finish`. Payloads carry no `hook_event_name`, so each registered command passes its own event name. The end-of-turn `Stop` continuation reaches the model and is a bounded nudge, not a gate: ACC continues a turn at most once and fails open, and the client caps consecutive continuations itself (vendor 1.1.9). `agy agentapi send-message` can wake an idle session - captured - but only with that session's language-server address and CSRF token, which exist in the agent's own shell and in no hook. ACC does not take that token, so live push and reply routing are false. A peer message that arrives while the model writes its last answer is carried by the `Stop` continuation instead. A write that parses can register nothing, so install and doctor read `agy -p "/hooks"` back instead of trusting the file. Live push (1.2.7, darwin-arm64, TUI only, experimental, recorded opt-in) runs through a relay the agent starts once per conversation from its own shell - the only process holding the session endpoint - after ACC's context asks it to, up to three times while none is serving; the operator approves that command at the client's permission prompt. An idle session wakes; a busy one sees the message after its running answer, or at the next model invocation when the turn waits on a tool. Print mode and the first session in a folder trusted at that launch get no relay. |
|
|
66
75
|
| Codex | Next-turn context, captured on 0.147.0, requires plugin trust. The observed stock 0.153.4 upgrade from ACC 0.3.1 to 0.4 required fresh review of five modified hook definitions; a subsequent restart retained all five active (activation evidence, not new event certification). LocalDaemon native delivery was captured through the installed package on 0.152.1 and 0.153.4, darwin-arm64; minimum 0.152.1, recorded opt-in, current feature probe and exact thread/cwd/process/version/protocol checks are required. Ordinary launch preserves the receiver workspace without ACC arguments or daemon ownership. Embedded or unreachable sessions keep their inbox. Native `replyRoute` remains false. |
|
|
67
|
-
| Claude Code | 2.1.233 next-turn delivery waits for the next user prompt. From 2.1.282 on darwin-arm64, `delivery.livePush` is a live capability behind the native contract (experimental, off until opted in). ACC wakes the session through the inbox socket that Claude Code opens for every session, with one line of fixed ACC text and the message id. An idle session starts a turn. A busy session takes the wake between two tool calls. Each delivered wake fires `UserPromptSubmit`, so the next-turn hook shows the body and records the receipt `offered` via `next-turn`. The model answers with `acc reply`, so `delivery.replyRoute` stays false. A session in `bypassPermissions` mode holds each wake for approval unless `crossSessionInbound` is `accept`, and `refuse` drops wakes. Native Windows keeps next-turn delivery: its inbox is a named pipe that requires an auth line, and it is uncaptured. Linux is uncaptured. |
|
|
76
|
+
| Claude Code | 2.1.233 next-turn delivery waits for the next user prompt. From 2.1.282, captured on darwin-arm64 and applied on every platform whose inbox is a Unix socket, `delivery.livePush` is a live capability behind the native contract (experimental, off until opted in). ACC wakes the session through the inbox socket that Claude Code opens for every session, with one line of fixed ACC text and the message id. An idle session starts a turn. A busy session takes the wake between two tool calls. Each delivered wake fires `UserPromptSubmit`, so the next-turn hook shows the body and records the receipt `offered` via `next-turn`. The model answers with `acc reply`, so `delivery.replyRoute` stays false. A session in `bypassPermissions` mode holds each wake for approval unless `crossSessionInbound` is `accept`, and `refuse` drops wakes. Native Windows keeps next-turn delivery: its inbox is a named pipe that requires an auth line, and it is uncaptured. Linux is uncaptured. |
|
|
68
77
|
| Gemini CLI | Package-shipped next-turn certification starts at 0.57.0. Its TUI has no captured external wake or queue interface and `--acp` changes launch ownership, so native delivery is fallback-only; live push and reply routing remain false. |
|
|
69
78
|
| Grok | The Grok 1.0.24 installed-client check observed own CLI arguments after a terminal result, followed by owned work, message, and finish calls. This identity-only path does not certify peer-context injection. Documentation-shaped payloads do not count as real captures. The public leader surface exposed no proven addressed injection into an ordinary TUI session, so native delivery is `awaiting_compatibility_capture`; all capabilities remain false. |
|
|
70
79
|
| Kimi Code | Next-turn and guard evidence was captured on 0.36.1, plus a 60-second heartbeat. Its server/queue APIs do not prove a transparent binding to an independently opened session, so native delivery is fallback-only. |
|
|
@@ -93,10 +102,11 @@ demand without a client heartbeat. Retirement is final: neither route may revive
|
|
|
93
102
|
retired binding, a closed session or an old generation. Delivery lease refresh does
|
|
94
103
|
not renew participant presence.
|
|
95
104
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
105
|
+
Codex LocalDaemon, the Claude Code inbox wake and the Antigravity CLI relay have captured
|
|
106
|
+
experimental live delivery, on darwin-arm64. Those captures set the minimum that applies on
|
|
107
|
+
every platform; on a host they never named, the probe and the per-session handshake decide,
|
|
108
|
+
and a transport that does not answer keeps the durable inbox. Other adapters retain their
|
|
109
|
+
separately certified next-turn paths or inbox polling.
|
|
100
110
|
|
|
101
111
|
Codex's 120-second delivery lease can refresh on demand for the same live endpoint;
|
|
102
112
|
it does not extend the 24-hour participant presence limit. A TUI exit can leave its
|
|
@@ -116,8 +126,8 @@ policy:
|
|
|
116
126
|
| `all` | every addressed message kind may use live push |
|
|
117
127
|
|
|
118
128
|
The default is `off`. `acc install --delivery actionable|all` records recipient consent.
|
|
119
|
-
Current activation remains off when the
|
|
120
|
-
|
|
129
|
+
Current activation remains off when the captured minimum or the feature probe does not
|
|
130
|
+
qualify; that temporary failure does not erase the requested policy. Each
|
|
121
131
|
session must pass its own generation-bound handshake. Every adapter reads consent from the
|
|
122
132
|
installation record and rereads it at hooks and before offers, so a long-running vendor
|
|
123
133
|
process cannot retain permission through an old binding snapshot.
|
package/docs/CLI.md
CHANGED
|
@@ -104,9 +104,12 @@ got. Two sessions of one participant show the same numbers, so read the workspac
|
|
|
104
104
|
`<n> offered, not retrieved` when that number is above zero. Offered is not read: a
|
|
105
105
|
transport took the bytes, and the model may or may not have looked.
|
|
106
106
|
|
|
107
|
-
A participant with no open session is left out, unless messages still wait for it
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
A participant with no open session is left out, unless messages still wait for it: queued,
|
|
108
|
+
or offered by a live transport and not yet shown again, so that its next turn would show
|
|
109
|
+
them. A message the next-turn hook already showed does not wait, since nothing will reach the
|
|
110
|
+
participant for it again. A waiting participant's latest session stays listed as `offline`,
|
|
111
|
+
with that number as `waiting`, and with `endReason` when the client said why the session
|
|
112
|
+
ended, such as `"clear"` for Claude Code's `/clear`. The text line names each one:
|
|
110
113
|
`waiting for a closed session: claude_code-b (cleared, 2)`. `acc status --all` lists every
|
|
111
114
|
session. Default `sync` reads
|
|
112
115
|
events after a 16-digit event cursor (100 by
|
package/docs/PROTOCOL.md
CHANGED
|
@@ -316,8 +316,8 @@ The recipient owns `livePolicy` because native push may start a model turn:
|
|
|
316
316
|
|
|
317
317
|
Default is `off`. Policy never creates a capability. The router requires one
|
|
318
318
|
current eligible recipient generation, supported client evidence, a verified
|
|
319
|
-
binding and adapter acceptance. The Claude Code inbox wake uses a
|
|
320
|
-
|
|
319
|
+
binding and adapter acceptance. The Claude Code inbox wake uses a 2.1.282 minimum,
|
|
320
|
+
captured on darwin-arm64 and applied on every platform. Its binding requires the same pid and socket in Claude Code's
|
|
321
321
|
session registry, and every refresh and wake also requires the same session id. Codex LocalDaemon uses a 0.152.1 minimum, confirmed
|
|
322
322
|
through installed-product captures on 0.152.1 and 0.153.4. Recorded consent and
|
|
323
323
|
exact thread, cwd, process, version and protocol verification govern every Codex session.
|
package/docs/RELEASING.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Releasing
|
|
2
2
|
|
|
3
3
|
Use this procedure to build one auditable npm artifact, verify that exact tarball, and keep
|
|
4
|
-
its evidence tied to the commit that supplied its bytes. The
|
|
5
|
-
|
|
4
|
+
its evidence tied to the commit that supplied its bytes. The commands derive the version
|
|
5
|
+
from `package.json` so the filename cannot drift.
|
|
6
6
|
|
|
7
7
|
```mermaid
|
|
8
8
|
graph LR
|
|
@@ -118,8 +118,10 @@ work belongs under `Unreleased` and gets a new candidate record.
|
|
|
118
118
|
## Capture only capabilities you observed
|
|
119
119
|
|
|
120
120
|
Run native real-client checks on the exact versions and platforms recorded in
|
|
121
|
-
retained fixtures.
|
|
122
|
-
|
|
121
|
+
retained fixtures. A capture records evidence, and it never gates beyond that floor: what it
|
|
122
|
+
proves applies from its version onward and on every platform, and what refuses a client is
|
|
123
|
+
the machine's own probe or handshake, an older client, or a recorded regression.
|
|
124
|
+
The Claude Code inbox wake uses its captured minimum plus a current probe and
|
|
123
125
|
per-session handshake. Its passing capture is a real-client product run of the packed
|
|
124
126
|
candidate, launched with the ordinary command and installed hooks. Codex LocalDaemon
|
|
125
127
|
uses a separate 0.152.1 minimum with installed-product captures on 0.152.1 and
|
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -240,8 +240,8 @@ local socket permissions as well; a writable state directory alone is insufficie
|
|
|
240
240
|
## Gemini does not guard a write
|
|
241
241
|
|
|
242
242
|
Default and `plan` modes expose no write tool to the model. `auto_edit` exposes edit tools;
|
|
243
|
-
shell availability depends on approval mode.
|
|
244
|
-
|
|
243
|
+
shell availability depends on approval mode. Gemini CLI next-turn delivery is certified from
|
|
244
|
+
0.57.0 onward, on every platform; an older client still uses inbox.
|
|
245
245
|
|
|
246
246
|
From 0.55 onwards there is a quieter cause with the same symptom: an untrusted folder. The
|
|
247
247
|
client prints `Approval mode overridden to "default" because the current folder is not
|
|
@@ -76,9 +76,8 @@ export function createAntigravityAdapter() {
|
|
|
76
76
|
// no retireNativeSession here: the hook runner retires and re-publishes the
|
|
77
77
|
// binding on every turn, and must not take the relay with it.
|
|
78
78
|
nativeDelivery: {
|
|
79
|
-
|
|
80
|
-
anchors: [{
|
|
81
|
-
protocolContract: PROTOCOL_CONTRACT }],
|
|
79
|
+
minimum: ANTIGRAVITY_CLI_VERSION,
|
|
80
|
+
anchors: [{ version: ANTIGRAVITY_CLI_VERSION, protocolContract: PROTOCOL_CONTRACT }],
|
|
82
81
|
knownBad: [], activationKinds: ["native-config"], policySource: "installation-record",
|
|
83
82
|
},
|
|
84
83
|
probeNativeDelivery, planNativeActivation, bindNativeSession, refreshNativeSession,
|
|
@@ -22,8 +22,9 @@ export const CLAUDE_DELIVERY_FALLBACK = Object.freeze({
|
|
|
22
22
|
* and a parent/child mapping claimed without observation is the kind of thing
|
|
23
23
|
* that quietly maps every child onto its parent. `startupInjection` and
|
|
24
24
|
* `safePointInjection` were not exercised - only the before-turn path was.
|
|
25
|
-
* Native live delivery is the inbox wake, certified from 2.1.282 on
|
|
26
|
-
* darwin-arm64
|
|
25
|
+
* Native live delivery is the inbox wake, certified from 2.1.282 (captured on
|
|
26
|
+
* darwin-arm64, applied on every platform the probe admits); reply routing
|
|
27
|
+
* through the transport is not claimed.
|
|
27
28
|
*/
|
|
28
29
|
export function createClaudeCodeAdapter() {
|
|
29
30
|
return defineAdapter({
|
|
@@ -52,8 +53,8 @@ export function createClaudeCodeAdapter() {
|
|
|
52
53
|
// newer stable client is admitted by the probe and the per-session
|
|
53
54
|
// handshake. The offer is a wake; see src/inbox-delivery.mjs.
|
|
54
55
|
nativeDelivery: {
|
|
55
|
-
|
|
56
|
-
anchors: [{
|
|
56
|
+
minimum: MIN_VERSION,
|
|
57
|
+
anchors: [{ version: MIN_VERSION, protocolContract: PROTOCOL_CONTRACT }],
|
|
57
58
|
knownBad: [],
|
|
58
59
|
activationKinds: ["native-service"],
|
|
59
60
|
policySource: "installation-record",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import net from "node:net";
|
|
2
|
+
import { compareVersionOrder, versionOrder } from "@agents-can-communicate/adapter-sdk";
|
|
2
3
|
|
|
3
4
|
import { INBOX_MODES, MIN_VERSION, PROTOCOL_CONTRACT, TRANSPORT } from "./inbox-contract.mjs";
|
|
4
5
|
import { claimWake, newEndpointId, readInboxEndpoint, releaseWake, removeInboxEndpoint,
|
|
@@ -26,7 +27,6 @@ const LEASE_MS = 120_000;
|
|
|
26
27
|
const MESSAGE_ID = /^[A-Za-z0-9_-]{1,200}$/;
|
|
27
28
|
const PROBE_NEEDLE = Buffer.from("messagingSocketPath");
|
|
28
29
|
const PROBE_MAX_BYTES = 256 * 1024 * 1024;
|
|
29
|
-
const STABLE_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z.-]+)?$/;
|
|
30
30
|
|
|
31
31
|
export { INBOX_MODES, MIN_VERSION, PROTOCOL_CONTRACT, TRANSPORT };
|
|
32
32
|
|
|
@@ -37,12 +37,11 @@ export const wakeText = messageId => `ACC: new peer message ${messageId} for thi
|
|
|
37
37
|
+ `acc inbox --message ${messageId}. Answer it through ACC with acc reply --message ${messageId}; `
|
|
38
38
|
+ "SendMessage cannot deliver to an ACC participant.";
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
40
|
+
// A version is judged by its release triple: a prerelease of a version above
|
|
41
|
+
// the minimum is not an older client, and the inbox in the executable is what
|
|
42
|
+
// admits it. An unreadable version orders as null.
|
|
43
|
+
const belowMinimum = clientVersion => compareVersionOrder(versionOrder(clientVersion),
|
|
44
|
+
versionOrder(MIN_VERSION)) < 0;
|
|
46
45
|
|
|
47
46
|
// The registry field only a build with the inbox writes. Read-only, bounded.
|
|
48
47
|
async function executableHasInbox(realExecutable) {
|
|
@@ -92,8 +91,8 @@ function withTimeout(work, ms) {
|
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
/**
|
|
95
|
-
* Read-only: a
|
|
96
|
-
*
|
|
94
|
+
* Read-only: a version at or above the capture, a platform whose inbox is a
|
|
95
|
+
* Unix socket, and an executable that writes the inbox into its session
|
|
97
96
|
* registry. Never launches a session. Native Windows serves a named pipe that
|
|
98
97
|
* demands an auth line; nothing there is captured, so it keeps hook delivery.
|
|
99
98
|
*/
|
|
@@ -105,10 +104,8 @@ export async function probeNativeDelivery({ realExecutable, timeoutMs = 750, pla
|
|
|
105
104
|
if (typeof realExecutable !== "string" || realExecutable === "") return unsupported("feature_probe_failed");
|
|
106
105
|
const clientVersion = await withTimeout(Promise.resolve(readVersion(realExecutable, timeoutMs)), timeoutMs)
|
|
107
106
|
.catch(() => null);
|
|
108
|
-
if (clientVersion === null
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
if (compare(clientVersion, MIN_VERSION) < 0) return unsupported("below_minimum_version", clientVersion);
|
|
107
|
+
if (versionOrder(clientVersion) === null) return unsupported("feature_probe_failed", clientVersion);
|
|
108
|
+
if (belowMinimum(clientVersion)) return unsupported("below_minimum_version", clientVersion);
|
|
112
109
|
const present = await withTimeout(Promise.resolve(hasInbox(realExecutable)), timeoutMs).catch(() => false);
|
|
113
110
|
if (!present) return unsupported("protocol_mismatch", clientVersion);
|
|
114
111
|
return { supported: true, clientVersion, protocolContract: PROTOCOL_CONTRACT,
|
|
@@ -145,13 +142,11 @@ export async function bindNativeSession({ event, clientPid, clientVersion, runti
|
|
|
145
142
|
if (typeof event?.sessionId !== "string" || event.sessionId === "") {
|
|
146
143
|
return closed(clientVersion, "handshake_failed");
|
|
147
144
|
}
|
|
148
|
-
if (
|
|
149
|
-
return closed(clientVersion, "version_unavailable");
|
|
150
|
-
}
|
|
145
|
+
if (versionOrder(clientVersion) === null) return closed(clientVersion, "version_unavailable");
|
|
151
146
|
// The contract refuses it anyway, after this returns. Refusing here keeps an
|
|
152
147
|
// older client - which binds again on every turn - from writing an endpoint
|
|
153
148
|
// record each time.
|
|
154
|
-
if (
|
|
149
|
+
if (belowMinimum(clientVersion)) return closed(clientVersion, "below_minimum_version");
|
|
155
150
|
const socketPath = env?.CLAUDE_CODE_MESSAGING_SOCKET;
|
|
156
151
|
if (typeof socketPath !== "string" || socketPath === "") {
|
|
157
152
|
return closed(clientVersion, "native_endpoint_unavailable");
|
|
@@ -2,6 +2,7 @@ import { randomBytes } from "node:crypto";
|
|
|
2
2
|
import { constants } from "node:fs";
|
|
3
3
|
import { lstat, mkdir, open, readdir, realpath, rename, rm } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { versionOrder } from "@agents-can-communicate/adapter-sdk";
|
|
5
6
|
|
|
6
7
|
import { PROTOCOL_CONTRACT } from "./inbox-contract.mjs";
|
|
7
8
|
import { inboxSocketIsSafe } from "./inbox-registry.mjs";
|
|
@@ -14,7 +15,6 @@ import { isReception } from "./inbox-settings.mjs";
|
|
|
14
15
|
const KEYS = ["schemaVersion", "endpointId", "socketPath", "configDir", "clientPid", "sessionId",
|
|
15
16
|
"clientVersion", "protocolContract", "leaseUntil", "reception"];
|
|
16
17
|
const ENDPOINT = /^claude_inbox_[a-f0-9]{32}$/;
|
|
17
|
-
const STABLE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z.-]+)?$/;
|
|
18
18
|
const MAX_BYTES = 8_192;
|
|
19
19
|
const absolute = value => typeof value === "string" && path.isAbsolute(value) && !value.includes("\0");
|
|
20
20
|
const own = info => typeof process.getuid !== "function" || info.uid === process.getuid();
|
|
@@ -29,7 +29,7 @@ function valid(record) {
|
|
|
29
29
|
&& absolute(record.socketPath) && absolute(record.configDir)
|
|
30
30
|
&& Number.isInteger(record.clientPid) && record.clientPid > 0
|
|
31
31
|
&& typeof record.sessionId === "string" && record.sessionId !== "" && record.sessionId.length <= 200
|
|
32
|
-
&& typeof record.clientVersion === "string" &&
|
|
32
|
+
&& typeof record.clientVersion === "string" && versionOrder(record.clientVersion) !== null
|
|
33
33
|
&& record.protocolContract === PROTOCOL_CONTRACT
|
|
34
34
|
&& typeof record.leaseUntil === "string"
|
|
35
35
|
&& /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z$/.test(record.leaseUntil)
|
|
@@ -18,7 +18,7 @@ import { planCodexInstall, detectCodex, installCodexPlugin, preflightCodexUninst
|
|
|
18
18
|
export { CODEX_QUEUE_MINIMUM };
|
|
19
19
|
export const CODEX_VERSION = "0.147.0";
|
|
20
20
|
export const CODEX_DELIVERY_FALLBACK = Object.freeze({
|
|
21
|
-
diagnostic: "Codex native delivery requires codex-cli 0.152.1 or newer
|
|
21
|
+
diagnostic: "Codex native delivery requires codex-cli 0.152.1 or newer, "
|
|
22
22
|
+ "recorded recipient consent and a reachable LocalDaemon session with exact thread, cwd, "
|
|
23
23
|
+ "process, version and protocol verification. Embedded or unreachable sessions retain "
|
|
24
24
|
+ "durable messages. Message delivery does not start, restart or stop the vendor daemon and adds no "
|
|
@@ -61,9 +61,8 @@ export function createCodexAdapter() {
|
|
|
61
61
|
// Native acceleration is a separate, per-session eligibility decision.
|
|
62
62
|
|
|
63
63
|
nativeDelivery: {
|
|
64
|
-
|
|
65
|
-
anchors: [{
|
|
66
|
-
protocolContract: PROTOCOL_CONTRACT }], knownBad: [],
|
|
64
|
+
minimum: CODEX_QUEUE_MINIMUM,
|
|
65
|
+
anchors: [{ version: CODEX_QUEUE_MINIMUM, protocolContract: PROTOCOL_CONTRACT }], knownBad: [],
|
|
67
66
|
activationKinds: ["native-service"], policySource: "installation-record",
|
|
68
67
|
},
|
|
69
68
|
probeNativeDelivery, planNativeActivation, bindNativeSession,
|
|
@@ -2,6 +2,8 @@ import os from "node:os";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { realpath, stat } from "node:fs/promises";
|
|
4
4
|
|
|
5
|
+
import { compareVersionOrder, versionOrder } from "@agents-can-communicate/adapter-sdk";
|
|
6
|
+
|
|
5
7
|
import { openWebSocketPeer } from "./ws-json-rpc.mjs";
|
|
6
8
|
|
|
7
9
|
// The Codex App Server queue protocol, captured on codex-cli 0.152.1. Every
|
|
@@ -18,7 +20,6 @@ export const MINIMUM_VERSION = "0.152.1";
|
|
|
18
20
|
export const CODEX_QUEUE_MINIMUM = MINIMUM_VERSION;
|
|
19
21
|
export const QUEUE_MODES = Object.freeze(["livePush", "idleWake", "busyQueue"]);
|
|
20
22
|
const CLIENT_INFO = Object.freeze({ name: "agents-can-communicate", version: "0.2.0" });
|
|
21
|
-
const STABLE_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
22
23
|
const MAX_PAGES = 20;
|
|
23
24
|
const METHOD_NOT_FOUND = -32601;
|
|
24
25
|
const INVALID_REQUEST = -32600;
|
|
@@ -27,15 +28,11 @@ export const controlSocketPath = (env = process.env) =>
|
|
|
27
28
|
path.join(env.CODEX_HOME ?? path.join(os.homedir(), ".codex"),
|
|
28
29
|
"app-server-control", "app-server-control.sock");
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
const b = parseStableVersion(right);
|
|
36
|
-
for (let index = 0; index < 3; index += 1) if (a[index] !== b[index]) return a[index] < b[index] ? -1 : 1;
|
|
37
|
-
return 0;
|
|
38
|
-
}
|
|
31
|
+
// A version is judged by its release triple, the same order certification
|
|
32
|
+
// evidence uses: a prerelease daemon above the minimum is admitted by the queue
|
|
33
|
+
// method it answers with, not refused for its suffix. Unreadable orders as null.
|
|
34
|
+
export { versionOrder };
|
|
35
|
+
export const compareVersions = (left, right) => compareVersionOrder(versionOrder(left), versionOrder(right));
|
|
39
36
|
export function serverVersionOf(userAgent) {
|
|
40
37
|
return /^[^\s/]+\/(\d+\.\d+\.\d+(?:-[0-9A-Za-z.]+)?)(?=[\s(]|$)/
|
|
41
38
|
.exec(String(userAgent ?? ""))?.[1] ?? null;
|
|
@@ -58,10 +55,10 @@ export async function initializeCodex(peer) {
|
|
|
58
55
|
|
|
59
56
|
export async function probeCodexQueue(peer, { threadId, minimum = MINIMUM_VERSION } = {}) {
|
|
60
57
|
const serverVersion = await initializeCodex(peer);
|
|
61
|
-
if (
|
|
62
|
-
return { supported: false, serverVersion, reasonCode: "
|
|
58
|
+
if (versionOrder(serverVersion) === null) {
|
|
59
|
+
return { supported: false, serverVersion, reasonCode: "version_unavailable" };
|
|
63
60
|
}
|
|
64
|
-
if (
|
|
61
|
+
if (compareVersions(serverVersion, minimum) < 0) {
|
|
65
62
|
return { supported: false, serverVersion, reasonCode: "below_minimum_version" };
|
|
66
63
|
}
|
|
67
64
|
try {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { channelSocketDirectory, tomlString } from "@agents-can-communicate/adapter-sdk";
|
|
3
|
-
import {
|
|
3
|
+
import { compareVersions } from "./app-server-client.mjs";
|
|
4
4
|
import { addPermissions, inspectPermissions } from "./permission-ownership.mjs";
|
|
5
5
|
import { scanConfig } from "./toml-scan.mjs";
|
|
6
6
|
|
|
@@ -8,9 +8,9 @@ const PROFILE = "acc-workspace";
|
|
|
8
8
|
const MINIMUM = "0.153.4";
|
|
9
9
|
const equal = (keys, expected) => JSON.stringify(keys) === JSON.stringify(expected);
|
|
10
10
|
const literal = value => { try { return JSON.parse(value); } catch { return null; } };
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
// Captured on darwin-arm64 with 0.153.4; the capture's minimum applies on
|
|
12
|
+
// every platform, and a prerelease is judged by its release triple.
|
|
13
|
+
const supported = context => compareVersions(context.clientVersion, MINIMUM) >= 0;
|
|
14
14
|
const hasPermissions = entries => entries.some(entry =>
|
|
15
15
|
["permissions", "default_permissions", "profile", "profiles"].includes(entry.keys[0]));
|
|
16
16
|
const configured = (source, context) => {
|
|
@@ -69,7 +69,7 @@ export function outgoingStatus(source, context) {
|
|
|
69
69
|
? "outgoing live delivery: local socket permissions configured for new sessions; active session overrides remain unverified"
|
|
70
70
|
: `outgoing live delivery: sender permissions unverified in ${context.file}; `
|
|
71
71
|
+ (reasonCode === "permission_configuration_uncaptured"
|
|
72
|
-
? `automatic setup requires Codex ${MINIMUM} or newer
|
|
72
|
+
? `automatic setup requires Codex ${MINIMUM} or newer`
|
|
73
73
|
: custom
|
|
74
74
|
? "custom permission policy was preserved; manually allow ACC state and local sockets "
|
|
75
75
|
+ "through the network proxy in the effective workspace profile"
|