agents-can-communicate 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +112 -91
  2. package/SECURITY.md +8 -5
  3. package/bin/acc-bootstrap.mjs +5 -54
  4. package/bin/acc-claude-channel.mjs +8 -175
  5. package/bin/acc-hook.mjs +4 -133
  6. package/bin/acc-mcp.mjs +4 -59
  7. package/bin/acc-update-worker.mjs +8 -0
  8. package/bin/acc.mjs +5 -39
  9. package/bin/entrypoints/acc-bootstrap.mjs +47 -0
  10. package/bin/entrypoints/acc-claude-channel.mjs +149 -0
  11. package/bin/entrypoints/acc-hook.mjs +58 -0
  12. package/bin/entrypoints/acc-mcp.mjs +70 -0
  13. package/bin/entrypoints/acc.mjs +51 -0
  14. package/bin/entrypoints/claude-channel-stdio.mjs +29 -0
  15. package/bin/entrypoints/hook-output.mjs +73 -0
  16. package/docs/ADAPTER_AUTHORING.md +62 -24
  17. package/docs/ARCHITECTURE.md +79 -20
  18. package/docs/CAPABILITIES.md +52 -30
  19. package/docs/CLI.md +164 -35
  20. package/docs/CONCEPTS.md +97 -93
  21. package/docs/CONFIGURATION.md +66 -45
  22. package/docs/DESIGN_DECISIONS.md +24 -15
  23. package/docs/GETTING_STARTED.md +124 -118
  24. package/docs/GLOSSARY.md +12 -6
  25. package/docs/HOW_IT_WORKS.md +136 -229
  26. package/docs/MCP.md +98 -31
  27. package/docs/PROTOCOL.md +76 -14
  28. package/docs/RELEASING.md +128 -86
  29. package/docs/SECURITY_MODEL.md +52 -15
  30. package/docs/TROUBLESHOOTING.md +88 -18
  31. package/docs/UPGRADING.md +119 -0
  32. package/docs/WHY_ACC.md +19 -16
  33. package/docs/index.md +40 -24
  34. package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +1 -1
  35. package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/skills/acc/SKILL.md +112 -9
  36. package/node_modules/@agents-can-communicate/adapter-claude-code/src/install.mjs +6 -6
  37. package/node_modules/@agents-can-communicate/adapter-claude-code/src/native-delivery.mjs +2 -1
  38. package/node_modules/@agents-can-communicate/adapter-codex/certification.json +38 -13
  39. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/certification-provenance.json +220 -0
  40. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-current-binding-product-evidence.json +1549 -0
  41. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-current-binding-product.json +27 -0
  42. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-product-evidence.json +1549 -0
  43. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-product.json +27 -0
  44. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-transport-evidence.json +259 -0
  45. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-transport.json +21 -0
  46. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-current-binding-product-evidence.json +1549 -0
  47. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-current-binding-product.json +27 -0
  48. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-product-evidence.json +1549 -0
  49. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-product.json +27 -0
  50. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-transport-evidence.json +259 -0
  51. package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-transport.json +21 -0
  52. package/node_modules/@agents-can-communicate/adapter-codex/package.json +14 -2
  53. package/node_modules/@agents-can-communicate/adapter-codex/plugin/skills/acc/SKILL.md +111 -8
  54. package/node_modules/@agents-can-communicate/adapter-codex/src/adapter.mjs +26 -44
  55. package/node_modules/@agents-can-communicate/adapter-codex/src/app-server-client.mjs +70 -17
  56. package/node_modules/@agents-can-communicate/adapter-codex/src/config-block.mjs +231 -0
  57. package/node_modules/@agents-can-communicate/adapter-codex/src/hooks.mjs +1 -1
  58. package/node_modules/@agents-can-communicate/adapter-codex/src/install.mjs +71 -63
  59. package/node_modules/@agents-can-communicate/adapter-codex/src/native-delivery.mjs +107 -103
  60. package/node_modules/@agents-can-communicate/adapter-codex/src/native-endpoint.mjs +96 -0
  61. package/node_modules/@agents-can-communicate/adapter-codex/src/ws-json-rpc.mjs +3 -2
  62. package/node_modules/@agents-can-communicate/adapter-gemini-cli/extension/gemini-extension.json +1 -1
  63. package/node_modules/@agents-can-communicate/adapter-gemini-cli/extension/skills/acc/SKILL.md +111 -8
  64. package/node_modules/@agents-can-communicate/adapter-gemini-cli/package.json +1 -1
  65. package/node_modules/@agents-can-communicate/adapter-gemini-cli/src/install.mjs +2 -2
  66. package/node_modules/@agents-can-communicate/adapter-grok/package.json +1 -1
  67. package/node_modules/@agents-can-communicate/adapter-grok/plugin/skills/acc/SKILL.md +111 -8
  68. package/node_modules/@agents-can-communicate/adapter-grok/src/install.mjs +2 -2
  69. package/node_modules/@agents-can-communicate/adapter-kimi/package.json +1 -1
  70. package/node_modules/@agents-can-communicate/adapter-kimi/plugin/skills/acc/SKILL.md +111 -8
  71. package/node_modules/@agents-can-communicate/adapter-kimi/src/hooks.mjs +1 -1
  72. package/node_modules/@agents-can-communicate/adapter-kimi/src/install.mjs +2 -2
  73. package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
  74. package/node_modules/@agents-can-communicate/adapter-sdk/src/capabilities.mjs +5 -0
  75. package/node_modules/@agents-can-communicate/adapter-sdk/src/context-projector.mjs +26 -4
  76. package/node_modules/@agents-can-communicate/adapter-sdk/src/decision-text.mjs +19 -0
  77. package/node_modules/@agents-can-communicate/adapter-sdk/src/hook-shim.mjs +5 -3
  78. package/node_modules/@agents-can-communicate/adapter-sdk/src/index.mjs +1 -0
  79. package/node_modules/@agents-can-communicate/adapter-sdk/src/native-delivery.mjs +9 -2
  80. package/node_modules/@agents-can-communicate/adapter-sdk/src/native-vocabulary.mjs +2 -6
  81. package/node_modules/@agents-can-communicate/adapter-sdk/src/session-binding.mjs +29 -9
  82. package/node_modules/@agents-can-communicate/adapter-sdk/src/toml-block.mjs +8 -6
  83. package/node_modules/@agents-can-communicate/cli/package.json +4 -2
  84. package/node_modules/@agents-can-communicate/cli/src/args.mjs +49 -22
  85. package/node_modules/@agents-can-communicate/cli/src/doctor-command.mjs +11 -4
  86. package/node_modules/@agents-can-communicate/cli/src/git-probe.mjs +9 -3
  87. package/node_modules/@agents-can-communicate/cli/src/help.mjs +109 -14
  88. package/node_modules/@agents-can-communicate/cli/src/install-command.mjs +66 -18
  89. package/node_modules/@agents-can-communicate/cli/src/main.mjs +35 -12
  90. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/activation.mjs +110 -0
  91. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/command.mjs +63 -0
  92. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/download.mjs +71 -0
  93. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/entry.mjs +110 -0
  94. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/generation.mjs +167 -0
  95. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/install.mjs +73 -0
  96. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/launchers.mjs +60 -0
  97. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/leases.mjs +60 -0
  98. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/location.mjs +55 -0
  99. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/mutex.mjs +145 -0
  100. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/policy.mjs +4 -0
  101. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/refresh.mjs +40 -0
  102. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/schedule.mjs +21 -0
  103. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/state.mjs +122 -0
  104. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/worker.mjs +65 -0
  105. package/node_modules/@agents-can-communicate/cli/src/session-owner.mjs +33 -108
  106. package/node_modules/@agents-can-communicate/cli/src/update-command.mjs +25 -7
  107. package/node_modules/@agents-can-communicate/core/package.json +1 -1
  108. package/node_modules/@agents-can-communicate/core/src/attention.mjs +3 -0
  109. package/node_modules/@agents-can-communicate/core/src/claims.mjs +13 -8
  110. package/node_modules/@agents-can-communicate/core/src/conversations.mjs +48 -24
  111. package/node_modules/@agents-can-communicate/core/src/decision-changes.mjs +36 -0
  112. package/node_modules/@agents-can-communicate/core/src/decision-state.mjs +42 -0
  113. package/node_modules/@agents-can-communicate/core/src/delivery-bindings.mjs +11 -4
  114. package/node_modules/@agents-can-communicate/core/src/inbox.mjs +48 -17
  115. package/node_modules/@agents-can-communicate/core/src/intents.mjs +35 -15
  116. package/node_modules/@agents-can-communicate/core/src/materialisation.mjs +7 -5
  117. package/node_modules/@agents-can-communicate/core/src/message-pages.mjs +75 -0
  118. package/node_modules/@agents-can-communicate/core/src/next-turn-delivery.mjs +31 -0
  119. package/node_modules/@agents-can-communicate/core/src/session-opening.mjs +128 -0
  120. package/node_modules/@agents-can-communicate/core/src/sessions.mjs +58 -132
  121. package/node_modules/@agents-can-communicate/core/src/sync.mjs +31 -2
  122. package/node_modules/@agents-can-communicate/delivery-router/package.json +1 -1
  123. package/node_modules/@agents-can-communicate/delivery-router/src/refresh-binding.mjs +38 -0
  124. package/node_modules/@agents-can-communicate/delivery-router/src/router.mjs +86 -10
  125. package/node_modules/@agents-can-communicate/hook-runner/package.json +1 -1
  126. package/node_modules/@agents-can-communicate/hook-runner/src/client-version.mjs +1 -0
  127. package/node_modules/@agents-can-communicate/hook-runner/src/native-binding.mjs +6 -4
  128. package/node_modules/@agents-can-communicate/hook-runner/src/native-retirement.mjs +48 -0
  129. package/node_modules/@agents-can-communicate/hook-runner/src/process-table.mjs +1 -1
  130. package/node_modules/@agents-can-communicate/hook-runner/src/runner.mjs +175 -67
  131. package/node_modules/@agents-can-communicate/hook-runner/src/session-lifecycle.mjs +13 -0
  132. package/node_modules/@agents-can-communicate/installer/package.json +4 -2
  133. package/node_modules/@agents-can-communicate/installer/src/apply.mjs +17 -2
  134. package/node_modules/@agents-can-communicate/installer/src/bootstrap-runtime.mjs +11 -5
  135. package/node_modules/@agents-can-communicate/installer/src/detect.mjs +1 -1
  136. package/node_modules/@agents-can-communicate/installer/src/index.mjs +1 -0
  137. package/node_modules/@agents-can-communicate/installer/src/live-policy.mjs +21 -0
  138. package/node_modules/@agents-can-communicate/installer/src/native-activation.mjs +20 -6
  139. package/node_modules/@agents-can-communicate/installer/src/ownership.mjs +10 -4
  140. package/node_modules/@agents-can-communicate/installer/src/plan.mjs +3 -3
  141. package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
  142. package/node_modules/@agents-can-communicate/mcp-server/src/protocol.mjs +85 -0
  143. package/node_modules/@agents-can-communicate/mcp-server/src/resources.mjs +3 -2
  144. package/node_modules/@agents-can-communicate/mcp-server/src/server.mjs +59 -57
  145. package/node_modules/@agents-can-communicate/mcp-server/src/tools.mjs +47 -33
  146. package/node_modules/@agents-can-communicate/protocol/package.json +1 -1
  147. package/node_modules/@agents-can-communicate/protocol/src/conversations.mjs +3 -0
  148. package/node_modules/@agents-can-communicate/protocol/src/decision-changes.mjs +15 -0
  149. package/node_modules/@agents-can-communicate/protocol/src/index.mjs +3 -1
  150. package/node_modules/@agents-can-communicate/protocol/src/schema.mjs +14 -5
  151. package/node_modules/@agents-can-communicate/storage-filesystem/package.json +1 -1
  152. package/node_modules/@agents-can-communicate/storage-filesystem/src/atomic-json.mjs +5 -1
  153. package/node_modules/@agents-can-communicate/storage-filesystem/src/deadline.mjs +7 -0
  154. package/node_modules/@agents-can-communicate/storage-filesystem/src/index.mjs +2 -0
  155. package/node_modules/@agents-can-communicate/storage-filesystem/src/session-read.mjs +25 -0
  156. package/node_modules/@agents-can-communicate/storage-filesystem/src/store.mjs +28 -25
  157. package/package.json +2 -1
@@ -0,0 +1,29 @@
1
+ // Workspace-free MCP fallback shared by composition and managed admission failure.
2
+ import { createInertChannel } from "@agents-can-communicate/adapter-claude-code/channel";
3
+
4
+ export function startInertChannel({ write = payload => process.stdout.write(JSON.stringify(payload) + "\n") } = {}) {
5
+ const inert = createInertChannel({ write });
6
+ pump(inert.handleLine, () => process.exit(0));
7
+ return inert;
8
+ }
9
+
10
+ /** One line-delimited JSON pump, for the bound server and the unbound one alike. */
11
+ export function pump(handleLine, shutdown) {
12
+ let buffer = "";
13
+ process.stdin.setEncoding("utf8");
14
+ process.stdin.on("data", async chunk => {
15
+ buffer += chunk;
16
+ let newline = buffer.indexOf("\n");
17
+ while (newline !== -1) {
18
+ const line = buffer.slice(0, newline).trim();
19
+ buffer = buffer.slice(newline + 1);
20
+ newline = buffer.indexOf("\n");
21
+ if (line !== "") await handleLine(line);
22
+ }
23
+ });
24
+ process.stdin.on("end", shutdown);
25
+ process.once("SIGTERM", shutdown);
26
+ process.once("SIGINT", shutdown);
27
+ // Keeps the child answerable for as long as Claude holds the transport open.
28
+ process.stdin.resume();
29
+ }
@@ -0,0 +1,73 @@
1
+ export const writeOutput = (stream, output, { deadlineAt } = {}) => {
2
+ if (output === "") return Promise.resolve();
3
+ return new Promise((resolve, reject) => {
4
+ let settled = false;
5
+ let timer;
6
+ const finish = error => {
7
+ if (settled) return;
8
+ settled = true;
9
+ if (timer !== undefined) clearTimeout(timer);
10
+ if (error === undefined || error === null) resolve();
11
+ else reject(error);
12
+ };
13
+ if (deadlineAt !== undefined) {
14
+ const remaining = deadlineAt - Date.now();
15
+ if (remaining <= 0) {
16
+ finish(new Error("hook budget exhausted before stdout write"));
17
+ return;
18
+ }
19
+ timer = setTimeout(() => finish(
20
+ new Error("hook budget exhausted waiting for stdout callback")), remaining);
21
+ }
22
+ try {
23
+ stream.write(output, finish);
24
+ } catch (error) {
25
+ finish(error);
26
+ }
27
+ });
28
+ };
29
+
30
+ const DIAGNOSTIC_BYTES = 512;
31
+
32
+ function boundedDiagnostic(label, error) {
33
+ const detail = String(error?.message ?? error).replace(/[\u0000-\u001f\u007f]/g, " ");
34
+ let line = `acc: ${label}: ${detail}`;
35
+ while (Buffer.byteLength(`${line}\n`, "utf8") > DIAGNOSTIC_BYTES && line.length > 0) {
36
+ line = line.slice(0, -1);
37
+ }
38
+ return `${line}\n`;
39
+ }
40
+
41
+ function tryWrite(stream, output) {
42
+ if (output === "") return;
43
+ try {
44
+ stream.write(output, () => {});
45
+ } catch {
46
+ // A broken diagnostic stream must not turn a failed-open hook into a crash.
47
+ }
48
+ }
49
+
50
+ export async function completeHookOutput(result,
51
+ { stdout = process.stdout, stderr = process.stderr } = {}) {
52
+ if (result.failed || result.timedOut) {
53
+ // Hook failures can include arbitrary payload or filesystem text. Report
54
+ // degradation without reflecting those details into the client's output.
55
+ tryWrite(stderr, "acc: coordination unavailable; hook continued without context\n");
56
+ }
57
+ try {
58
+ await writeOutput(stdout, result.stdout ?? "", { deadlineAt: result.deadlineAt });
59
+ } catch (error) {
60
+ tryWrite(stderr, boundedDiagnostic("stdout write failed", error));
61
+ return { exitCode: 0, wroteStdout: false, committedOffers: false };
62
+ }
63
+
64
+ let committedOffers = true;
65
+ try {
66
+ await result.commitOffers?.();
67
+ } catch (error) {
68
+ committedOffers = false;
69
+ tryWrite(stderr, boundedDiagnostic("offer commit failed", error));
70
+ }
71
+ if (result.stderr) tryWrite(stderr, `${result.stderr}\n`);
72
+ return { exitCode: 0, wroteStdout: true, committedOffers };
73
+ }
@@ -1,9 +1,9 @@
1
1
  # Writing an adapter
2
2
 
3
- An adapter teaches ACC one client. Nothing else in ACC knows that client exists. This page
4
- assumes the vocabulary in [Protocol](PROTOCOL.md#identity-hierarchy) — session, participant,
5
- claim — and points to [Capabilities](CAPABILITIES.md) for what was actually measured per
6
- client; see the [documentation map](index.md) for where both fit among the rest.
3
+ Use this page to add one client without leaking vendor behavior into core. Start with the
4
+ [identity contract](PROTOCOL.md#identity), then compare the shipped evidence under
5
+ [certified support](CAPABILITIES.md#certified-support). The [documentation map](index.md)
6
+ links the surrounding concepts and runtime architecture.
7
7
 
8
8
  ```mermaid
9
9
  graph LR
@@ -14,7 +14,7 @@ graph LR
14
14
  R --> H
15
15
  ```
16
16
 
17
- ## The manifest
17
+ ## Define the manifest
18
18
 
19
19
  ```js
20
20
  export function createExampleAdapter() {
@@ -28,6 +28,7 @@ export function createExampleAdapter() {
28
28
 
29
29
  detect, install, uninstall, doctor,
30
30
  planInstall, // what install would write
31
+ preflightUninstall, // optional read-only removal validation
31
32
  normalizeHook, // client payload -> normalised event
32
33
  renderContext, // SyncResult -> text
33
34
  renderContextResult, // text + ids of complete rendered groups
@@ -36,9 +37,14 @@ export function createExampleAdapter() {
36
37
  }
37
38
  ```
38
39
 
40
+ `preflightUninstall(context)` is optional and read-only. Throw to refuse unsafe
41
+ removal before the installer deactivates native integration or deletes recorded
42
+ artifacts. `uninstall` should retain its own validation for direct adapter callers;
43
+ recorded artifact fingerprint checks still determine the `keep` paths it receives.
44
+
39
45
  `renderContextResult` is required wherever an adapter renders peer messages. It returns
40
- `{ text, includedMessageIds, includedAttentionIds }`, and the [delivery
41
- lifecycle](PROTOCOL.md#delivery-lifecycle) advances only from those ids — never by searching
46
+ `{ text, offeredMessageIds, includedAttentionIds }`, and the [receipt
47
+ lifecycle](PROTOCOL.md#receipt-lifecycle) advances only from those ids — never by searching
42
48
  `text` for one, because peer text is untrusted and can imitate another message's header.
43
49
  `projectContextResult()` implements this contract; `projectContext()` remains the text-only
44
50
  convenience API for adapters that don't need it. An adapter with only the older
@@ -46,6 +52,12 @@ convenience API for adapters that don't need it. An adapter with only the older
46
52
  instead: repeating an untracked body every turn would be quieter in code and dishonest about
47
53
  what was actually delivered.
48
54
 
55
+ The hook also supplies `reminderMessageIds`, derived from this participant's `offered` or
56
+ `retrieved` receipts with unresolved obligations. The standard projector combines those
57
+ reply/acknowledgement attention items into counts after new bodies. A missing body alone
58
+ is not evidence for compaction. Counts add no ids to `offeredMessageIds` or
59
+ `includedAttentionIds`; complete inbox and status results remain available.
60
+
49
61
  `client.command` does double duty. `detect.mjs` uses it as the version-probe binary, and
50
62
  presence liveness separately walks the hook's process ancestry for the first ancestor whose
51
63
  executable basename matches it, to learn the client's own pid. Declare the binary the client
@@ -53,7 +65,7 @@ actually runs as — `command: "claude"` for a client that really runs as `node`
53
65
  nothing, and the failure is silent: the session gets `pid: null` and falls back to reading
54
66
  presence by age alone, with nothing telling you why.
55
67
 
56
- ## Capabilities
68
+ ## Declare only proven capabilities
57
69
 
58
70
  Fourteen booleans in four groups, declared in the manifest's `capabilities` object:
59
71
 
@@ -68,7 +80,8 @@ Fourteen booleans in four groups, declared in the manifest's `capabilities` obje
68
80
  `defineAdapter` enforces the method — declaring `guards.beforeWrite: true` without
69
81
  `guardWrite()` is a usage error at construction. It also requires a passing entry in the
70
82
  validated `certification.json`; method existence is never evidence. What each shipped
71
- client was actually observed doing against this list is [Capabilities](CAPABILITIES.md#matrix).
83
+ client was actually observed doing against this list is under
84
+ [certified support](CAPABILITIES.md#certified-support).
72
85
 
73
86
  `lifecycle.heartbeat` is deliberately not a flavour of `delivery.nextTurn`. Next-turn
74
87
  delivery happens only when the client reaches a normal turn boundary; heartbeat fires on a
@@ -118,24 +131,48 @@ The rules `defineAdapter` enforces, and the ones the runtime applies:
118
131
  when numerically newer. `knownBad` names exact versions or inclusive intervals.
119
132
  - Exact-version certification still governs every non-native capability;
120
133
  `effectiveCapabilities()` is unchanged. The native rule is used for live delivery alone.
121
- - The three native methods return closed facts (`validateNativeActivationPlan()` closes the
134
+ - Native methods return closed facts (`validateNativeActivationPlan()` closes the
122
135
  activation plan) and never put vendor data - endpoints, sockets, raw errors - into core.
123
136
 
124
- ## How far you can get
137
+ `nativeDelivery.policySource` defaults to the session environment. An adapter may
138
+ declare `"installation-record"` when hooks and senders must read current recorded
139
+ recipient consent independently of the vendor process environment. Installation
140
+ records the requested policy even when current activation is unavailable. An absent
141
+ or invalid record means `off`; a previously published binding cannot override it.
142
+
143
+ The optional `refreshNativeSession()` method may revalidate an expired lease before
144
+ an offer. It returns the same closed handshake shape as `bindNativeSession()` and
145
+ must preserve the exact endpoint and protocol identity. The router rechecks the
146
+ current session, generation, retirement, uniqueness and policy before publishing
147
+ the refreshed lease. Refresh is not a heartbeat and cannot extend presence beyond
148
+ its own expiry. Optional `retireNativeSession()` cleans adapter-owned endpoint state
149
+ after confirmed core retirement; it must remain bounded and fail open for hooks.
150
+
151
+ A package-shipped passing Codex installed-hook capture additionally references its
152
+ complete real product matrix through the selected provenance record's
153
+ `productEvidence: { fixture, sha256 }`. Package verification checks the actual file,
154
+ raw digest and client/version/platform/package identity. A transport-only capture
155
+ cannot certify the installed product route. `historicalFixtures` may retain hashed
156
+ earlier failures without duplicating a certification tuple.
157
+
158
+ ## Choose the integration depth
125
159
 
126
160
  | Tier | You register | You get | You do not get |
127
161
  |---|---|---|---|
128
162
  | 0 | nothing — humans run `acc` | durable messages, status, claims | anything automatic |
129
163
  | 1 | the MCP server | attach on first call, read, claim, message | guards, session end |
130
- | 2 | hooks + skill | automatic attach, turn context, write guards, cleanup | realtime |
131
- | 3 | + realtime surface | delivery receipts, safe-point injection, child sessions | — |
164
+ | 2 | hooks + skill | only the lifecycle, context, guard, and next-turn behaviors separately captured for this client | uncaptured hook behaviors |
165
+ | 3 | + native delivery surface | only captured live-push and reply-route modes | safe-point injection or child sessions unless separately captured |
132
166
 
133
- Installed hook wiring may reach tier 2, but the effective capability is still limited to
134
- an exact certified client/version/platform. Passing native captures exist for Claude Code
135
- 2.1.258 and Codex 0.152.1 on macOS arm64; they become live capabilities only when the
136
- production transports ship behind the native delivery contract above.
167
+ Installed hook wiring may reach tier 2, but each effective hook capability still
168
+ requires exact client/version/platform evidence. Tier 3 has separate native
169
+ contracts: Claude Code Channel from 2.1.258 and Codex LocalDaemon from 0.152.1 on
170
+ macOS arm64, with current probes and exact session handshakes. Codex's earlier
171
+ remote-wrapper failure remains historical evidence. Ordinary launch now preserves
172
+ workspace identity; the full installed product matrix, including negative controls
173
+ and fallback, backs its native claim.
137
174
 
138
- ## normalizeHook
175
+ ## Normalize hook input
139
176
 
140
177
  Whitelist, never a filter. Every client hands hooks the prompt, the transcript path, or the
141
178
  tool output; none of it may survive.
@@ -151,7 +188,7 @@ return normalizedEvent({
151
188
  Refuse an unrecognised payload. Inventing a session attaches the wrong one, or a new one
152
189
  every hook, and looks like it is working.
153
190
 
154
- ## Response contracts do not port
191
+ ## Measure response contracts
155
192
 
156
193
  Measure them. Every client differs, and a wrong shape fails **silently**:
157
194
 
@@ -166,10 +203,11 @@ Measure them. Every client differs, and a wrong shape fails **silently**:
166
203
  `denyOutcome(reason)` returns `{ stdout, stderr, exitCode }`, so the runtime never has to
167
204
  know which client it is talking to. This table is only the shape each shipped adapter
168
205
  actually uses; the full experimental grid — every candidate shape tried against every
169
- client, including which ones are silently ignored — is measured in
170
- [Capabilities](CAPABILITIES.md#response-contracts-which-do-not-port).
206
+ client, including which ones are silently ignored — is recorded in each adapter's
207
+ `COMPATIBILITY.md` and certification fixtures. The cross-client summary is under
208
+ [guard limitations](CAPABILITIES.md#guard-limitations).
171
209
 
172
- ## Install and ownership
210
+ ## Preserve install ownership
173
211
 
174
212
  ```mermaid
175
213
  graph TB
@@ -188,7 +226,7 @@ Rules that are not negotiable:
188
226
  `planInstall` must use the same path helpers as `install`. A conformance test compares
189
227
  them, because a plan that drifts makes `--dry-run` a decoration.
190
228
 
191
- ## Conformance
229
+ ## Run conformance
192
230
 
193
231
  ```bash
194
232
  node --test tests/conformance/*.test.mjs
@@ -198,7 +236,7 @@ node --test tests/process/hook-wiring.test.mjs
198
236
  The second one *executes* what your install wrote. Three adapters once shipped a hook
199
237
  command that did not exist anywhere; every test was green.
200
238
 
201
- ## Record what you learned
239
+ ## Record the evidence
202
240
 
203
241
  One `COMPATIBILITY.md` per adapter: client version, event names, payload fields, the deny
204
242
  matrix, and what you could **not** observe. The next person's alternative is guessing.
@@ -1,8 +1,9 @@
1
1
  # Architecture
2
2
 
3
- ACC is a local control plane for communication facts. The execution planes remain the AI
4
- clients the user opened. Core records the truth before a delivery adapter is asked to make
5
- it arrive sooner.
3
+ Use this page to trace a communication fact from an independently opened client through
4
+ core, storage, and an optional delivery adapter. ACC is the local control plane; the AI
5
+ clients remain independent execution planes. Core records the truth before an adapter is
6
+ asked to make it arrive sooner.
6
7
 
7
8
  ```mermaid
8
9
  flowchart LR
@@ -41,7 +42,7 @@ The order is part of the public guarantee:
41
42
 
42
43
  1. validate and commit the message plus one `queued` receipt per resolved recipient;
43
44
  2. resolve the recipient's current delivery bindings;
44
- 3. check recipient policy, current reachability, and exact client-version certification;
45
+ 3. check recipient policy and the current binding's reachability and offered native mode;
45
46
  4. ask one adapter to cross its transport boundary;
46
47
  5. only after acceptance, commit `offered` and an immutable success event.
47
48
 
@@ -71,16 +72,21 @@ lease without exposing the endpoint.
71
72
 
72
73
  ## Certified capability versus current reachability
73
74
 
74
- A capability says an exact client version on an exact platform passed a captured behavior.
75
- It does not say one particular session is reachable now. A binding says what that current
76
- generation exposes and whether its lease is current. Recipient policy says whether it may
77
- spend a turn. All three must agree before live delivery is possible.
78
-
79
- Codex and Claude Code currently ship failed native-delivery captures, so their
80
- `delivery.livePush` and `delivery.replyRoute` capabilities resolve false. Gemini CLI and
81
- Kimi Code have exact-version next-turn evidence only. Grok and generic MCP use inbox
82
- polling. The architecture includes a live seam without pretending the current clients
83
- proved it.
75
+ Ordinary hook capabilities say an exact client version on an exact platform passed a
76
+ captured behavior. Native live delivery uses a separate contract: installation checks the
77
+ captured minimum, platform, and current feature probe, then each session publishes a binding
78
+ only after a generation-bound handshake. The router trusts that admission and verifies the
79
+ binding, recipient policy, declared adapter capability, and adapter response; it does not add
80
+ a third exact-version certification check. A binding says what that current generation
81
+ exposes and whether its lease is current, while recipient policy says whether it may spend a
82
+ turn.
83
+
84
+ Claude Code Channel has installed-client evidence on 2.1.258 and 2.1.260; Codex
85
+ LocalDaemon has it on 0.152.1 and 0.153.4. Both use a captured platform minimum,
86
+ current probe and exact session handshake. Codex preserves the ordinary client
87
+ launch and verifies its registered thread and workspace; ACC owns no vendor daemon
88
+ lifecycle. Gemini CLI and Kimi Code have exact-version next-turn evidence only;
89
+ Grok and generic MCP use inbox polling.
84
90
 
85
91
  ## Storage and workspace identity
86
92
 
@@ -90,20 +96,34 @@ lexically, so a cursor is simply the last sequence returned.
90
96
 
91
97
  Workspace discovery checks an explicit config first, then the Git common directory, then
92
98
  the plain directory. Multiple worktrees share a workspace id while each session records
93
- its checkout and branch. Nothing written by ACC lands inside those roots.
99
+ its checkout and branch. Runtime state never lands inside those roots; the only project
100
+ file ACC writes is an optional `acc.workspace.json` explicitly requested through
101
+ `acc config init`.
94
102
 
95
103
  A lone session can remain ephemeral. Durable state materialises when a second live session
96
- appears or the first claim, message, or handoff is committed. This makes “silent when
97
- alone” an architectural behavior, not a UI preference.
104
+ appears or the first claim, message, or handoff is committed. Solo presence therefore
105
+ does not require durable workspace history. Native turn hooks still supply the session's
106
+ own CLI arguments, so a peer joining later in the same turn does not require reattachment.
107
+ Without relevant coordination context, that identity header is the only projected content.
98
108
 
99
109
  ## Inbox, attention, and projection
100
110
 
101
- Inbox reads only messages addressed to the calling participant and advances that
102
- participant's receipt to `retrieved`. Reply validates ownership, creates an `answer` in the
111
+ Public inbox discovery uses core `listInbox`: bounded, read-only message summaries
112
+ addressed to the current participant. Exact body reads use `readInbox` and advance only
113
+ that participant's receipt to `retrieved`; the embedded core API still supports bulk
114
+ retrieval. History sync reads message records without receipt changes, using the same
115
+ summary pager or one exact id. Reply validates ownership, creates an `answer` in the
103
116
  same thread, and acknowledges the original atomically. No participant can advance another
104
117
  participant's receipt.
105
118
 
106
- Attention is computed from six explicit rules:
119
+ Decision lifecycle is derived from immutable message links, without a new record kind
120
+ or mutable decision store. Read copies carry bounded status; raw forensic snapshots stay
121
+ unchanged. Non-head decisions leave pending inbox, bulk delivery, and attention while
122
+ exact reads retain them. Replacement/withdrawal records inherit recipients before commit,
123
+ so offline readers receive the change without a forged acknowledgement. See
124
+ [the protocol contract](PROTOCOL.md#decision-lifecycle).
125
+
126
+ Attention is computed from six explicit rules for current obligations:
107
127
 
108
128
  | Priority | Kind | Observable trigger |
109
129
  |---|---|---|
@@ -121,6 +141,14 @@ Adapters project peer bodies in an attributed untrusted frame. If a complete bod
121
141
  fit, the projection keeps the message id and points to `acc inbox --message <id>` instead
122
142
  of silently truncating it.
123
143
 
144
+ For this participant's unresolved messages already `offered` or `retrieved`, the standard
145
+ projector combines reply and acknowledgement reminders into a compact count after new
146
+ message bodies. Queued obligations and claim conflicts remain individual attention items.
147
+ All pending headers stay discoverable through `acc inbox` pages; owned `acc status` retains
148
+ its complete attention list. Full bodies require an exact inbox read; projection
149
+ does not acknowledge or delete records. Explicit decision links, rather than the
150
+ projector or message age, retire old decision obligations.
151
+
124
152
  ## Hooks fail open
125
153
 
126
154
  Hook execution is bounded. If coordination state cannot be read or a decision cannot be
@@ -129,3 +157,34 @@ it, but a coordination tool must not be the reason a session stops working.
129
157
 
130
158
  Next: [Protocol](PROTOCOL.md) · [Capabilities](CAPABILITIES.md) ·
131
159
  [Security model](SECURITY_MODEL.md)
160
+
161
+ ## Managed runtime activation
162
+
163
+ The CLI owns installation generations under `<dataHome>/acc/runtime`, outside workspaces.
164
+ All five launch paths select a generation and publish an actual-process lease under one
165
+ admission mutex before loading workspace-capable code. Immutable launcher modules and
166
+ runtime directories preserve in-progress imports. Leases survive `main()` returning and
167
+ are removed only after confirmed process death. Parsed help, version, and update recovery
168
+ remain available when workspace admission is unavailable.
169
+
170
+ An independent worker downloads npm packages with lifecycle scripts disabled, checks exact
171
+ stable package identity and the discovered integrity, and health-checks the staged runtime.
172
+ Installer detection and planning happen before exclusive admission. The worker then checks
173
+ ACC process leases and native binding PIDs, records `activating`, applies every integration,
174
+ and only publishes the new active pointer after all applications succeed. Partial refresh
175
+ remains fenced and repairs forward, with the old active pointer retained but normal
176
+ workspace admission unavailable until recovery completes. Native bindings cease holding
177
+ after observed SessionEnd cleanup or confirmed process death; the vendor daemon may remain
178
+ running. Unknown PIDs remain holds until lifecycle cleanup. Presence TTL, `acc finish`,
179
+ and delivery off alone do not prove native lifecycle end. ACC never manages the daemon.
180
+
181
+ Lock ownership never expires by age. Retained nonempty tombstones prevent delayed observers
182
+ from reclaiming a successor. Under an acquired lock, maintenance removes historical
183
+ bookkeeping only when no live or unknown contender could still reference it; a stopped
184
+ contender postpones cleanup. Admission also removes confirmed-dead process leases. None of
185
+ this metadata is agent conversation history or injected context.
186
+
187
+ The first upgrade from an unmanaged binary still requires a restart. ACC cannot fence old
188
+ direct binaries or external applications importing core. Native trust and client-side cache
189
+ activation remain vendor responsibilities; refreshed files do not establish observed
190
+ capabilities.
@@ -1,5 +1,11 @@
1
1
  # Capabilities
2
2
 
3
+ Use this page to set expectations after installation. Integration means ACC can introduce
4
+ peer awareness and coordination instructions; it does not guarantee what a model will do
5
+ with them. Delivery also varies independently from awareness. The durable inbox works for
6
+ every participant, supported exact versions may add next-turn delivery, and the
7
+ experimental Codex LocalDaemon and Claude Code Channel paths can deliver while a session is idle.
8
+
3
9
  Capability honesty separates four questions that are easy to collapse:
4
10
 
5
11
  1. **Certified support** — did this exact client version and platform pass a shipped
@@ -10,8 +16,12 @@ Capability honesty separates four questions that are easy to collapse:
10
16
  kind?
11
17
  4. **Fallback** — what durable path remains when any earlier answer is no?
12
18
 
13
- A source method or vendor documentation is not certification. Unknown versions and
14
- platforms degrade to false. No weaker session inherits a stronger peer's capability.
19
+ A source method or vendor documentation is not certification. Uncaptured hook versions and
20
+ unsupported platforms degrade to false. Native minimum-based eligibility is separate. No weaker session inherits a stronger peer's capability.
21
+
22
+ Run `acc doctor` in the project when observed behavior differs from this page. It reports
23
+ the installed client version, platform, effective capability, and fallback instead of
24
+ assuming that a newer or differently packaged client behaves like a captured one.
15
25
 
16
26
  ## Certified support
17
27
 
@@ -32,22 +42,23 @@ Passing evidence currently ships for these exact versions on `darwin-arm64`:
32
42
  Every other capability in the closed shape defaults to false, including session resume,
33
43
  child sessions, startup or safe-point injection, and before-read guards.
34
44
 
35
- The `delivery.livePush` and `delivery.replyRoute` row is `no` for the exact hook versions
36
- this matrix is keyed to. Native live delivery was captured on newer clients - Claude Code
37
- 2.1.258 (livePush and replyRoute) and Codex 0.152.1 (livePush) - and is admitted through the
38
- native delivery contract rather than exact-version certification: it is off until a per-client
39
- opt-in, experimental, and never turns on for a client below the captured minimum.
45
+ The native rows remain `no` for the older exact hook versions in this matrix.
46
+ Separate installed-client captures establish Codex `livePush` on 0.152.1 and
47
+ 0.153.4, and Claude Code `livePush` plus `replyRoute` on 2.1.258 and 2.1.260.
48
+ Native eligibility uses the captured platform minimum, a current feature probe,
49
+ and an exact per-session handshake. It is experimental and requires recipient
50
+ opt-in. Codex replies through `acc reply`; its native `replyRoute` remains false.
40
51
 
41
52
  The limitations belong next to the adapters they affect:
42
53
 
43
54
  | Adapter | Exact limitation and evidence |
44
55
  |---|---|
45
- | Codex | 0.147.0 next-turn stdout arrives as unwrapped developer-role context and requires plugin trust. A 0.152.1 capture proved the App Server queue transport, but the release capture then measured that native delivery there requires `codex --remote unix://`, and in that mode the session runs inside the daemon: both the hook payload and the App Server's own thread record report the daemon's directory instead of the session's, so ACC cannot tell which workspace the session is in. Nothing ACC can reach carries the real one, and placing a session in the wrong workspace is worse than not placing it, so `delivery.livePush` is **not** claimed - the probe and the handshake both refuse with `workspace_identity_unavailable`. `replyRoute` stays false. |
56
+ | Codex | Exact 0.147.0 next-turn context 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. |
46
57
  | Claude Code | 2.1.233 next-turn delivery waits for the next user prompt. A 2.1.258 Channel capture proved idle offer, busy queue-after-turn, explicit reply, duplicate suppression, and durable fallback, so `delivery.livePush` and `delivery.replyRoute` are live capabilities behind the native contract (experimental, off until opted in; Claude's development-channel warning is vendor-owned and visible). |
47
58
  | Gemini CLI | Only 0.57.0 has package-shipped next-turn certification. 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. |
48
59
  | Grok | 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. |
49
60
  | Kimi Code | 0.36.1 has next-turn and guard evidence, 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. |
50
- | Generic MCP | Tool polling is not next-turn injection, live push, or a native reply route. It has no write guard or client-lifecycle evidence. |
61
+ | Generic MCP | As a receiver, tool polling is not next-turn injection, live push, or a native reply route. It has no write guard or client-lifecycle evidence. Outgoing messages may use an eligible recipient's opted-in native adapter. |
51
62
 
52
63
  `certification.json` beside each adapter is machine-readable. `COMPATIBILITY.md` records the
53
64
  captured client behavior, including what could not be observed.
@@ -59,23 +70,28 @@ would publish a generation-bound binding with `availableModes`, `clientVersion`,
59
70
  `livePolicy`, and `leaseUntil`. `acc status --json` reports these as `deliveryBindings`
60
71
  with a computed `reachable` boolean while keeping the opaque endpoint private.
61
72
 
62
- The router requires exactly one current eligible generation. No binding, an expired lease,
63
- several live sessions for one participant, a busy target, or a version that does not match
64
- passing evidence all stay on durable fallback.
65
-
66
- The lease is extended by whoever serves the endpoint, because only that process knows it is
67
- still alive. A client that publishes no heartbeat - Claude Code among them - would otherwise
68
- let the lease run out under an idle session, which is exactly when live delivery is worth
69
- having. Giving a binding up is a separate, final fact rather than an expired lease, so a
70
- channel that has not yet noticed cannot extend something the session already retired.
71
-
72
- Current shipped reality: Claude Code on darwin-arm64 has a passing experimental `livePush`
73
- capture behind the native delivery contract, off until a per-client opt-in. Codex is
74
- next-turn and inbox only: its queue transport works, but the mode that makes a session
75
- reachable is the mode that hides which workspace it is in. Every other client is next-turn or
76
- inbox only;
77
- Gemini CLI and Kimi Code are next-turn only at their exact captured versions; Grok and MCP
78
- poll inbox.
73
+ The router requires exactly one current eligible generation. No binding, several live
74
+ sessions for one participant, and an adapter refusal keep delivery on the durable
75
+ fallback. An expired lease may be revalidated by an adapter's optional refresh method;
76
+ failure leaves the message queued. Busy behavior belongs to the transport: Claude's Channel can accept a
77
+ message while the target is busy and queue it until the current turn finishes; an adapter
78
+ that instead refuses with `recipient_busy` leaves the message on the durable path.
79
+
80
+ An endpoint may renew its lease, or an adapter may revalidate that exact endpoint on
81
+ demand without a client heartbeat. Retirement is final: neither route may revive a
82
+ retired binding, a closed session or an old generation. Delivery lease refresh does
83
+ not renew participant presence.
84
+
85
+ On darwin-arm64, Codex LocalDaemon and Claude Code Channel have captured
86
+ experimental live delivery. Other adapters retain their separately certified
87
+ next-turn paths or inbox polling. Linux and other uncaptured platforms retain
88
+ the durable inbox; these captures establish no new hook capabilities there.
89
+
90
+ Codex's 120-second delivery lease can refresh on demand for the same live endpoint;
91
+ it does not extend the 24-hour participant presence limit. A TUI exit can leave its
92
+ daemon thread loaded and able to execute opted-in messages. Actual thread teardown,
93
+ including explicit archive, runs `SessionEnd` and retires the binding. Turning ACC
94
+ delivery off prevents new offers even while that thread remains loaded.
79
95
 
80
96
  ## Recipient policy
81
97
 
@@ -88,10 +104,16 @@ policy:
88
104
  | `actionable` | questions, requests, answers, decisions, and addressed handoffs may use live push; notes wait for the next turn |
89
105
  | `all` | every addressed message kind may use live push |
90
106
 
91
- The default is `off`. `acc install --delivery actionable|all` is an explicit request, not
92
- a force switch. The installer applies it only when the detected exact client has certified
93
- live push; otherwise effective policy remains off and the fallback diagnostic is printed.
94
- Room messages are never live-push candidates.
107
+ The default is `off`. `acc install --delivery actionable|all` records recipient consent.
108
+ Current activation remains off when the platform, captured minimum or feature probe
109
+ does not qualify; that temporary failure does not erase the requested policy. Each
110
+ session must pass its own generation-bound handshake. An adapter using the installation
111
+ record rereads consent at hooks and before offers, so a long-running vendor process
112
+ cannot retain permission through an old environment value or binding snapshot.
113
+
114
+ For an adapter using recorded consent, `--delivery off` and uninstall prevent new
115
+ native offers. They cannot withdraw a submission already accepted by the vendor queue. Ordinary hook capabilities still
116
+ require exact-version certification. Room messages are never live-push candidates.
95
117
 
96
118
  ## Fallback
97
119