agents-can-communicate 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -4
- package/docs/ARCHITECTURE.md +7 -1
- package/docs/CLI.md +23 -3
- package/docs/GETTING_STARTED.md +11 -3
- package/docs/RELEASING.md +1 -1
- package/docs/TROUBLESHOOTING.md +43 -5
- package/docs/UPGRADING.md +31 -19
- 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 +3 -3
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/install.mjs +10 -0
- package/node_modules/@agents-can-communicate/adapter-codex/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/src/app-server-client.mjs +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/src/native-delivery.mjs +1 -1
- 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/index.mjs +2 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-attempt-writer.mjs +64 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-attempt.mjs +65 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-vocabulary.mjs +1 -0
- package/node_modules/@agents-can-communicate/cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/cli/src/doctor-command.mjs +17 -52
- package/node_modules/@agents-can-communicate/cli/src/install-command.mjs +20 -45
- package/node_modules/@agents-can-communicate/cli/src/native-delivery-status.mjs +88 -0
- package/node_modules/@agents-can-communicate/cli/src/native-session-diagnostics.mjs +64 -0
- package/node_modules/@agents-can-communicate/core/package.json +1 -1
- package/node_modules/@agents-can-communicate/delivery-router/package.json +1 -1
- package/node_modules/@agents-can-communicate/hook-runner/package.json +1 -1
- package/node_modules/@agents-can-communicate/hook-runner/src/native-attempt.mjs +43 -0
- package/node_modules/@agents-can-communicate/hook-runner/src/runner.mjs +17 -27
- package/node_modules/@agents-can-communicate/installer/package.json +1 -1
- package/node_modules/@agents-can-communicate/installer/src/delivery-diagnostics.mjs +36 -0
- package/node_modules/@agents-can-communicate/installer/src/detect.mjs +23 -10
- package/node_modules/@agents-can-communicate/installer/src/index.mjs +3 -1
- package/node_modules/@agents-can-communicate/installer/src/live-policy.mjs +10 -3
- package/node_modules/@agents-can-communicate/installer/src/plan.mjs +6 -1
- 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
package/README.md
CHANGED
|
@@ -73,8 +73,8 @@ Run `acc doctor` from the project if a peer is missing. A directory containing A
|
|
|
73
73
|
state, commonly your home directory, cannot be used as a workspace; start the client in a
|
|
74
74
|
project directory. See [Troubleshooting](docs/TROUBLESHOOTING.md).
|
|
75
75
|
|
|
76
|
-
Already using ACC? Follow the [upgrade guide](docs/UPGRADING.md), including the 0.4.0 →
|
|
77
|
-
0.4.
|
|
76
|
+
Already using ACC? Follow the [upgrade guide](docs/UPGRADING.md), including the 0.4.0/0.4.1 →
|
|
77
|
+
0.4.2 update and the data-format boundary when moving from 0.3.1.
|
|
78
78
|
|
|
79
79
|
## When messages arrive
|
|
80
80
|
|
|
@@ -95,9 +95,16 @@ mutations. It adds no automatic peer-message injection or idle delivery. A reloc
|
|
|
95
95
|
**Optional live delivery can start a turn in an idle Codex or Claude Code session.** It is
|
|
96
96
|
experimental, off by default, and can spend model tokens. On Apple Silicon Macs, Codex
|
|
97
97
|
0.152.1 or newer requires an already-running LocalDaemon and a verified session;
|
|
98
|
-
Claude Code 2.1.258 or newer requires zsh and
|
|
99
|
-
startup
|
|
98
|
+
Claude Code 2.1.258 or newer requires zsh and client-side Channels activation; check its
|
|
99
|
+
startup notice for ACC and accept the development warning when shown. An MCP connection
|
|
100
|
+
alone does not verify inbound delivery. Messages arriving mid-turn wait for the turn to
|
|
101
|
+
finish. The receiving session's
|
|
100
102
|
opt-in policy and current reachability determine whether delivery can proceed.
|
|
103
|
+
`acc install` reports each client's delivery state and can save Codex consent before its
|
|
104
|
+
service is available. `acc doctor` also names each session’s last native binding result,
|
|
105
|
+
including missing launch consent, an unidentified client process or a failed handshake.
|
|
106
|
+
It distinguishes a disabled policy from an unavailable service or a missing live channel
|
|
107
|
+
in the current project.
|
|
101
108
|
|
|
102
109
|
A Codex thread retained by LocalDaemon can receive opted-in messages after its terminal
|
|
103
110
|
exits. Turning ACC delivery off prevents new native offers; already accepted queue entries
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -68,7 +68,13 @@ livePolicy · opaqueEndpointRef · leaseUntil
|
|
|
68
68
|
|
|
69
69
|
Core validates identity and expiry but never interprets the opaque endpoint. That remains
|
|
70
70
|
inside the owning adapter. `acc status` reports available modes, policy, reachability, and
|
|
71
|
-
lease without exposing the endpoint.
|
|
71
|
+
lease without exposing the endpoint. The hook runner also replaces one optional native
|
|
72
|
+
attempt diagnostic beside its session-owner file: closed policy/process/handshake
|
|
73
|
+
metadata with a timestamp, never vendor output. Doctor joins it only to the current open
|
|
74
|
+
generation and reports it separately from transport and receipt facts. It is not journalled
|
|
75
|
+
or projected into agent context; session restart replaces it and SessionEnd attempts bounded
|
|
76
|
+
cleanup. Doctor ignores closed or superseded generations even if cleanup could not finish. Diagnostic disk I/O runs in an
|
|
77
|
+
unreferenced worker, so a stalled write cannot hold the hook process open.
|
|
72
78
|
|
|
73
79
|
## Certified capability versus current reachability
|
|
74
80
|
|
package/docs/CLI.md
CHANGED
|
@@ -239,8 +239,9 @@ external AI client. An addressed handoff requires acknowledgement; a room handof
|
|
|
239
239
|
`--delivery off|actionable|all` is a per-client recipient policy request, not a capability
|
|
240
240
|
switch, and the default is `off`. `--adapter` is repeatable to name several clients. An
|
|
241
241
|
explicit `--delivery` applies uniformly and never prompts; omitting it on an interactive
|
|
242
|
-
terminal asks one default-No question per
|
|
243
|
-
|
|
242
|
+
terminal asks one default-No question per supported client. Codex can save consent while
|
|
243
|
+
its local service is unavailable or has no loaded session; this does not activate delivery.
|
|
244
|
+
A non-interactive run or a `--dry-run` keeps fresh clients off. A recorded opt-in is kept on upgrade. If the detected
|
|
244
245
|
client cannot receive native delivery - unsupported, below the captured minimum, a
|
|
245
246
|
prerelease, known-bad, a wrong platform, or an unsupported shell - installation keeps the
|
|
246
247
|
effective policy off and prints the reason. Claude Code shell activation writes an owned
|
|
@@ -248,7 +249,26 @@ zsh PATH block and a shim that `exec`s the real client; `ACC_BYPASS=1` bypasses
|
|
|
248
249
|
activation. Codex LocalDaemon delivery uses recorded installation consent without changing
|
|
249
250
|
ordinary launch arguments. Its opt-in remains active when shim variables are absent or
|
|
250
251
|
bypassed; `acc install --adapter codex --delivery off` disables new native offers. ACC never
|
|
251
|
-
starts or stops the vendor daemon.
|
|
252
|
+
starts or stops the vendor daemon. The install summary names each client's requested policy,
|
|
253
|
+
activation state and verified fallback. `doctor` separates protocol readiness, recorded
|
|
254
|
+
consent and a live channel in the current workspace, with a next step for missing activation.
|
|
255
|
+
A supported version or an installed plugin alone is not an active delivery channel.
|
|
256
|
+
`runtime: active` means ACC has a reachable local transport binding. Claude can still
|
|
257
|
+
block inbound Channels messages while its MCP server and tools remain connected;
|
|
258
|
+
doctor therefore also names the client-side startup check.
|
|
259
|
+
In doctor JSON, `nativeDelivery.activation` distinguishes missing launch setup from a
|
|
260
|
+
recorded setup (or `not_required` for a pre-existing service). `policy` is the installed
|
|
261
|
+
choice; `sessionPolicy` describes a native binding when one is visible. Existing Claude
|
|
262
|
+
sessions can retain their launch policy after a different choice is installed for new
|
|
263
|
+
sessions; Codex checks current recorded consent before new offers.
|
|
264
|
+
`nativeDelivery.sessions` lists each current session's identity, present transport state
|
|
265
|
+
and `lastAttempt`: timestamp, startup/turn event, effective policy and its source, whether
|
|
266
|
+
that policy was missing/invalid/off, whether a client process was identified, and the closed
|
|
267
|
+
handshake result/reason. `lastAttempt: null` means no usable attempt was observed for this
|
|
268
|
+
generation; it does not prove hooks are disabled. A past successful handshake is separate
|
|
269
|
+
from current transport reachability. ACC replaces this small diagnostic beside the
|
|
270
|
+
hook-owner file outside the repository; it never adds attempt history to agent context.
|
|
271
|
+
See [delivery consent](CONFIGURATION.md#keep-delivery-consent-user-owned).
|
|
252
272
|
|
|
253
273
|
An initial `acc install` enables automatic updates. Reinstalling preserves an explicit
|
|
254
274
|
`acc update --auto off` choice. A full uninstall pauses updates; reinstalling restores
|
package/docs/GETTING_STARTED.md
CHANGED
|
@@ -15,7 +15,7 @@ pair or session-bound ACC MCP tools. See [CLI ownership](CLI.md#coordinate-from-
|
|
|
15
15
|
|
|
16
16
|
ACC requires macOS or Linux and Node.js 24 or newer.
|
|
17
17
|
|
|
18
|
-
Already using ACC? Follow the [upgrade guide](UPGRADING.md) for the 0.4.0 → 0.4.
|
|
18
|
+
Already using ACC? Follow the [upgrade guide](UPGRADING.md) for the 0.4.0/0.4.1 → 0.4.2
|
|
19
19
|
update or the data-format boundary when upgrading from 0.3.1.
|
|
20
20
|
|
|
21
21
|
```bash
|
|
@@ -44,8 +44,16 @@ state directory is in `sandbox_workspace_write.writable_roots` in the config it
|
|
|
44
44
|
ACC keeps those user settings unchanged. Installed files alone do not establish that hooks
|
|
45
45
|
are active; `acc doctor` leaves current readiness unverified and directs you to Codex.
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
Read the delivery summary for each client. Live delivery needs an explicit opt-in and a
|
|
48
|
+
verified channel in the current session. Codex can save your choice even when its local
|
|
49
|
+
service is not running yet. Declining uses the reported fallback: `acc inbox`, or next-turn
|
|
50
|
+
hooks only where the exact client version and platform are certified.
|
|
51
|
+
|
|
52
|
+
If you opt into Claude Code's experimental idle delivery, check Claude's Channels startup
|
|
53
|
+
notice for ACC and accept its development-channel warning when shown. If it reports Channels
|
|
54
|
+
unavailable or blocked, a connected MCP server does not make inbound delivery work;
|
|
55
|
+
see [troubleshooting](TROUBLESHOOTING.md#i-enabled-live-delivery-but-got-fallback).
|
|
56
|
+
The feature is off by default, can spend
|
|
49
57
|
model tokens, and currently requires Apple Silicon macOS, zsh, and Claude Code 2.1.258 or
|
|
50
58
|
newer. You do not need it for durable messages or supported next-turn delivery.
|
|
51
59
|
|
package/docs/RELEASING.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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 package is currently `0.4.
|
|
4
|
+
its evidence tied to the commit that supplied its bytes. The package is currently `0.4.2`;
|
|
5
5
|
the commands derive the version from `package.json` so the filename cannot drift.
|
|
6
6
|
|
|
7
7
|
```mermaid
|
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -94,13 +94,51 @@ For Codex, use your ordinary launch command with 0.152.1 or newer on Apple Silic
|
|
|
94
94
|
macOS. Its LocalDaemon must already be running, and trusted hooks must establish
|
|
95
95
|
the receiver's exact thread and workspace. Embedded sessions, an absent socket,
|
|
96
96
|
ambiguous recipients or failed identity checks retain durable inbox access.
|
|
97
|
-
`acc doctor` reports
|
|
98
|
-
|
|
97
|
+
`acc doctor` reports readiness, consent and the current workspace's live channel separately.
|
|
98
|
+
`native_endpoint_unavailable` means the local service endpoint is missing or is not a safe socket;
|
|
99
|
+
`native_session_unavailable` means the service answered but has no loaded thread to probe.
|
|
100
|
+
An interactive install can save consent in either case. ACC preserves it through a temporary
|
|
101
|
+
outage and does not start the daemon for you.
|
|
102
|
+
|
|
103
|
+
Check `acc doctor --json` and `acc status --json` while both clients are open. If policy is
|
|
104
|
+
`off`, opt in with `acc install --adapter codex --delivery actionable` (or select another
|
|
105
|
+
adapter). This can spend model tokens. If policy is enabled but runtime is `waiting` and
|
|
106
|
+
`deliveryBindings` is empty, no live channel is bound in this workspace. Start a new client
|
|
107
|
+
session, check its integration prompts and re-run doctor. A healthy store, online peers,
|
|
108
|
+
or a connected MCP server does not by itself establish automatic delivery. `lifecycle:
|
|
109
|
+
manual` can also mean the current version has no certified session-end hook; it does not
|
|
110
|
+
prove that no hooks ran.
|
|
99
111
|
|
|
100
112
|
For Claude Code, open a fresh interactive zsh after installation so its launcher
|
|
101
|
-
is on PATH
|
|
102
|
-
|
|
103
|
-
|
|
113
|
+
is on PATH. Check Claude's **Channels startup notice for ACC**, and accept the
|
|
114
|
+
development-channel warning when it appears. A successful bootstrap cache means the
|
|
115
|
+
executable passed ACC's compatibility probe. `/mcp` showing `connected` and two tools
|
|
116
|
+
means the MCP server connected. Even `runtime: active` in ACC's doctor JSON describes
|
|
117
|
+
the local transport; none of these proves that Claude enabled inbound channel messages.
|
|
118
|
+
|
|
119
|
+
If Claude says `--dangerously-load-development-channels ignored` or `Channels are not
|
|
120
|
+
currently available`, it has not enabled that delivery path. On a fresh 2.1.266 test
|
|
121
|
+
profile, the first launch showed those messages and the next launch showed the development
|
|
122
|
+
warning; restarting once can recheck availability, but is not a guaranteed fix. If it
|
|
123
|
+
remains unavailable, follow Claude's notice. Organization policy can also block Channels;
|
|
124
|
+
an administrator must enable them where required. ACC does not override that policy.
|
|
125
|
+
See [Claude's Channels documentation](https://code.claude.com/docs/en/channels).
|
|
126
|
+
|
|
127
|
+
If `deliveryBindings` is empty, ACC has not bound a local transport either. Read the
|
|
128
|
+
per-session lines in `acc doctor`, or `nativeDelivery.sessions[].lastAttempt` in JSON:
|
|
129
|
+
|
|
130
|
+
- An absent launch policy means that hook did not receive delivery consent, even if an
|
|
131
|
+
earlier bootstrap probe succeeded. For Claude, open a new terminal and client through
|
|
132
|
+
the installed launcher.
|
|
133
|
+
- `client_process_unknown` means ACC could not identify the client process. A new client
|
|
134
|
+
session repeats that lookup.
|
|
135
|
+
- `handshake_failed` or `handshake_timeout` means the session could not bind the local
|
|
136
|
+
channel. Check the client's integration/channel setup; the next ordinary turn retries.
|
|
137
|
+
- No attempt observed can mean no native hook ran, an older runtime wrote the owner, or
|
|
138
|
+
diagnostic persistence failed. Check hook activation and the runtime versions in doctor.
|
|
139
|
+
|
|
140
|
+
The timestamp describes the last attempt for that exact generation. A past successful
|
|
141
|
+
handshake does not establish a currently reachable channel or client-side admission.
|
|
104
142
|
|
|
105
143
|
Closing a Codex terminal can leave its daemon thread loaded and eligible. Use
|
|
106
144
|
`acc install --adapter codex --delivery off` to stop new ACC native offers.
|
package/docs/UPGRADING.md
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
# Upgrading to 0.4.
|
|
1
|
+
# Upgrading to 0.4.2
|
|
2
2
|
|
|
3
|
-
## From 0.4.0
|
|
3
|
+
## From 0.4.1 or 0.4.0
|
|
4
4
|
|
|
5
|
-
This patch keeps the existing workspace data format. It
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
This patch keeps the existing workspace data format. It makes installation and delivery
|
|
6
|
+
diagnostics clearer, records the last native binding attempt per session, and restores
|
|
7
|
+
hook participation after detaching a solo session without restarting the conversation.
|
|
8
|
+
It also includes the 0.4.1 Grok, reinstall-preference and workspace-warning fixes.
|
|
8
9
|
|
|
9
|
-
Close
|
|
10
|
+
Close participating clients and persistent ACC MCP processes, then run:
|
|
10
11
|
|
|
11
12
|
```bash
|
|
12
13
|
acc update
|
|
@@ -14,17 +15,28 @@ acc version
|
|
|
14
15
|
acc doctor
|
|
15
16
|
```
|
|
16
17
|
|
|
17
|
-
After publication, `acc version` should report 0.4.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
After publication, `acc version` should report 0.4.2. If a pin keeps an older version,
|
|
19
|
+
select 0.4.2 or clear it first. A managed update refreshes installed integrations and
|
|
20
|
+
skills; use the npm instructions below for unmanaged or pre-0.4 installations. Start
|
|
21
|
+
clients again and complete any hook/trust review they request.
|
|
22
|
+
|
|
23
|
+
Run `acc doctor` in the project after the new clients have started (or submitted a normal
|
|
24
|
+
turn). Its per-session lines now explain missing launch consent, an unidentified client
|
|
25
|
+
process and a failed or timed-out handshake. `nativeDelivery.sessions[].lastAttempt` in
|
|
26
|
+
`acc doctor --json` provides the same closed metadata and timestamp. Existing owners may
|
|
27
|
+
have no attempt record until a new hook runs; missing metadata does not prove hooks are
|
|
28
|
+
disabled. A previous successful handshake and a connected MCP server do not prove that
|
|
29
|
+
Claude admitted inbound Channels messages. See [delivery troubleshooting](TROUBLESHOOTING.md#i-enabled-live-delivery-but-got-fallback).
|
|
30
|
+
|
|
31
|
+
Upgrading preserves delivery consent. If doctor reports native delivery off and you want
|
|
32
|
+
automatic peer requests, use `acc install --adapter codex --delivery actionable` (or the
|
|
33
|
+
relevant adapter); this can spend model tokens. Codex can save that consent before its
|
|
34
|
+
local service/session becomes available. ACC does not start the vendor daemon, and it
|
|
35
|
+
keeps durable inbox access when no verified live channel is bound.
|
|
36
|
+
|
|
37
|
+
In Grok, public status supplies the session's own CLI arguments through the next tool
|
|
38
|
+
hook. Grok still uses explicit inbox reads; this patch adds no external wake or guards.
|
|
39
|
+
Start clients in a project directory, rather than a home directory containing ACC state.
|
|
28
40
|
|
|
29
41
|
## From 0.3.1
|
|
30
42
|
|
|
@@ -47,7 +59,7 @@ restores access; deleting or editing the record is not a remedy.
|
|
|
47
59
|
2. Install the released package, then refresh the client integrations:
|
|
48
60
|
|
|
49
61
|
```bash
|
|
50
|
-
npm install --global agents-can-communicate@0.4.
|
|
62
|
+
npm install --global agents-can-communicate@0.4.2
|
|
51
63
|
acc version
|
|
52
64
|
acc install
|
|
53
65
|
```
|
|
@@ -97,7 +109,7 @@ acc update # download and apply, or report what is keeping it
|
|
|
97
109
|
acc update --check # check without installing or changing update settings
|
|
98
110
|
acc update --auto off # disable background updates
|
|
99
111
|
acc update --auto on # enable them again
|
|
100
|
-
acc update --pin 0.4.
|
|
112
|
+
acc update --pin 0.4.2 # stay on this exact stable version
|
|
101
113
|
acc update --pin none # follow stable releases again
|
|
102
114
|
```
|
|
103
115
|
|
|
@@ -11,9 +11,9 @@ import { bindNativeSession, offerMessage, planNativeActivation, probeNativeDeliv
|
|
|
11
11
|
export const CLAUDE_CODE_VERSION = "2.1.233";
|
|
12
12
|
export const CLAUDE_CHANNEL_MINIMUM = "2.1.258";
|
|
13
13
|
export const CLAUDE_DELIVERY_FALLBACK = Object.freeze({
|
|
14
|
-
diagnostic: "Claude Code native delivery
|
|
15
|
-
+ "
|
|
16
|
-
+ "
|
|
14
|
+
diagnostic: "Claude Code native delivery requires client-side Channels activation; "
|
|
15
|
+
+ "a successful bootstrap or connected ACC MCP server does not prove inbound delivery "
|
|
16
|
+
+ "is enabled. Messages remain durable for certified next-turn delivery or acc inbox",
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { cp, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
+
// Kept together above 300 lines: source and cache publication, vendor registry
|
|
5
|
+
// merges and their exact uninstall share one ownership transaction and helpers.
|
|
6
|
+
|
|
4
7
|
import { AccError, EXIT } from "@agents-can-communicate/protocol";
|
|
5
8
|
import { fileURLToPath } from "node:url";
|
|
6
9
|
|
|
@@ -19,6 +22,9 @@ const manifest = fileURLToPath(new URL("../plugin/.claude-plugin/plugin.json",
|
|
|
19
22
|
const PLUGIN_NAME = "agents-can-communicate";
|
|
20
23
|
const MARKETPLACE = "acc-local";
|
|
21
24
|
const QUALIFIED = `${PLUGIN_NAME}@${MARKETPLACE}`;
|
|
25
|
+
const CHANNEL_ACTIVATION_CHECK = "Check Claude's Channels startup notice for ACC; "
|
|
26
|
+
+ "an MCP connection does not verify inbound delivery. If Channels are unavailable "
|
|
27
|
+
+ "or blocked, resolve that client warning before expecting live messages.";
|
|
22
28
|
|
|
23
29
|
/**
|
|
24
30
|
* How this client actually installs a plugin.
|
|
@@ -244,6 +250,7 @@ export async function installClaudePlugin({ configDir, runner, cli, preserveVers
|
|
|
244
250
|
changes: [source, cached, marketplaceFile(configDir),
|
|
245
251
|
knownMarketplacesPath(configDir), installedPluginsPath(configDir), file,
|
|
246
252
|
...(live ? [mcpPath(source), mcpPath(cached)] : [])],
|
|
253
|
+
needsAction: live ? [CHANNEL_ACTIVATION_CHECK] : [],
|
|
247
254
|
diagnostics: live
|
|
248
255
|
? ["native channel wired; Claude's experimental development-channel warning still applies"]
|
|
249
256
|
: [] };
|
|
@@ -290,7 +297,10 @@ export async function detectClaude({ configDir }) {
|
|
|
290
297
|
const registered = Object.hasOwn(
|
|
291
298
|
(await readJson(installedPluginsPath(configDir), { plugins: {} })).plugins ?? {},
|
|
292
299
|
QUALIFIED);
|
|
300
|
+
const channelConfigured = enabled && registered
|
|
301
|
+
&& (await readJson(mcpPath(sourceDir(configDir)), null))?.mcpServers?.["acc-channel"] != null;
|
|
293
302
|
return { ok: true, changes: [],
|
|
303
|
+
needsAction: channelConfigured ? [CHANNEL_ACTIVATION_CHECK] : [],
|
|
294
304
|
diagnostics: [enabled && registered
|
|
295
305
|
? "acc plugin registered and enabled"
|
|
296
306
|
: "acc plugin not registered"] };
|
|
@@ -65,7 +65,7 @@ export async function probeCodexQueue(peer, { threadId, minimum = MINIMUM_VERSIO
|
|
|
65
65
|
const loaded = await pageAll(peer, "thread/loaded/list", {});
|
|
66
66
|
threadId = loaded.find(id => typeof id === "string" && id !== "");
|
|
67
67
|
if (threadId === undefined) return { supported: false, serverVersion,
|
|
68
|
-
reasonCode: "
|
|
68
|
+
reasonCode: "native_session_unavailable" };
|
|
69
69
|
}
|
|
70
70
|
queueEntries(await peer.request("thread/queue/list", { threadId }));
|
|
71
71
|
} catch (error) {
|
|
@@ -35,7 +35,7 @@ export async function probeNativeDelivery({ timeoutMs = 750, env = process.env,
|
|
|
35
35
|
const unsupported = (reasonCode, clientVersion = null) => ({ supported: false, clientVersion,
|
|
36
36
|
protocolContract: PROTOCOL_CONTRACT, executableFingerprint: null, modes: [], reasonCode });
|
|
37
37
|
const socketPath = controlSocketPath(env);
|
|
38
|
-
if (!await socketIsReady(socketPath)) return unsupported("
|
|
38
|
+
if (!await socketIsReady(socketPath)) return unsupported("native_endpoint_unavailable");
|
|
39
39
|
try {
|
|
40
40
|
return await usingPeer(socketPath, timeoutMs, open, async peer => {
|
|
41
41
|
const probe = await probeCodexQueue(peer);
|
|
@@ -23,3 +23,5 @@ export { formatJsonAs, jsonStyleOf, mergeOwnedConfig, mergeOwnedEntries, ownedEn
|
|
|
23
23
|
export { clearSessionBinding, listSessionBindings, loadSessionBinding,
|
|
24
24
|
storeSessionBinding }
|
|
25
25
|
from "./session-binding.mjs";
|
|
26
|
+
|
|
27
|
+
export { clearNativeAttempt, loadNativeAttempt, storeNativeAttempt } from "./native-attempt.mjs";
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdir, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
5
|
+
|
|
6
|
+
import { assertPortableId } from "@agents-can-communicate/protocol";
|
|
7
|
+
import { withWriterMutex } from "@agents-can-communicate/storage-filesystem";
|
|
8
|
+
import { loadNativeAttempt, nativeAttemptFrom } from "./native-attempt.mjs";
|
|
9
|
+
|
|
10
|
+
const keyFor = harnessSessionId => createHash("sha256").update(String(harnessSessionId))
|
|
11
|
+
.digest("hex").slice(0, 32);
|
|
12
|
+
const fileFor = (runtimeDir, harnessSessionId) =>
|
|
13
|
+
path.join(runtimeDir, "native-attempts", `${keyFor(harnessSessionId)}.json`);
|
|
14
|
+
const checkDeadline = deadline => {
|
|
15
|
+
if (Date.now() >= deadline) throw new Error("native diagnostic deadline expired");
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// The caller stops waiting after 250 ms at most. An in-flight filesystem call
|
|
19
|
+
// may complete later, so retain this SEPARATE mutex until its continuation is
|
|
20
|
+
// finished. Unique staging names also isolate cleanup after stale-lock recovery.
|
|
21
|
+
// No diagnostic operation can touch the authoritative hook-owner file.
|
|
22
|
+
async function boundedWrite(input, operation) {
|
|
23
|
+
const deadline = input.deadlineAt;
|
|
24
|
+
try {
|
|
25
|
+
checkDeadline(deadline);
|
|
26
|
+
await withWriterMutex({ locks: path.join(input.runtimeDir, "native-attempt-locks",
|
|
27
|
+
keyFor(input.harnessSessionId)) }, { root: input.runtimeDir,
|
|
28
|
+
clock: { now: () => new Date().toISOString() }, deadlineAt: deadline },
|
|
29
|
+
() => operation(deadline));
|
|
30
|
+
return true;
|
|
31
|
+
} catch { return false; }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function storeNativeAttempt(input) {
|
|
35
|
+
return boundedWrite(input, async deadline => {
|
|
36
|
+
const { runtimeDir, harnessSessionId, accSessionId, generation } = input;
|
|
37
|
+
assertPortableId(accSessionId, "native diagnostic session id");
|
|
38
|
+
assertPortableId(generation, "native diagnostic generation");
|
|
39
|
+
const attempt = nativeAttemptFrom(input.nativeAttempt);
|
|
40
|
+
if (attempt === null) return;
|
|
41
|
+
const file = fileFor(runtimeDir, harnessSessionId);
|
|
42
|
+
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
43
|
+
checkDeadline(deadline);
|
|
44
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
45
|
+
try {
|
|
46
|
+
checkDeadline(deadline);
|
|
47
|
+
await writeFile(temporary, JSON.stringify({ schemaVersion: 1, accSessionId, generation,
|
|
48
|
+
attempt }) + "\n", "utf8");
|
|
49
|
+
checkDeadline(deadline);
|
|
50
|
+
await rename(temporary, file);
|
|
51
|
+
} finally { await rm(temporary, { force: true }).catch(() => {}); }
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function clearNativeAttempt(input) {
|
|
56
|
+
return boundedWrite(input, async deadline => {
|
|
57
|
+
if (await loadNativeAttempt(input) === null) return;
|
|
58
|
+
checkDeadline(deadline);
|
|
59
|
+
await rm(fileFor(input.runtimeDir, input.harnessSessionId), { force: true });
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const operation = workerData.operation === "clear" ? clearNativeAttempt : storeNativeAttempt;
|
|
64
|
+
parentPort.postMessage(await operation(workerData.input));
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { Worker } from "node:worker_threads";
|
|
5
|
+
|
|
6
|
+
import { NATIVE_REASON_CODES, TIMESTAMP } from "./native-vocabulary.mjs";
|
|
7
|
+
|
|
8
|
+
// Optional diagnostic metadata beside the hook-owner file, never an event
|
|
9
|
+
// history or injected context. Copy only closed values: vendor errors, prompts,
|
|
10
|
+
// endpoints and extra properties must not survive persistence or doctor reads.
|
|
11
|
+
export function nativeAttemptFrom(value) {
|
|
12
|
+
if (!value || typeof value.at !== "string" || !TIMESTAMP.test(value.at)
|
|
13
|
+
|| !Number.isFinite(Date.parse(value.at))
|
|
14
|
+
|| !["sessionStart", "beforeTurn"].includes(value.event)
|
|
15
|
+
|| !["active", "off", "degraded", "unsupported"].includes(value.state)
|
|
16
|
+
|| !(value.reasonCode === null || NATIVE_REASON_CODES.includes(value.reasonCode))
|
|
17
|
+
|| !["off", "actionable", "all"].includes(value.policy)
|
|
18
|
+
|| !["bootstrap-environment", "installation-record"].includes(value.policySource)
|
|
19
|
+
|| !["enabled", "off", "missing", "invalid", "unavailable"].includes(value.policyStatus)
|
|
20
|
+
|| !["identified", "unknown"].includes(value.clientProcess)) return null;
|
|
21
|
+
return { at: value.at, event: value.event, state: value.state, reasonCode: value.reasonCode,
|
|
22
|
+
policy: value.policy, policySource: value.policySource, policyStatus: value.policyStatus,
|
|
23
|
+
clientProcess: value.clientProcess };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const keyFor = harnessSessionId => createHash("sha256").update(String(harnessSessionId))
|
|
27
|
+
.digest("hex").slice(0, 32);
|
|
28
|
+
const fileFor = (runtimeDir, harnessSessionId) =>
|
|
29
|
+
path.join(runtimeDir, "native-attempts", `${keyFor(harnessSessionId)}.json`);
|
|
30
|
+
export async function loadNativeAttempt({ runtimeDir, harnessSessionId, accSessionId, generation }) {
|
|
31
|
+
try {
|
|
32
|
+
const record = JSON.parse(await readFile(fileFor(runtimeDir, harnessSessionId), "utf8"));
|
|
33
|
+
return record.schemaVersion === 1 && record.accSessionId === accSessionId
|
|
34
|
+
&& record.generation === generation ? nativeAttemptFrom(record.attempt) : null;
|
|
35
|
+
} catch { return null; }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Isolate filesystem requests as well as JS waiting: even a stuck fs request
|
|
39
|
+
// must not keep the vendor's short-lived hook process alive after its output.
|
|
40
|
+
// The unreferenced worker owns its mutex through any late I/O continuation.
|
|
41
|
+
async function writeDiagnostic(operation, input) {
|
|
42
|
+
const deadlineAt = Math.min(input.deadlineAt ?? Infinity, Date.now() + 250);
|
|
43
|
+
if (Date.now() >= deadlineAt) return false;
|
|
44
|
+
let worker;
|
|
45
|
+
let timer;
|
|
46
|
+
try {
|
|
47
|
+
worker = new Worker(new URL("./native-attempt-writer.mjs", import.meta.url), {
|
|
48
|
+
workerData: { operation, input: { runtimeDir: input.runtimeDir,
|
|
49
|
+
harnessSessionId: input.harnessSessionId, accSessionId: input.accSessionId,
|
|
50
|
+
generation: input.generation, nativeAttempt: nativeAttemptFrom(input.nativeAttempt), deadlineAt } },
|
|
51
|
+
});
|
|
52
|
+
const result = new Promise(resolve => {
|
|
53
|
+
worker.once("message", value => resolve(value === true));
|
|
54
|
+
worker.once("error", () => resolve(false));
|
|
55
|
+
worker.once("exit", () => resolve(false));
|
|
56
|
+
timer = setTimeout(() => resolve(false), Math.max(0, deadlineAt - Date.now()));
|
|
57
|
+
});
|
|
58
|
+
worker.unref();
|
|
59
|
+
return await result;
|
|
60
|
+
} catch { return false; }
|
|
61
|
+
finally { clearTimeout(timer); }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const storeNativeAttempt = input => writeDiagnostic("store", input);
|
|
65
|
+
export const clearNativeAttempt = input => writeDiagnostic("clear", input);
|
|
@@ -23,6 +23,7 @@ export const NATIVE_REASON_CODES = Object.freeze([
|
|
|
23
23
|
"native_delivery_unsupported", "platform_not_captured", "version_unavailable",
|
|
24
24
|
"prerelease_not_captured", "below_minimum_version", "known_bad_version",
|
|
25
25
|
"feature_probe_failed", "probe_timeout", "probe_version_mismatch", "protocol_mismatch",
|
|
26
|
+
"native_endpoint_unavailable", "native_session_unavailable",
|
|
26
27
|
"handshake_failed", "handshake_timeout", "handshake_version_mismatch",
|
|
27
28
|
"session_generation_stale", "client_process_unknown", "unsupported_shell",
|
|
28
29
|
// The current session's canonical workspace identity could not be verified.
|