agents-can-communicate 0.1.18 → 0.3.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.
- package/README.md +87 -69
- package/SECURITY.md +31 -0
- package/bin/acc-bootstrap.mjs +56 -0
- package/bin/acc-claude-channel.mjs +177 -0
- package/bin/acc-hook.mjs +94 -12
- package/bin/acc-mcp.mjs +6 -2
- package/bin/acc.mjs +13 -3
- package/docs/ADAPTER_AUTHORING.md +204 -0
- package/docs/ARCHITECTURE.md +131 -0
- package/docs/CAPABILITIES.md +117 -214
- package/docs/CLI.md +164 -0
- package/docs/CONCEPTS.md +134 -0
- package/docs/CONFIGURATION.md +147 -0
- package/docs/DESIGN_DECISIONS.md +89 -0
- package/docs/GETTING_STARTED.md +145 -0
- package/docs/GLOSSARY.md +26 -0
- package/docs/HOW_IT_WORKS.md +277 -0
- package/docs/MCP.md +94 -0
- package/docs/PROTOCOL.md +200 -0
- package/docs/RELEASING.md +115 -0
- package/docs/SECURITY_MODEL.md +131 -0
- package/docs/TROUBLESHOOTING.md +108 -0
- package/docs/WHY_ACC.md +61 -0
- package/docs/index.md +44 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/certification.json +228 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/PreToolUse-Edit.json +19 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/PreToolUse.json +17 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/SessionEnd.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/SessionStart.json +7 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/UserPromptSubmit.json +9 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/certification-provenance.json +269 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/delivery/claude-code-2.1.252.json +21 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/delivery/claude-code-2.1.258.json +23 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/delivery/claude-code-2.1.260.json +23 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +13 -2
- package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/.mcp.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/skills/acc/SKILL.md +22 -22
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/adapter.mjs +45 -5
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/channel.mjs +377 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/install.mjs +27 -7
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/native-delivery.mjs +229 -0
- package/node_modules/@agents-can-communicate/adapter-codex/certification.json +150 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/PreToolUse.json +14 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/SessionEnd.json +7 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/SessionStart.json +9 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/UserPromptSubmit.json +10 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/certification-provenance.json +199 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.0.json +21 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-remote-workspace.json +25 -0
- package/node_modules/@agents-can-communicate/adapter-codex/package.json +11 -2
- package/node_modules/@agents-can-communicate/adapter-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/plugin/skills/acc/SKILL.md +22 -22
- package/node_modules/@agents-can-communicate/adapter-codex/src/adapter.mjs +54 -12
- package/node_modules/@agents-can-communicate/adapter-codex/src/app-server-client.mjs +121 -0
- package/node_modules/@agents-can-communicate/adapter-codex/src/native-delivery.mjs +151 -0
- package/node_modules/@agents-can-communicate/adapter-codex/src/ws-json-rpc.mjs +192 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/certification.json +68 -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/extension/skills/acc/SKILL.md +22 -22
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/BeforeAgent-0.57.0.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/BeforeTool-0.57.0.json +12 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/BeforeTool-shell-0.57.0.json +12 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/SessionEnd-0.57.0.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/SessionStart-0.57.0.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/certification-provenance.json +293 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/package.json +8 -1
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/src/adapter.mjs +31 -13
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/src/install.mjs +4 -2
- package/node_modules/@agents-can-communicate/adapter-grok/certification.json +3 -0
- package/node_modules/@agents-can-communicate/adapter-grok/package.json +2 -1
- package/node_modules/@agents-can-communicate/adapter-grok/plugin/skills/acc/SKILL.md +22 -22
- package/node_modules/@agents-can-communicate/adapter-grok/src/adapter.mjs +11 -9
- package/node_modules/@agents-can-communicate/adapter-kimi/certification.json +52 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/PreToolUse-Bash.json +12 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/PreToolUse-Write.json +12 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/SessionHeartbeat.json +7 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/SessionStart.json +9 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/UserPromptSubmit.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/certification-provenance.json +66 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/package.json +8 -1
- package/node_modules/@agents-can-communicate/adapter-kimi/plugin/skills/acc/SKILL.md +22 -22
- package/node_modules/@agents-can-communicate/adapter-kimi/src/adapter.mjs +7 -3
- package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-sdk/src/capabilities.mjs +52 -18
- package/node_modules/@agents-can-communicate/adapter-sdk/src/certification.mjs +158 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/context-projector.mjs +36 -17
- package/node_modules/@agents-can-communicate/adapter-sdk/src/hook-shim.mjs +9 -1
- package/node_modules/@agents-can-communicate/adapter-sdk/src/index.mjs +7 -2
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-activation.mjs +76 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-delivery.mjs +202 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-vocabulary.mjs +101 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/session-binding.mjs +28 -4
- package/node_modules/@agents-can-communicate/cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/cli/src/args.mjs +12 -31
- package/node_modules/@agents-can-communicate/cli/src/doctor-command.mjs +70 -5
- package/node_modules/@agents-can-communicate/cli/src/help.mjs +2 -5
- package/node_modules/@agents-can-communicate/cli/src/install-command.mjs +111 -12
- package/node_modules/@agents-can-communicate/cli/src/main.mjs +100 -121
- package/node_modules/@agents-can-communicate/core/package.json +1 -1
- package/node_modules/@agents-can-communicate/core/src/attention.mjs +106 -0
- package/node_modules/@agents-can-communicate/core/src/conversations.mjs +276 -0
- package/node_modules/@agents-can-communicate/core/src/delivery-bindings.mjs +131 -0
- package/node_modules/@agents-can-communicate/core/src/finish-retries.mjs +97 -0
- package/node_modules/@agents-can-communicate/core/src/inbox.mjs +91 -107
- package/node_modules/@agents-can-communicate/core/src/index.mjs +3 -3
- package/node_modules/@agents-can-communicate/core/src/intents.mjs +0 -1
- package/node_modules/@agents-can-communicate/core/src/ports.mjs +2 -1
- package/node_modules/@agents-can-communicate/core/src/receipts.mjs +109 -0
- package/node_modules/@agents-can-communicate/core/src/service.mjs +21 -10
- package/node_modules/@agents-can-communicate/core/src/sessions.mjs +22 -20
- package/node_modules/@agents-can-communicate/core/src/status.mjs +11 -9
- package/node_modules/@agents-can-communicate/core/src/sync.mjs +3 -294
- package/node_modules/@agents-can-communicate/delivery-router/package.json +12 -0
- package/node_modules/@agents-can-communicate/delivery-router/src/index.mjs +1 -0
- package/node_modules/@agents-can-communicate/delivery-router/src/router.mjs +131 -0
- package/node_modules/@agents-can-communicate/hook-runner/package.json +4 -2
- package/node_modules/@agents-can-communicate/hook-runner/src/client-version.mjs +20 -0
- package/node_modules/@agents-can-communicate/hook-runner/src/native-binding.mjs +90 -0
- package/node_modules/@agents-can-communicate/hook-runner/src/runner.mjs +190 -105
- package/node_modules/@agents-can-communicate/installer/package.json +1 -1
- package/node_modules/@agents-can-communicate/installer/src/apply.mjs +70 -10
- package/node_modules/@agents-can-communicate/installer/src/bootstrap-runtime.mjs +144 -0
- package/node_modules/@agents-can-communicate/installer/src/detect.mjs +89 -5
- package/node_modules/@agents-can-communicate/installer/src/index.mjs +10 -2
- package/node_modules/@agents-can-communicate/installer/src/native-activation.mjs +161 -0
- package/node_modules/@agents-can-communicate/installer/src/ownership.mjs +112 -12
- package/node_modules/@agents-can-communicate/installer/src/plan.mjs +54 -2
- package/node_modules/@agents-can-communicate/installer/src/shell-bootstrap.mjs +210 -0
- package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
- package/node_modules/@agents-can-communicate/mcp-server/src/input-validator.mjs +79 -0
- package/node_modules/@agents-can-communicate/mcp-server/src/resources.mjs +23 -28
- package/node_modules/@agents-can-communicate/mcp-server/src/server.mjs +102 -72
- package/node_modules/@agents-can-communicate/mcp-server/src/tools.mjs +54 -97
- package/node_modules/@agents-can-communicate/protocol/package.json +1 -1
- package/node_modules/@agents-can-communicate/protocol/src/config.mjs +1 -1
- package/node_modules/@agents-can-communicate/protocol/src/conversations.mjs +64 -0
- package/node_modules/@agents-can-communicate/protocol/src/fields.mjs +17 -0
- package/node_modules/@agents-can-communicate/protocol/src/index.mjs +4 -1
- package/node_modules/@agents-can-communicate/protocol/src/schema.mjs +64 -90
- package/node_modules/@agents-can-communicate/protocol/src/states.mjs +13 -40
- package/node_modules/@agents-can-communicate/storage-filesystem/package.json +1 -1
- package/node_modules/@agents-can-communicate/storage-filesystem/src/active-journal.mjs +230 -0
- package/node_modules/@agents-can-communicate/storage-filesystem/src/atomic-json.mjs +77 -28
- package/node_modules/@agents-can-communicate/storage-filesystem/src/identity.mjs +1 -1
- package/node_modules/@agents-can-communicate/storage-filesystem/src/journal.mjs +83 -35
- package/node_modules/@agents-can-communicate/storage-filesystem/src/retention.mjs +112 -0
- package/node_modules/@agents-can-communicate/storage-filesystem/src/safe-file.mjs +18 -8
- package/node_modules/@agents-can-communicate/storage-filesystem/src/store.mjs +68 -26
- package/node_modules/@agents-can-communicate/storage-filesystem/src/writer-mutex.mjs +113 -35
- package/package.json +20 -1
- package/node_modules/@agents-can-communicate/core/src/communication.mjs +0 -334
- package/node_modules/@agents-can-communicate/core/src/message-signals.mjs +0 -41
- package/node_modules/@agents-can-communicate/core/src/notify.mjs +0 -95
- package/node_modules/@agents-can-communicate/core/src/tasks.mjs +0 -244
- package/node_modules/@agents-can-communicate/core/src/workstreams.mjs +0 -109
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Writing an adapter
|
|
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.
|
|
7
|
+
|
|
8
|
+
```mermaid
|
|
9
|
+
graph LR
|
|
10
|
+
H[client hook] --> R[acc-hook runtime]
|
|
11
|
+
R -->|normalizeHook| E[normalised event]
|
|
12
|
+
E --> CO[core: attach, claims, sync]
|
|
13
|
+
CO -->|denyOutcome / injectOutcome| R
|
|
14
|
+
R --> H
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## The manifest
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
export function createExampleAdapter() {
|
|
21
|
+
return defineAdapter({
|
|
22
|
+
id: "example", // portable id
|
|
23
|
+
displayName: "Example CLI",
|
|
24
|
+
client: { command: "example", certificationName: "example-cli",
|
|
25
|
+
versionArgs: ["--version"] },
|
|
26
|
+
certification, // imported package-local certification.json
|
|
27
|
+
capabilities: { delivery: { nextTurn: true } },
|
|
28
|
+
|
|
29
|
+
detect, install, uninstall, doctor,
|
|
30
|
+
planInstall, // what install would write
|
|
31
|
+
normalizeHook, // client payload -> normalised event
|
|
32
|
+
renderContext, // SyncResult -> text
|
|
33
|
+
renderContextResult, // text + ids of complete rendered groups
|
|
34
|
+
denyOutcome, injectOutcome, // how this client is answered
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`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
|
|
42
|
+
`text` for one, because peer text is untrusted and can imitate another message's header.
|
|
43
|
+
`projectContextResult()` implements this contract; `projectContext()` remains the text-only
|
|
44
|
+
convenience API for adapters that don't need it. An adapter with only the older
|
|
45
|
+
`renderContext()` gets pending bodies withheld and a visible `acc inbox` degradation warning
|
|
46
|
+
instead: repeating an untracked body every turn would be quieter in code and dishonest about
|
|
47
|
+
what was actually delivered.
|
|
48
|
+
|
|
49
|
+
`client.command` does double duty. `detect.mjs` uses it as the version-probe binary, and
|
|
50
|
+
presence liveness separately walks the hook's process ancestry for the first ancestor whose
|
|
51
|
+
executable basename matches it, to learn the client's own pid. Declare the binary the client
|
|
52
|
+
actually runs as — `command: "claude"` for a client that really runs as `node` resolves
|
|
53
|
+
nothing, and the failure is silent: the session gets `pid: null` and falls back to reading
|
|
54
|
+
presence by age alone, with nothing telling you why.
|
|
55
|
+
|
|
56
|
+
## Capabilities
|
|
57
|
+
|
|
58
|
+
Fourteen booleans in four groups, declared in the manifest's `capabilities` object:
|
|
59
|
+
|
|
60
|
+
| Group | Entries |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `lifecycle` | `sessionStart` `sessionResume` `sessionEnd` `heartbeat` `childSessions` |
|
|
63
|
+
| `context` | `startupInjection` `beforeTurnInjection` `safePointInjection` |
|
|
64
|
+
| `guards` | `beforeRead` `beforeWrite` `beforeShell` |
|
|
65
|
+
| `delivery` | `nextTurn` `livePush` `replyRoute` |
|
|
66
|
+
|
|
67
|
+
**False by default. `true` requires a backing method *and* an observed capture.**
|
|
68
|
+
`defineAdapter` enforces the method — declaring `guards.beforeWrite: true` without
|
|
69
|
+
`guardWrite()` is a usage error at construction. It also requires a passing entry in the
|
|
70
|
+
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).
|
|
72
|
+
|
|
73
|
+
`lifecycle.heartbeat` is deliberately not a flavour of `delivery.nextTurn`. Next-turn
|
|
74
|
+
delivery happens only when the client reaches a normal turn boundary; heartbeat fires on a
|
|
75
|
+
timer even while the session is idle.
|
|
76
|
+
|
|
77
|
+
### Certification evidence
|
|
78
|
+
|
|
79
|
+
Every adapter ships `certification.json` and every referenced capture under `fixtures/`.
|
|
80
|
+
Each evidence entry contains `client`, exact `version`, exact `platform`, `observedAt`,
|
|
81
|
+
`capability`, package-relative `fixture`, `idleBehavior`, `busyBehavior`,
|
|
82
|
+
`authorityLevel`, `limitations`, and `result` (`pass` or `fail`). A copied documentation
|
|
83
|
+
example is not a capture. Failed experiments stay in the manifest as `fail`; they explain
|
|
84
|
+
the false value and can never enable it.
|
|
85
|
+
|
|
86
|
+
`effectiveCapabilities(adapter, { clientVersion, platform })` returns the full boolean
|
|
87
|
+
shape for the installed client. Only an exact passing version/platform match remains true.
|
|
88
|
+
Unreadable, unknown, or mismatched clients degrade every uncertified row to false.
|
|
89
|
+
|
|
90
|
+
The backing methods for delivery are `renderContextResult()` for `nextTurn`,
|
|
91
|
+
`offerMessage()` for `livePush`, and `routeReply()` for `replyRoute`.
|
|
92
|
+
|
|
93
|
+
### Native delivery contract
|
|
94
|
+
|
|
95
|
+
An adapter that can push a message into a running session declares `nativeDelivery` next
|
|
96
|
+
to its capabilities:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
nativeDelivery: {
|
|
100
|
+
minimumByPlatform: { "darwin-arm64": "2.1.258" },
|
|
101
|
+
anchors: [{ platform: "darwin-arm64", version: "2.1.258",
|
|
102
|
+
protocolContract: "claude-code-channel-mcp-v1" }],
|
|
103
|
+
knownBad: [],
|
|
104
|
+
activationKinds: ["shell-bootstrap"],
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The rules `defineAdapter` enforces, and the ones the runtime applies:
|
|
109
|
+
|
|
110
|
+
- A minimum is the **first passing capture** on that platform, never a guessed first vendor
|
|
111
|
+
release. Every anchor must have passing `delivery.livePush` evidence for the same client,
|
|
112
|
+
version, and platform, and each minimum must itself be an anchor.
|
|
113
|
+
- There is intentionally **no maximum version**. A newer stable release is admitted only
|
|
114
|
+
when a current read-only feature probe (`probeNativeDelivery()`) and a per-session
|
|
115
|
+
handshake (`bindNativeSession()`) both report the anchored `protocolContract`;
|
|
116
|
+
`evaluateNativeEligibility()` and `validateNativeHandshake()` are those two checks.
|
|
117
|
+
- Prereleases require their own passing capture; they are `prerelease_not_captured` even
|
|
118
|
+
when numerically newer. `knownBad` names exact versions or inclusive intervals.
|
|
119
|
+
- Exact-version certification still governs every non-native capability;
|
|
120
|
+
`effectiveCapabilities()` is unchanged. The native rule is used for live delivery alone.
|
|
121
|
+
- The three native methods return closed facts (`validateNativeActivationPlan()` closes the
|
|
122
|
+
activation plan) and never put vendor data - endpoints, sockets, raw errors - into core.
|
|
123
|
+
|
|
124
|
+
## How far you can get
|
|
125
|
+
|
|
126
|
+
| Tier | You register | You get | You do not get |
|
|
127
|
+
|---|---|---|---|
|
|
128
|
+
| 0 | nothing — humans run `acc` | durable messages, status, claims | anything automatic |
|
|
129
|
+
| 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 | — |
|
|
132
|
+
|
|
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.
|
|
137
|
+
|
|
138
|
+
## normalizeHook
|
|
139
|
+
|
|
140
|
+
Whitelist, never a filter. Every client hands hooks the prompt, the transcript path, or the
|
|
141
|
+
tool output; none of it may survive.
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
return normalizedEvent({
|
|
145
|
+
kind, sessionId, cwd, model, parentSessionId, tool,
|
|
146
|
+
targets, // paths this call would WRITE. For a shell call, pass the command to
|
|
147
|
+
// shellWriteTargets() — it reads write positions only, never reads.
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Refuse an unrecognised payload. Inventing a session attaches the wrong one, or a new one
|
|
152
|
+
every hook, and looks like it is working.
|
|
153
|
+
|
|
154
|
+
## Response contracts do not port
|
|
155
|
+
|
|
156
|
+
Measure them. Every client differs, and a wrong shape fails **silently**:
|
|
157
|
+
|
|
158
|
+
| | deny | inject |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| Codex | exit 2 + stderr | plain stdout (`developer` message) |
|
|
161
|
+
| Claude Code | `hookSpecificOutput.permissionDecision` | same envelope |
|
|
162
|
+
| Gemini CLI | `{"decision":"block"}` | `hookSpecificOutput` envelope |
|
|
163
|
+
| Grok | `{"decision":"deny","reason"}` (documented; deny not yet captured) | UserPromptSubmit stdout discarded on 1.0.13 |
|
|
164
|
+
| Kimi Code | `hookSpecificOutput.permissionDecision` | plain stdout |
|
|
165
|
+
|
|
166
|
+
`denyOutcome(reason)` returns `{ stdout, stderr, exitCode }`, so the runtime never has to
|
|
167
|
+
know which client it is talking to. This table is only the shape each shipped adapter
|
|
168
|
+
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).
|
|
171
|
+
|
|
172
|
+
## Install and ownership
|
|
173
|
+
|
|
174
|
+
```mermaid
|
|
175
|
+
graph TB
|
|
176
|
+
P[planInstall] -->|artifacts| K{kind}
|
|
177
|
+
K -->|tree| T[a directory ACC creates<br/>removable if unchanged]
|
|
178
|
+
K -->|merge| M[a file the user owns<br/>never deleted]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Rules that are not negotiable:
|
|
182
|
+
|
|
183
|
+
- idempotent — installing twice equals installing once;
|
|
184
|
+
- reversible — uninstall restores the user's file byte for byte;
|
|
185
|
+
- absolute command paths — a hook's environment carries no PATH;
|
|
186
|
+
- honour `keep`: uninstall receives paths the user has since edited.
|
|
187
|
+
|
|
188
|
+
`planInstall` must use the same path helpers as `install`. A conformance test compares
|
|
189
|
+
them, because a plan that drifts makes `--dry-run` a decoration.
|
|
190
|
+
|
|
191
|
+
## Conformance
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
node --test tests/conformance/*.test.mjs
|
|
195
|
+
node --test tests/process/hook-wiring.test.mjs
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The second one *executes* what your install wrote. Three adapters once shipped a hook
|
|
199
|
+
command that did not exist anywhere; every test was green.
|
|
200
|
+
|
|
201
|
+
## Record what you learned
|
|
202
|
+
|
|
203
|
+
One `COMPATIBILITY.md` per adapter: client version, event names, payload fields, the deny
|
|
204
|
+
matrix, and what you could **not** observe. The next person's alternative is guessing.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Architecture
|
|
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.
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart LR
|
|
9
|
+
S["sender session"] -->|record| C["core"]
|
|
10
|
+
C --> D[("filesystem store<br/>outside repositories")]
|
|
11
|
+
C -->|message id| R["delivery router"]
|
|
12
|
+
R --> A["certified recipient adapter"]
|
|
13
|
+
A --> T["already-running recipient session"]
|
|
14
|
+
T -->|reply or acknowledge| C
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
ACC never starts, resumes, interrupts, supervises, or terminates the target client. A
|
|
18
|
+
transport that requires owning that lifecycle is outside the product boundary.
|
|
19
|
+
|
|
20
|
+
## Packages and dependency direction
|
|
21
|
+
|
|
22
|
+
| Package | Owns |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `protocol` | ids, closed schemas, semantic validation, receipt transitions, error and exit codes |
|
|
25
|
+
| `core` | sessions, intent, claims, conversations, inbox, receipts, attention, ephemeral delivery bindings |
|
|
26
|
+
| `storage-filesystem` | atomic transactions, event journal, materialised views, recovery, writer lock |
|
|
27
|
+
| `adapter-sdk` | capability validation, exact-version certification, context projection, hook and install helpers |
|
|
28
|
+
| `adapter-*` | vendor-specific hooks, responses, installation, evidence, and optional transport |
|
|
29
|
+
| `delivery-router` | recipient policy, one-generation selection, safe offer, then receipt commit |
|
|
30
|
+
| `hook-runner` | bounded adapter hook entry point |
|
|
31
|
+
| `mcp-server` | polling participation for clients without a native adapter |
|
|
32
|
+
| `cli` | universal local command boundary |
|
|
33
|
+
|
|
34
|
+
Core cannot branch on a vendor name or import an adapter, Git, or
|
|
35
|
+
`node:child_process`. Package-boundary tests enforce this rather than relying on
|
|
36
|
+
convention.
|
|
37
|
+
|
|
38
|
+
## Record, then offer
|
|
39
|
+
|
|
40
|
+
The order is part of the public guarantee:
|
|
41
|
+
|
|
42
|
+
1. validate and commit the message plus one `queued` receipt per resolved recipient;
|
|
43
|
+
2. resolve the recipient's current delivery bindings;
|
|
44
|
+
3. check recipient policy, current reachability, and exact client-version certification;
|
|
45
|
+
4. ask one adapter to cross its transport boundary;
|
|
46
|
+
5. only after acceptance, commit `offered` and an immutable success event.
|
|
47
|
+
|
|
48
|
+
A failed offer records the selected recipient session id and generation with a safe
|
|
49
|
+
diagnostic code, then leaves the receipt queued. Core validates that target provenance and
|
|
50
|
+
derives the event actor from it. A crash after bytes cross the boundary but before the
|
|
51
|
+
receipt commit may duplicate a later offer; ACC prefers that truthful underclaim to
|
|
52
|
+
claiming delivery before it happened.
|
|
53
|
+
|
|
54
|
+
## Delivery router is not a session manager
|
|
55
|
+
|
|
56
|
+
Messages address participants. The router may choose a live path only when exactly one
|
|
57
|
+
unexpired session generation is eligible. It does not open a session to make one eligible.
|
|
58
|
+
Zero candidates, several candidates, an unknown version, recipient policy `off`, or an
|
|
59
|
+
adapter refusal all produce durable fallback.
|
|
60
|
+
|
|
61
|
+
The binding is ephemeral and stores:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
sessionId · generation · adapterId · clientVersion · availableModes
|
|
65
|
+
livePolicy · opaqueEndpointRef · leaseUntil
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Core validates identity and expiry but never interprets the opaque endpoint. That remains
|
|
69
|
+
inside the owning adapter. `acc status` reports available modes, policy, reachability, and
|
|
70
|
+
lease without exposing the endpoint.
|
|
71
|
+
|
|
72
|
+
## Certified capability versus current reachability
|
|
73
|
+
|
|
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.
|
|
84
|
+
|
|
85
|
+
## Storage and workspace identity
|
|
86
|
+
|
|
87
|
+
The filesystem store is the durable source of truth. Every meaningful mutation updates
|
|
88
|
+
materialised records and appends an immutable event in one transaction. Sequence ids sort
|
|
89
|
+
lexically, so a cursor is simply the last sequence returned.
|
|
90
|
+
|
|
91
|
+
Workspace discovery checks an explicit config first, then the Git common directory, then
|
|
92
|
+
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.
|
|
94
|
+
|
|
95
|
+
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.
|
|
98
|
+
|
|
99
|
+
## Inbox, attention, and projection
|
|
100
|
+
|
|
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
|
|
103
|
+
same thread, and acknowledges the original atomically. No participant can advance another
|
|
104
|
+
participant's receipt.
|
|
105
|
+
|
|
106
|
+
Attention is computed from six explicit rules:
|
|
107
|
+
|
|
108
|
+
| Priority | Kind | Observable trigger |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| 1 | `reply_required` | this participant owns an unacknowledged receipt whose message requires a reply |
|
|
111
|
+
| 2 | `acknowledgement_required` | this participant owns an unacknowledged receipt whose message requires acknowledgement |
|
|
112
|
+
| 3 | `recipient_unavailable` | a required recipient of this participant's message has no online session |
|
|
113
|
+
| 4 | `claim_conflict` | this session's intent hint overlaps another live claim |
|
|
114
|
+
| 5 | `claim_contended` | a peer intent hint overlaps a live claim this session owns |
|
|
115
|
+
| 6 | `claim_expired` | a claim this session owned has reached its lease time |
|
|
116
|
+
|
|
117
|
+
Semantic relevance belongs to the receiving model; correctness never depends on a hidden
|
|
118
|
+
classifier.
|
|
119
|
+
|
|
120
|
+
Adapters project peer bodies in an attributed untrusted frame. If a complete body cannot
|
|
121
|
+
fit, the projection keeps the message id and points to `acc inbox --message <id>` instead
|
|
122
|
+
of silently truncating it.
|
|
123
|
+
|
|
124
|
+
## Hooks fail open
|
|
125
|
+
|
|
126
|
+
Hook execution is bounded. If coordination state cannot be read or a decision cannot be
|
|
127
|
+
made in time, the client action continues. ACC may warn or guard where a real hook allows
|
|
128
|
+
it, but a coordination tool must not be the reason a session stops working.
|
|
129
|
+
|
|
130
|
+
Next: [Protocol](PROTOCOL.md) · [Capabilities](CAPABILITIES.md) ·
|
|
131
|
+
[Security model](SECURITY_MODEL.md)
|