agents-can-communicate 0.7.1 → 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/README.md +3 -1
- package/bin/entrypoints/acc-bootstrap.mjs +6 -48
- package/bin/entrypoints/acc-claude-channel.mjs +47 -157
- package/bin/entrypoints/acc-mcp.mjs +0 -1
- package/bin/entrypoints/acc.mjs +0 -1
- package/docs/ADAPTER_AUTHORING.md +74 -24
- package/docs/ARCHITECTURE.md +31 -18
- package/docs/CAPABILITIES.md +38 -25
- package/docs/CLI.md +51 -13
- package/docs/CONCEPTS.md +5 -4
- package/docs/CONFIGURATION.md +18 -11
- package/docs/GETTING_STARTED.md +15 -12
- package/docs/GLOSSARY.md +2 -1
- package/docs/HOW_IT_WORKS.md +8 -4
- package/docs/PROTOCOL.md +25 -6
- package/docs/RELEASING.md +8 -5
- package/docs/SECURITY_MODEL.md +14 -1
- package/docs/TROUBLESHOOTING.md +66 -29
- package/docs/UPGRADING.md +49 -8
- package/docs/WHY_ACC.md +5 -3
- 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/certification.json +14 -73
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/certification-provenance.json +29 -71
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/delivery/claude-code-2.1.282-product-evidence.json +161 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/delivery/claude-code-2.1.282.json +28 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/inbox-wake-2.1.282.json +44 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +5 -2
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/adapter.mjs +26 -30
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/hooks.mjs +6 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/inbox-contract.mjs +10 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/inbox-delivery.mjs +249 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/inbox-endpoint.mjs +166 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/inbox-registry.mjs +71 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/inbox-settings.mjs +109 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/install.mjs +25 -31
- 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/events.mjs +16 -3
- package/node_modules/@agents-can-communicate/adapter-sdk/src/hook-shim.mjs +4 -3
- package/node_modules/@agents-can-communicate/adapter-sdk/src/index.mjs +4 -3
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-activation.mjs +4 -14
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-delivery.mjs +104 -84
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-vocabulary.mjs +8 -4
- package/node_modules/@agents-can-communicate/cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/cli/src/doctor-command.mjs +8 -6
- package/node_modules/@agents-can-communicate/cli/src/install-command.mjs +8 -23
- package/node_modules/@agents-can-communicate/cli/src/install-delivery-consent.mjs +3 -4
- package/node_modules/@agents-can-communicate/cli/src/main.mjs +24 -3
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/entry.mjs +7 -43
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/launchers.mjs +5 -3
- 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/cli/src/managed-runtime/refresh.mjs +3 -4
- package/node_modules/@agents-can-communicate/cli/src/native-delivery-status.mjs +2 -2
- 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/sessions.mjs +25 -2
- package/node_modules/@agents-can-communicate/core/src/status.mjs +31 -1
- 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 +33 -48
- 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-attempt.mjs +6 -8
- package/node_modules/@agents-can-communicate/hook-runner/src/native-binding.mjs +27 -15
- 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 +4 -2
- package/node_modules/@agents-can-communicate/installer/package.json +1 -1
- package/node_modules/@agents-can-communicate/installer/src/apply.mjs +19 -5
- package/node_modules/@agents-can-communicate/installer/src/delivery-diagnostics.mjs +0 -2
- package/node_modules/@agents-can-communicate/installer/src/detect.mjs +6 -11
- package/node_modules/@agents-can-communicate/installer/src/index.mjs +4 -7
- package/node_modules/@agents-can-communicate/installer/src/native-activation.mjs +23 -61
- package/node_modules/@agents-can-communicate/installer/src/ownership.mjs +7 -1
- package/node_modules/@agents-can-communicate/installer/src/plan.mjs +15 -5
- package/node_modules/@agents-can-communicate/installer/src/shell-bootstrap.mjs +10 -133
- 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/node_modules/@agents-can-communicate/storage-filesystem/src/atomic-json.mjs +6 -1
- package/package.json +1 -1
- package/bin/acc-bootstrap.mjs +0 -7
- package/bin/acc-claude-channel.mjs +0 -10
- package/bin/entrypoints/claude-channel-binding.mjs +0 -46
- package/bin/entrypoints/claude-channel-stdio.mjs +0 -29
- package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/.mcp.json +0 -8
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/channel.mjs +0 -381
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/native-delivery.mjs +0 -232
- package/node_modules/@agents-can-communicate/installer/src/bootstrap-runtime.mjs +0 -150
|
@@ -1,73 +1,93 @@
|
|
|
1
|
-
import { CONTRACT_ID, FINGERPRINT, HANDSHAKE_KEYS, KNOWN_BAD_REASON,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { CONTRACT_ID, FINGERPRINT, HANDSHAKE_KEYS, KNOWN_BAD_REASON, NATIVE_BINDING_MODES,
|
|
2
|
+
PLANNABLE_ACTIVATION_KINDS, PROBE_KEYS, TIMESTAMP, assertModes, assertReasonCode, closed,
|
|
3
|
+
compareStableVersions, deepFreeze, isPlainObject, isText, parseStableVersion, usage }
|
|
4
|
+
from "./native-vocabulary.mjs";
|
|
5
|
+
import { compareVersionOrder, versionOrder } from "./certification.mjs";
|
|
5
6
|
|
|
6
|
-
// The native-delivery compatibility contract: a
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
7
|
+
// The native-delivery compatibility contract: a minimum that is a real passing
|
|
8
|
+
// capture, one or more anchors naming the captured protocol, an explicit
|
|
9
|
+
// denylist, and the activation kinds an adapter may ask the installer for.
|
|
10
|
+
// There is deliberately no maximum: a newer stable client is admitted only
|
|
10
11
|
// when a current read-only probe and a per-session handshake confirm the same
|
|
11
|
-
// protocol contract.
|
|
12
|
-
//
|
|
12
|
+
// protocol contract. There is no platform either: a capture records where a
|
|
13
|
+
// behaviour was observed, the minimum it establishes applies everywhere, and
|
|
14
|
+
// the probe and handshake decide on the machine in hand (see
|
|
15
|
+
// docs/design/2026-09-26-native-delivery-across-platforms.md). Certification
|
|
16
|
+
// evidence still governs every other capability; this rule is used for native
|
|
17
|
+
// live delivery alone.
|
|
13
18
|
|
|
14
|
-
export { NATIVE_ACTIVATION_KINDS, NATIVE_BINDING_MODES,
|
|
15
|
-
compareStableVersions, parseStableVersion } from "./native-vocabulary.mjs";
|
|
19
|
+
export { NATIVE_ACTIVATION_KINDS, NATIVE_BINDING_MODES, NATIVE_REASON_CODES,
|
|
20
|
+
PLANNABLE_ACTIVATION_KINDS, compareStableVersions, parseStableVersion } from "./native-vocabulary.mjs";
|
|
16
21
|
export { validateNativeActivationPlan } from "./native-activation.mjs";
|
|
17
22
|
|
|
18
23
|
const orderedModes = modes => NATIVE_BINDING_MODES.filter(mode => modes.includes(mode));
|
|
19
24
|
|
|
20
25
|
export function validateNativeDeliveryContract(value, { certification, client }) {
|
|
26
|
+
// Consent to live delivery is what `acc install` recorded. Up to 0.7.x a
|
|
27
|
+
// Claude shell shim could export it instead ("bootstrap-environment"); the
|
|
28
|
+
// shim is gone, and the installation record is the one source left.
|
|
21
29
|
const policySource = Object.hasOwn(value ?? {}, "policySource")
|
|
22
|
-
? value.policySource : "
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
? value.policySource : "installation-record";
|
|
31
|
+
// What a successful offer put in front of the model. "message" carries the
|
|
32
|
+
// body, so the router records the receipt as offered. "wake" carries only a
|
|
33
|
+
// notice that makes the client run a turn; the body reaches the model through
|
|
34
|
+
// the next-turn hook, which records the offer itself once its stdout carried
|
|
35
|
+
// the body. Recording a wake as offered would hide the body from that hook.
|
|
36
|
+
const offerKind = Object.hasOwn(value ?? {}, "offerKind") ? value.offerKind : "message";
|
|
37
|
+
// The per-platform shape 0.8.0 and earlier declared. Refused by name: an
|
|
38
|
+
// adapter built against that SDK should learn what replaced it, not fail on
|
|
39
|
+
// a generic unknown field.
|
|
40
|
+
if (Object.hasOwn(value ?? {}, "minimumByPlatform")) {
|
|
41
|
+
usage("nativeDelivery.minimumByPlatform is no longer declared; a capture's minimum applies on "
|
|
42
|
+
+ "every platform, so declare nativeDelivery.minimum instead "
|
|
43
|
+
+ "(docs/design/2026-09-26-native-delivery-across-platforms.md)");
|
|
44
|
+
}
|
|
45
|
+
closed({ ...value, policySource, offerKind },
|
|
46
|
+
["minimum", "anchors", "knownBad", "activationKinds", "policySource", "offerKind"],
|
|
25
47
|
"nativeDelivery");
|
|
26
|
-
if (
|
|
27
|
-
usage("nativeDelivery.policySource must be installation-record
|
|
48
|
+
if (policySource !== "installation-record") {
|
|
49
|
+
usage("nativeDelivery.policySource must be installation-record");
|
|
28
50
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
usage("nativeDelivery.minimumByPlatform must map at least one captured platform to a version");
|
|
51
|
+
if (!["message", "wake"].includes(offerKind)) {
|
|
52
|
+
usage("nativeDelivery.offerKind must be message or wake");
|
|
32
53
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
if (parseStableVersion(version) === null) {
|
|
38
|
-
usage(`nativeDelivery.minimumByPlatform ${platform} must be a stable version`);
|
|
39
|
-
}
|
|
54
|
+
const minimum = value.minimum;
|
|
55
|
+
if (parseStableVersion(minimum) === null) {
|
|
56
|
+
usage("nativeDelivery.minimum must be a stable version");
|
|
40
57
|
}
|
|
41
58
|
if (!Array.isArray(value.anchors) || value.anchors.length === 0) {
|
|
42
59
|
usage("nativeDelivery.anchors must name at least one passing capture");
|
|
43
60
|
}
|
|
44
61
|
const anchors = value.anchors.map((anchor, index) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
if (Object.hasOwn(anchor ?? {}, "platform")) {
|
|
63
|
+
usage(`nativeDelivery anchor ${index} names a platform; the platform a capture was taken on `
|
|
64
|
+
+ "is recorded by its evidence, and the anchor names the version alone "
|
|
65
|
+
+ "(docs/design/2026-09-26-native-delivery-across-platforms.md)");
|
|
49
66
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
usage(`nativeDelivery anchor ${index} version must be a stable version
|
|
67
|
+
closed(anchor, ["version", "protocolContract"], "nativeDelivery anchor");
|
|
68
|
+
if (parseStableVersion(anchor.version) === null) {
|
|
69
|
+
usage(`nativeDelivery anchor ${index} version must be a stable version`);
|
|
70
|
+
}
|
|
71
|
+
if (compareStableVersions(anchor.version, minimum) < 0) {
|
|
72
|
+
usage(`nativeDelivery anchor ${index} version ${anchor.version} is below the minimum ${minimum}: `
|
|
73
|
+
+ "the minimum must be the first passing capture");
|
|
53
74
|
}
|
|
54
75
|
if (!isText(anchor.protocolContract) || !CONTRACT_ID.test(anchor.protocolContract)) {
|
|
55
76
|
usage(`nativeDelivery anchor ${index} protocolContract must be a closed identifier`);
|
|
56
77
|
}
|
|
78
|
+
// Where the capture was taken is provenance the evidence row keeps; the
|
|
79
|
+
// anchor is proven by a passing capture of this version on any platform.
|
|
57
80
|
const proven = (certification?.evidence ?? []).some(item => item.result === "pass"
|
|
58
81
|
&& item.capability === "delivery.livePush" && item.client === client
|
|
59
|
-
&& item.version === anchor.version
|
|
82
|
+
&& item.version === anchor.version);
|
|
60
83
|
if (!proven) {
|
|
61
|
-
usage(`nativeDelivery anchor ${anchor.version}
|
|
62
|
-
|
|
84
|
+
usage(`nativeDelivery anchor ${anchor.version} has no passing delivery.livePush certification`,
|
|
85
|
+
{ anchor });
|
|
63
86
|
}
|
|
64
87
|
return { ...anchor };
|
|
65
88
|
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
usage(`nativeDelivery minimum ${minimum} on ${platform} must be the first passing capture: `
|
|
69
|
-
+ "no anchor matches it");
|
|
70
|
-
}
|
|
89
|
+
if (!anchors.some(anchor => compareStableVersions(anchor.version, minimum) === 0)) {
|
|
90
|
+
usage(`nativeDelivery minimum ${minimum} must be the first passing capture: no anchor matches it`);
|
|
71
91
|
}
|
|
72
92
|
if (!Array.isArray(value.knownBad)) usage("nativeDelivery.knownBad must be an array");
|
|
73
93
|
const knownBad = value.knownBad.map(entry => {
|
|
@@ -89,27 +109,32 @@ export function validateNativeDeliveryContract(value, { certification, client })
|
|
|
89
109
|
});
|
|
90
110
|
const kinds = value.activationKinds;
|
|
91
111
|
if (!Array.isArray(kinds) || kinds.length === 0 || new Set(kinds).size !== kinds.length
|
|
92
|
-
|| kinds.some(kind => !
|
|
93
|
-
usage(`nativeDelivery.activationKinds must be unique entries of ${
|
|
112
|
+
|| kinds.some(kind => !PLANNABLE_ACTIVATION_KINDS.includes(kind))) {
|
|
113
|
+
usage(`nativeDelivery.activationKinds must be unique entries of ${PLANNABLE_ACTIVATION_KINDS.join(", ")}`);
|
|
94
114
|
}
|
|
95
|
-
return deepFreeze({
|
|
96
|
-
|
|
115
|
+
return deepFreeze({ minimum, anchors, knownBad, activationKinds: [...kinds], policySource,
|
|
116
|
+
offerKind });
|
|
97
117
|
}
|
|
98
118
|
|
|
99
|
-
|
|
119
|
+
// `order` is the client's release triple; the denylist names stable versions,
|
|
120
|
+
// so a prerelease of a denylisted release is on it too.
|
|
121
|
+
function knownBadHit(contract, order) {
|
|
122
|
+
const hit = (left, right) => compareVersionOrder(left, versionOrder(right));
|
|
100
123
|
return contract.knownBad.some(entry => (Object.hasOwn(entry, "version")
|
|
101
|
-
?
|
|
102
|
-
:
|
|
124
|
+
? hit(order, entry.version) === 0
|
|
125
|
+
: hit(order, entry.from) >= 0 && hit(order, entry.to) <= 0));
|
|
103
126
|
}
|
|
104
127
|
|
|
105
128
|
// Judges one reported client version against an adapter's captured
|
|
106
|
-
// native-delivery contract: is the
|
|
107
|
-
//
|
|
129
|
+
// native-delivery contract: is the version at or above the captured minimum,
|
|
130
|
+
// and is it not on the denylist. Returns
|
|
108
131
|
// { reasonCode, minimumVersion, protocolContract }; reasonCode is null when
|
|
109
132
|
// the version satisfies the contract, otherwise one of
|
|
110
|
-
// "native_delivery_unsupported", "
|
|
111
|
-
//
|
|
112
|
-
//
|
|
133
|
+
// "native_delivery_unsupported", "version_unavailable", "below_minimum_version",
|
|
134
|
+
// or "known_bad_version". The platform the caller runs on plays no part: the
|
|
135
|
+
// minimum was captured somewhere and applies everywhere. A prerelease is
|
|
136
|
+
// judged by its release triple, as hook capabilities are: what admits it is
|
|
137
|
+
// the protocol contract the probe or handshake reports, not the suffix.
|
|
113
138
|
//
|
|
114
139
|
// This is the static half of the rule only: it never contacts the client, so
|
|
115
140
|
// passing here proves nothing about whether a live probe or handshake
|
|
@@ -118,38 +143,33 @@ function knownBadHit(contract, version) {
|
|
|
118
143
|
// validateNativeHandshake below, and the delivery router's own offer check,
|
|
119
144
|
// which uses this function alone because a response already carries no probe
|
|
120
145
|
// or handshake shape to check further).
|
|
121
|
-
export function evaluateVersionContract(adapter, { clientVersion
|
|
146
|
+
export function evaluateVersionContract(adapter, { clientVersion } = {}) {
|
|
122
147
|
const contract = adapter?.nativeDelivery;
|
|
123
|
-
|
|
124
|
-
// contract" as a missing one, and reading `contract.minimumByPlatform` off it
|
|
125
|
-
// threw the very TypeError the guard below exists to prevent. defineAdapter
|
|
126
|
-
// cannot produce that shape; a hand-built registry entry can.
|
|
127
|
-
if (contract == null) {
|
|
128
|
-
return { reasonCode: "native_delivery_unsupported", minimumVersion: null, protocolContract: null };
|
|
129
|
-
}
|
|
130
|
-
const uncaptured = { reasonCode: "platform_not_captured", minimumVersion: null,
|
|
148
|
+
const unsupported = { reasonCode: "native_delivery_unsupported", minimumVersion: null,
|
|
131
149
|
protocolContract: null };
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
150
|
+
// `== null` rather than `=== undefined`: a null declaration is as much "no
|
|
151
|
+
// contract" as a missing one, and reading `contract.minimum` off it threw
|
|
152
|
+
// the very TypeError the guard below exists to prevent. defineAdapter cannot
|
|
153
|
+
// produce that shape; a hand-built registry entry can.
|
|
154
|
+
if (contract == null) return unsupported;
|
|
155
|
+
// validateNativeDeliveryContract guarantees a minimum and a matching anchor,
|
|
156
|
+
// but this function is also handed adapter objects that never went through
|
|
157
|
+
// it. A declaration missing either half has captured nothing, which is a
|
|
158
|
+
// closed answer - not a TypeError raised deep inside a delivery offer, far
|
|
159
|
+
// from the declaration that caused it. Every other entry point below
|
|
160
|
+
// already answers malformed input this way.
|
|
161
|
+
const minimumVersion = isText(contract.minimum) ? contract.minimum : null;
|
|
162
|
+
if (minimumVersion === null) return unsupported;
|
|
141
163
|
const anchor = (Array.isArray(contract.anchors) ? contract.anchors : [])
|
|
142
|
-
.find(item => item
|
|
143
|
-
if (anchor === undefined) return
|
|
164
|
+
.find(item => item?.version === minimumVersion);
|
|
165
|
+
if (anchor === undefined) return unsupported;
|
|
144
166
|
const facts = { minimumVersion, protocolContract: anchor.protocolContract };
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
if (compareStableVersions(clientVersion, minimumVersion) < 0) {
|
|
167
|
+
const order = versionOrder(clientVersion);
|
|
168
|
+
if (order === null) return { ...facts, reasonCode: "version_unavailable" };
|
|
169
|
+
if (compareVersionOrder(order, versionOrder(minimumVersion)) < 0) {
|
|
150
170
|
return { ...facts, reasonCode: "below_minimum_version" };
|
|
151
171
|
}
|
|
152
|
-
if (knownBadHit(contract,
|
|
172
|
+
if (knownBadHit(contract, order)) return { ...facts, reasonCode: "known_bad_version" };
|
|
153
173
|
return { ...facts, reasonCode: null };
|
|
154
174
|
}
|
|
155
175
|
|
|
@@ -172,13 +192,13 @@ function validateNativeProbe(probe) {
|
|
|
172
192
|
return probe;
|
|
173
193
|
}
|
|
174
194
|
|
|
175
|
-
export function evaluateNativeEligibility(adapter, { clientVersion,
|
|
195
|
+
export function evaluateNativeEligibility(adapter, { clientVersion, probe }) {
|
|
176
196
|
// The probe names the process that will serve the delivery. Judging the
|
|
177
197
|
// detected binary instead refuses a service that satisfies the contract.
|
|
178
198
|
// Only the version is read here; the shape is validated where it always was,
|
|
179
199
|
// so a malformed probe still returns a closed result rather than throwing.
|
|
180
200
|
const serving = isText(probe?.clientVersion) ? probe.clientVersion : clientVersion;
|
|
181
|
-
const rule = evaluateVersionContract(adapter, { clientVersion: serving
|
|
201
|
+
const rule = evaluateVersionContract(adapter, { clientVersion: serving });
|
|
182
202
|
const base = { eligible: false, reasonCode: null, minimumVersion: rule.minimumVersion,
|
|
183
203
|
protocolContract: rule.protocolContract, modes: [] };
|
|
184
204
|
const closedResult = reasonCode => deepFreeze({ ...base, reasonCode });
|
|
@@ -230,13 +250,13 @@ function validateNativeHandshakeShape(handshake) {
|
|
|
230
250
|
// the two records disagreed on every machine where a daemon had updated under
|
|
231
251
|
// its CLI, which is exactly the case the release exists to support. A refused
|
|
232
252
|
// verdict admits nothing and carries null.
|
|
233
|
-
export function validateNativeHandshake(adapter, { clientVersion,
|
|
253
|
+
export function validateNativeHandshake(adapter, { clientVersion, handshake }) {
|
|
234
254
|
// Same rationale as the probe: the handshake names the session that will
|
|
235
255
|
// actually serve, so the static rule is judged against that version. Only
|
|
236
256
|
// the version is read here; the shape is validated where it always was, so
|
|
237
257
|
// a malformed handshake still returns a closed result rather than throwing.
|
|
238
258
|
const serving = isText(handshake?.clientVersion) ? handshake.clientVersion : clientVersion;
|
|
239
|
-
const rule = evaluateVersionContract(adapter, { clientVersion: serving
|
|
259
|
+
const rule = evaluateVersionContract(adapter, { clientVersion: serving });
|
|
240
260
|
const base = { ok: false, reasonCode: null, clientVersion: null,
|
|
241
261
|
protocolContract: rule.protocolContract, modes: [],
|
|
242
262
|
opaqueEndpointRef: null, leaseUntil: null };
|
|
@@ -9,16 +9,21 @@ export const NATIVE_BINDING_MODES = Object.freeze([
|
|
|
9
9
|
"livePush", "idleWake", "busyQueue", "replyRoute",
|
|
10
10
|
]);
|
|
11
11
|
|
|
12
|
+
// Every kind an install record may carry. "shell-bootstrap" is the `claude`
|
|
13
|
+
// shim ACC 0.7.x wrote; records of it stay readable so it can be retired.
|
|
12
14
|
export const NATIVE_ACTIVATION_KINDS = Object.freeze([
|
|
13
15
|
"shell-bootstrap", "native-config", "native-service",
|
|
14
16
|
]);
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
]);
|
|
18
|
+
// The kinds an adapter may still ask for.
|
|
19
|
+
export const PLANNABLE_ACTIVATION_KINDS = Object.freeze(["native-config", "native-service"]);
|
|
19
20
|
|
|
20
21
|
// Every reason a client is not live, closed so doctor, hooks, and docs share
|
|
21
22
|
// one vocabulary and vendor strings never leak through it.
|
|
23
|
+
// "platform_not_captured" and "prerelease_not_captured" are historical: 0.8.0
|
|
24
|
+
// and earlier refused a platform no capture named and any prerelease suffix,
|
|
25
|
+
// and their native-attempt records carry both. Nothing produces either any
|
|
26
|
+
// more (docs/design/2026-09-26-native-delivery-across-platforms.md).
|
|
22
27
|
export const NATIVE_REASON_CODES = Object.freeze([
|
|
23
28
|
"native_delivery_unsupported", "platform_not_captured", "version_unavailable",
|
|
24
29
|
"prerelease_not_captured", "below_minimum_version", "known_bad_version",
|
|
@@ -34,7 +39,6 @@ export const NATIVE_REASON_CODES = Object.freeze([
|
|
|
34
39
|
export const STABLE_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z.-]+)?$/;
|
|
35
40
|
export const CONTRACT_ID = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
36
41
|
export const IDENTIFIER = /^[a-z][a-z0-9_-]*$/;
|
|
37
|
-
export const COMMAND_NAME = /^[a-z][a-z0-9_.-]*$/;
|
|
38
42
|
export const FINGERPRINT = /^sha256:[0-9a-f]{64}$/;
|
|
39
43
|
export const TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/;
|
|
40
44
|
export const SHELL_SOURCE = /[;&|<>`\n\0]|\$\(/;
|
|
@@ -2,7 +2,7 @@ import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
-
import { describeDeliveryFallback, detectInstallation, livePolicyOf, loadOwnership,
|
|
5
|
+
import { describeDeliveryFallback, detectInstallation, livePolicyOf, loadOwnership, verifyOwned }
|
|
6
6
|
from "@agents-can-communicate/installer";
|
|
7
7
|
import { AccError, EXIT } from "@agents-can-communicate/protocol";
|
|
8
8
|
|
|
@@ -114,12 +114,9 @@ export async function diagnoseAdapters({ options, runtime, detect = detectInstal
|
|
|
114
114
|
const home = options?.home ?? runtime?.env?.HOME ?? homedir();
|
|
115
115
|
const { data: dataHome } = platformPaths({ platform: runtime?.platform,
|
|
116
116
|
env: runtime?.env ?? {} });
|
|
117
|
-
// The same
|
|
118
|
-
// shell bootstrap it would apply. Omitting them left detection with a null
|
|
119
|
-
// shell, which degrades every shell-bootstrap client to `unsupported_shell` -
|
|
120
|
-
// so a zsh machine with a working shim read as degraded on every run.
|
|
117
|
+
// The same environment install reads, so detection probes the same client.
|
|
121
118
|
const clients = clientContext(home, path.join(dataHome, "acc"),
|
|
122
|
-
{
|
|
119
|
+
{ env: runtime?.env ?? {} });
|
|
123
120
|
const adapters = ALL_ADAPTERS();
|
|
124
121
|
const detected = await detect({ adapters, context: clients,
|
|
125
122
|
probeTimeoutMs: probeTimeout(runtime?.env) });
|
|
@@ -297,6 +294,11 @@ export async function runDoctor({ options, context, runtime }) {
|
|
|
297
294
|
...nativeSessionLines(adapters),
|
|
298
295
|
...adapters.filter(adapter => adapter.present && adapter.outgoingDelivery?.state === "configured")
|
|
299
296
|
.map(adapter => ` ${adapter.displayName} ${adapter.outgoingDelivery.diagnostic}`),
|
|
297
|
+
// The client's own inbound control decides whether a wake reaches the model.
|
|
298
|
+
// Only someone who turned live delivery on needs to hear about it.
|
|
299
|
+
...adapters.filter(adapter => adapter.present && adapter.nativeDelivery.configured
|
|
300
|
+
&& typeof adapter.inboundDelivery?.diagnostic === "string")
|
|
301
|
+
.map(adapter => ` ${adapter.displayName} inbound: ${adapter.inboundDelivery.diagnostic}`),
|
|
300
302
|
...(manager === null ? [] : [` automatic updates ${manager.auto ? "on" : "off"}; ACC ${manager.active.version}`
|
|
301
303
|
+ (manager.pin ? `; pinned to ${manager.pin}` : ""), ...(update.notice ? [` ${update.notice}`] : [])]),
|
|
302
304
|
...data.remediation.map(line => ` ${line}`),
|
|
@@ -7,8 +7,7 @@ import { createCodexAdapter } from "@agents-can-communicate/adapter-codex";
|
|
|
7
7
|
import { createGeminiCliAdapter } from "@agents-can-communicate/adapter-gemini-cli";
|
|
8
8
|
import { createGrokAdapter } from "@agents-can-communicate/adapter-grok";
|
|
9
9
|
import { createKimiAdapter } from "@agents-can-communicate/adapter-kimi";
|
|
10
|
-
import { applyPlan, detectInstallation,
|
|
11
|
-
loadOwnership, planInstallation, rcFileFor, shellOf, shimDirFor }
|
|
10
|
+
import { applyPlan, detectInstallation, loadOwnership, planInstallation }
|
|
12
11
|
from "@agents-can-communicate/installer";
|
|
13
12
|
import { AccError, EXIT } from "@agents-can-communicate/protocol";
|
|
14
13
|
|
|
@@ -29,7 +28,7 @@ export { decideDelivery } from "./install-delivery-consent.mjs";
|
|
|
29
28
|
// pointed at the home itself writes beside them rather than inside them. That
|
|
30
29
|
// install reports success and the client never reads a byte of it.
|
|
31
30
|
export const clientContext = (home, stateRoot,
|
|
32
|
-
{
|
|
31
|
+
{ env = {}, dataHome, cwd = process.cwd() } = {}) => ({
|
|
33
32
|
home,
|
|
34
33
|
...(dataHome === undefined ? {} : { dataHome }),
|
|
35
34
|
configDir: path.join(home, ".claude"),
|
|
@@ -39,10 +38,8 @@ export const clientContext = (home, stateRoot,
|
|
|
39
38
|
kimiHome: path.join(home, ".kimi-code"),
|
|
40
39
|
grokHome: typeof env.GROK_HOME === "string" && env.GROK_HOME !== ""
|
|
41
40
|
? env.GROK_HOME : path.join(home, ".grok"),
|
|
42
|
-
// The user's
|
|
43
|
-
//
|
|
44
|
-
// shim would exec. Detection reads them; nothing here writes.
|
|
45
|
-
shell,
|
|
41
|
+
// The user's PATH, for finding the real client executable to probe.
|
|
42
|
+
// Detection reads it; nothing here writes.
|
|
46
43
|
env,
|
|
47
44
|
// Antigravity CLI keeps its hooks in the `~/.gemini` tree Gemini CLI also
|
|
48
45
|
// uses, and reads none of the same files. Two locations load - the
|
|
@@ -200,8 +197,7 @@ export function describeOutcome({ action, acted, failed = [], skipped = [],
|
|
|
200
197
|
export function failureOf({ action, acted, failed = [], operations = [], skipped = [], home }) {
|
|
201
198
|
if (failed.length === 0) return null;
|
|
202
199
|
return new AccError(EXIT.DATA,
|
|
203
|
-
|
|
204
|
-
...reloadAdvice(operations)].join("\n"), { failed });
|
|
200
|
+
describeOutcome({ action, acted, failed, operations, skipped, home }), { failed });
|
|
205
201
|
}
|
|
206
202
|
|
|
207
203
|
/**
|
|
@@ -220,23 +216,13 @@ export function actedOn(result) {
|
|
|
220
216
|
+ (operation.changes?.length ?? 0) > 0)).length;
|
|
221
217
|
}
|
|
222
218
|
|
|
223
|
-
// Said once, after the first PATH block is written: a running shell and a
|
|
224
|
-
// running client know nothing about it.
|
|
225
|
-
function reloadAdvice(operations) {
|
|
226
|
-
const appended = operations.filter(operation => operation.appendedRcBlock === true);
|
|
227
|
-
if (appended.length === 0) return [];
|
|
228
|
-
const rc = appended[0].nativeActivation?.rcFile ?? "your shell rc file";
|
|
229
|
-
return ["", `native delivery is wired for new sessions: open a new terminal (or reload ${rc}), `
|
|
230
|
-
+ "then start the client normally; a session already running keeps durable delivery"];
|
|
231
|
-
}
|
|
232
|
-
|
|
233
219
|
export async function runInstallCommand({ options, runtime, action = "install" }) {
|
|
234
220
|
const adapters = selectAdapters(options.adapter);
|
|
235
221
|
const home = options.home ?? runtime.env?.HOME ?? homedir();
|
|
236
222
|
const { data: dataHome } = platformPaths({ platform: runtime.platform,
|
|
237
223
|
env: runtime.env ?? {} });
|
|
238
224
|
const context = clientContext(home, path.join(dataHome, "acc"),
|
|
239
|
-
{
|
|
225
|
+
{ env: runtime.env ?? {}, dataHome });
|
|
240
226
|
|
|
241
227
|
const detected = await detectInstallation({ adapters, context,
|
|
242
228
|
probeTimeoutMs: probeTimeout(runtime.env) });
|
|
@@ -273,7 +259,7 @@ export async function runInstallCommand({ options, runtime, action = "install" }
|
|
|
273
259
|
deliveryDecisionByAdapter: decided.deliveryDecisionByAdapter,
|
|
274
260
|
allowServiceSetup: action === "install" });
|
|
275
261
|
const apply = (paths = {}) => applyPlan({ plan, adapters, context: { ...context, ...paths },
|
|
276
|
-
dataHome, dryRun, accVersion
|
|
262
|
+
dataHome, dryRun, accVersion });
|
|
277
263
|
const result = action === "install" && !dryRun && runtime.packageRoot
|
|
278
264
|
? await installManaged({ packageRoot: runtime.packageRoot,
|
|
279
265
|
managerRoot: path.join(dataHome, "acc", "runtime"), dataHome, home, cwd: options.cwd ?? runtime.cwd,
|
|
@@ -293,9 +279,8 @@ export async function runInstallCommand({ options, runtime, action = "install" }
|
|
|
293
279
|
|
|
294
280
|
return { data: { ...result, plan, dataHome, deliveryByAdapter: decided.deliveryByAdapter,
|
|
295
281
|
deliveryDecisionByAdapter: decided.deliveryDecisionByAdapter, asked: decided.asked },
|
|
296
|
-
text:
|
|
282
|
+
text: describeOutcome({ action, acted, failed: result.failed,
|
|
297
283
|
skipped: plan.skipped, operations: result.operations, home }),
|
|
298
|
-
...reloadAdvice(result.operations)].join("\n"),
|
|
299
284
|
error: failureOf({ action, acted, failed: result.failed, operations: result.operations,
|
|
300
285
|
skipped: plan.skipped, home }) };
|
|
301
286
|
}
|
|
@@ -57,9 +57,6 @@ function questionFor(entries) {
|
|
|
57
57
|
const names = entries.map(entry => entry.displayName ?? entry.adapterId).join(", ");
|
|
58
58
|
const setup = [...new Set(entries.map(entry => entry.outgoingDelivery?.setup)
|
|
59
59
|
.filter(value => typeof value === "string"))];
|
|
60
|
-
const channels = entries.some(entry => (entry.nativeDelivery?.activationPlan?.mechanisms ?? [])
|
|
61
|
-
.some(mechanism => mechanism.kind === "shell-bootstrap"
|
|
62
|
-
&& (mechanism.prefixArgs ?? []).some(argument => argument.startsWith("--"))));
|
|
63
60
|
return [
|
|
64
61
|
`Complete automatic peer-request setup for ${names}? (experimental)`,
|
|
65
62
|
" Yes: automatic turns can spend tokens without waiting for you.",
|
|
@@ -69,7 +66,9 @@ function questionFor(entries) {
|
|
|
69
66
|
...entries.filter(needsPrerequisite).map(entry =>
|
|
70
67
|
` Download the official Codex ${entry.nativeServiceSetup.cliVersion} standalone package for its service; retain your existing codex command.`),
|
|
71
68
|
...setup.map(note => ` ${note}`),
|
|
72
|
-
...(
|
|
69
|
+
...(entries.some(entry => entry.adapterId === "claude_code")
|
|
70
|
+
? [" Claude Code sessions that bypass permission prompts ask before each ACC wake;",
|
|
71
|
+
" set Claude Code's crossSessionInbound to accept to take wakes without asking."] : []),
|
|
73
72
|
" No: keep current delivery policies and use available next-turn hooks or acc inbox.",
|
|
74
73
|
].join("\n");
|
|
75
74
|
}
|
|
@@ -138,13 +138,26 @@ function obligationFor(kind, explicit, addressed) {
|
|
|
138
138
|
return obligation;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
function recordedText(message, delivery) {
|
|
141
|
+
export function recordedText(message, delivery) {
|
|
142
142
|
const diagnostics = delivery.map(item => item.outcome === "offered"
|
|
143
143
|
? `live offered to ${item.recipientParticipantId} via ${item.transport}`
|
|
144
|
+
: item.outcome === "woken" && item.pendingApproval === true
|
|
145
|
+
? `sent a wake to ${item.recipientParticipantId} via ${item.transport}, which its session holds `
|
|
146
|
+
+ "for approval because it bypasses permission prompts; the message arrives with its next turn"
|
|
147
|
+
: item.outcome === "woken"
|
|
148
|
+
? `woke ${item.recipientParticipantId} via ${item.transport}; the message arrives with its next turn`
|
|
144
149
|
: item.outcome === "queued"
|
|
145
150
|
? item.errorCode === "transport_permission_denied"
|
|
146
151
|
? "live offer blocked by sender permissions; message remains queued; run acc doctor in the sending session"
|
|
147
|
-
:
|
|
152
|
+
: item.errorCode === "no_live_transport"
|
|
153
|
+
? `${item.recipientParticipantId} has no live transport; the message waits in its inbox`
|
|
154
|
+
: item.errorCode === "recipient_offline" && item.endReason === "clear"
|
|
155
|
+
? `${item.recipientParticipantId}'s conversation was cleared (/clear); the message waits `
|
|
156
|
+
+ "until that conversation resumes"
|
|
157
|
+
: item.errorCode === "recipient_offline"
|
|
158
|
+
? `${item.recipientParticipantId} has no open session; the message waits until it starts `
|
|
159
|
+
+ "or resumes one"
|
|
160
|
+
: `live offer unavailable (${item.errorCode ?? "durable_fallback"})`
|
|
148
161
|
: `delivery already ${item.outcome}`);
|
|
149
162
|
return [`recorded ${message.messageId}`, ...diagnostics].join("; ");
|
|
150
163
|
}
|
|
@@ -185,7 +198,15 @@ export function describeStatus(status) {
|
|
|
185
198
|
// A transport accepting a message is not a model reading it. Said only when
|
|
186
199
|
// it is true of something, so the everyday line stays the everyday line.
|
|
187
200
|
const offered = status.counts.unretrieved?.offered ?? 0;
|
|
188
|
-
|
|
201
|
+
const withOffers = offered === 0 ? line : `${line}; ${offered} offered, not retrieved`;
|
|
202
|
+
// Status lists a participant with no open session only while messages wait
|
|
203
|
+
// for it; each is named here, with the client's reason when it gave one. The
|
|
204
|
+
// count is what its next turn would still show, never an offer already made.
|
|
205
|
+
const waiting = (status.participants ?? [])
|
|
206
|
+
.filter(item => item.presence === "offline" && (item.waiting ?? 0) > 0)
|
|
207
|
+
.map(item => `${item.participantId} (${item.endReason === "clear" ? "cleared, " : ""}${item.waiting})`);
|
|
208
|
+
return waiting.length === 0 ? withOffers
|
|
209
|
+
: `${withOffers}; waiting for a closed session: ${waiting.join(", ")}`;
|
|
189
210
|
}
|
|
190
211
|
|
|
191
212
|
const HANDLERS = Object.freeze({
|
|
@@ -7,24 +7,13 @@ import { canonicalManagerRoot, readControl, readManagedJson } from "./state.mjs"
|
|
|
7
7
|
import { commandPrefix } from "./command-prefix.mjs";
|
|
8
8
|
|
|
9
9
|
export const ENTRY_KINDS = Object.freeze([
|
|
10
|
-
"acc", "acc-hook", "acc-mcp", "acc-
|
|
10
|
+
"acc", "acc-hook", "acc-mcp", "acc-antigravity-relay",
|
|
11
11
|
]);
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export function parseBootstrapOptions(args) {
|
|
18
|
-
const options = {};
|
|
19
|
-
for (let index = 0; index < args.length; index += 2) {
|
|
20
|
-
const key = BOOTSTRAP_FLAGS.get(args[index]);
|
|
21
|
-
const value = args[index + 1];
|
|
22
|
-
if (key === undefined || typeof value !== "string" || value === "" || key in options) return null;
|
|
23
|
-
options[key] = value;
|
|
24
|
-
}
|
|
25
|
-
return BOOTSTRAP_FLAGS.values().every(key => key in options) && path.isAbsolute(options.dataHome)
|
|
26
|
-
? options : null;
|
|
27
|
-
}
|
|
13
|
+
// Kinds a 0.7.x install wrote a launcher for. The package still ships their
|
|
14
|
+
// entrypoints for 0.7.x updaters and launchers; a current install removes the
|
|
15
|
+
// launchers themselves.
|
|
16
|
+
export const RETIRED_ENTRY_KINDS = Object.freeze(["acc-bootstrap", "acc-claude-channel"]);
|
|
28
17
|
|
|
29
18
|
// Bootstrap resolution deliberately needs only Node built-ins: no selected
|
|
30
19
|
// generation is imported until admission and its actual PID lease are durable.
|
|
@@ -43,11 +32,9 @@ export function invokedDirectly(url) {
|
|
|
43
32
|
|
|
44
33
|
function unavailable(kind) {
|
|
45
34
|
// Never reflect control file content or payloads into a client diagnostic.
|
|
46
|
-
if (kind === "acc-hook" || kind === "acc-
|
|
35
|
+
if (kind === "acc-hook" || kind === "acc-antigravity-relay") {
|
|
47
36
|
process.stderr.write("acc: coordination unavailable during runtime update; session may continue\n");
|
|
48
37
|
process.exitCode = 0;
|
|
49
|
-
} else if (kind === "acc-bootstrap") {
|
|
50
|
-
process.exitCode = 1; // The vendor shell shim launches its original command.
|
|
51
38
|
} else {
|
|
52
39
|
process.stderr.write("acc: runtime unavailable; retry after the update or run acc update to recover\n");
|
|
53
40
|
process.exitCode = 4;
|
|
@@ -83,28 +70,12 @@ async function updateImplementation(packageRoot, control) {
|
|
|
83
70
|
export async function runEntry({ kind, packageRoot, managerRoot, managedRequired = false }) {
|
|
84
71
|
if (!ENTRY_KINDS.includes(kind)) throw new Error("unknown ACC entry point");
|
|
85
72
|
const command = kind === "acc" ? commandPrefix(process.argv.slice(2)).command : null;
|
|
86
|
-
const bootstrapOptions = kind === "acc-bootstrap" ? parseBootstrapOptions(process.argv.slice(2)) : null;
|
|
87
|
-
if (kind === "acc-bootstrap" && bootstrapOptions === null) {
|
|
88
|
-
if (process.env.ACC_BOOTSTRAP_DEBUG === "1") {
|
|
89
|
-
process.stderr.write("acc-bootstrap: usage: --adapter <id> --real-executable <path> --data-home <path>\n");
|
|
90
|
-
}
|
|
91
|
-
process.exitCode = 2;
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
73
|
let selected = packageRoot;
|
|
95
74
|
let managed = null;
|
|
96
75
|
let managementOnly = false;
|
|
97
76
|
let root;
|
|
98
77
|
try {
|
|
99
|
-
|
|
100
|
-
const requestedRoot = requestedDataHome === null ? null
|
|
101
|
-
: await canonicalManagerRoot(path.join(requestedDataHome, "acc", "runtime"));
|
|
102
|
-
if (managerRoot && requestedDataHome !== null
|
|
103
|
-
&& requestedDataHome !== await canonicalManagerRoot(path.dirname(path.dirname(managerRoot)))) {
|
|
104
|
-
throw new Error("bootstrap cache home differs from its launcher");
|
|
105
|
-
}
|
|
106
|
-
root = await canonicalManagerRoot(managerRoot ?? requestedRoot ?? managerLocation());
|
|
107
|
-
if (requestedRoot !== null && root !== requestedRoot) throw new Error("bootstrap data home differs from its launcher");
|
|
78
|
+
root = await canonicalManagerRoot(managerRoot ?? managerLocation());
|
|
108
79
|
const control = await readControl(root);
|
|
109
80
|
if (control !== null) {
|
|
110
81
|
selected = control.active.root;
|
|
@@ -138,13 +109,6 @@ export async function runEntry({ kind, packageRoot, managerRoot, managedRequired
|
|
|
138
109
|
} catch { /* Fall through to the same graceful unavailability below. */ }
|
|
139
110
|
}
|
|
140
111
|
unavailable(kind);
|
|
141
|
-
if (kind === "acc-claude-channel" && selected) {
|
|
142
|
-
try {
|
|
143
|
-
const { startInertChannel } = await import(pathToFileURL(path.join(selected,
|
|
144
|
-
"bin", "entrypoints", "claude-channel-stdio.mjs")).href);
|
|
145
|
-
startInertChannel();
|
|
146
|
-
} catch { /* The stderr notice above already fired either way. */ }
|
|
147
|
-
}
|
|
148
112
|
return;
|
|
149
113
|
}
|
|
150
114
|
const runtime = await import(pathToFileURL(path.join(selected, "bin", "entrypoints", `${kind}.mjs`)).href);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { lstat, mkdtemp, open, readFile, rename, rm } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { ENTRY_KINDS } from "./entry.mjs";
|
|
4
|
+
import { ENTRY_KINDS, RETIRED_ENTRY_KINDS } from "./entry.mjs";
|
|
5
5
|
import { managedDirectory, syncDirectory } from "./state.mjs";
|
|
6
6
|
|
|
7
7
|
const MODULES = ["entry.mjs", "command-prefix.mjs", "state.mjs", "generation-files.mjs",
|
|
@@ -14,8 +14,6 @@ async function durableFile(file, bytes, mode = 0o600) {
|
|
|
14
14
|
|
|
15
15
|
export const stablePaths = root => ({
|
|
16
16
|
cli: path.join(root, "bin", "acc.mjs"), runner: path.join(root, "bin", "acc-hook.mjs"),
|
|
17
|
-
bootstrap: path.join(root, "bin", "acc-bootstrap.mjs"),
|
|
18
|
-
channel: path.join(root, "bin", "acc-claude-channel.mjs"),
|
|
19
17
|
antigravityRelay: path.join(root, "bin", "acc-antigravity-relay.mjs"),
|
|
20
18
|
});
|
|
21
19
|
|
|
@@ -57,6 +55,10 @@ export async function writeLaunchers(root, packageRoot) {
|
|
|
57
55
|
await rename(temporary, path.join(bin, `${kind}.mjs`));
|
|
58
56
|
} finally { await rm(temporary, { force: true }); }
|
|
59
57
|
}
|
|
58
|
+
// A 0.7.x `claude` shim or plugin copy that still reaches for one of these
|
|
59
|
+
// finds nothing: the shim then launches the vendor command untouched, and a
|
|
60
|
+
// re-laid plugin names none of them.
|
|
61
|
+
for (const kind of RETIRED_ENTRY_KINDS) await rm(path.join(bin, `${kind}.mjs`), { force: true });
|
|
60
62
|
await syncDirectory(bin);
|
|
61
63
|
return stablePaths(root);
|
|
62
64
|
}
|