agents-can-communicate 0.1.2 → 0.1.4

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 (23) hide show
  1. package/README.md +121 -152
  2. package/bin/acc-mcp.mjs +13 -1
  3. package/docs/CAPABILITIES.md +5 -0
  4. package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +1 -1
  5. package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/.claude-plugin/plugin.json +1 -1
  6. package/node_modules/@agents-can-communicate/adapter-codex/package.json +1 -1
  7. package/node_modules/@agents-can-communicate/adapter-codex/plugin/.codex-plugin/plugin.json +1 -1
  8. package/node_modules/@agents-can-communicate/adapter-codex/src/install.mjs +65 -21
  9. package/node_modules/@agents-can-communicate/adapter-gemini-cli/extension/gemini-extension.json +1 -1
  10. package/node_modules/@agents-can-communicate/adapter-gemini-cli/package.json +1 -1
  11. package/node_modules/@agents-can-communicate/adapter-kimi/package.json +1 -1
  12. package/node_modules/@agents-can-communicate/adapter-kimi/plugin/.kimi-plugin/plugin.json +1 -1
  13. package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
  14. package/node_modules/@agents-can-communicate/cli/package.json +1 -1
  15. package/node_modules/@agents-can-communicate/cli/src/help.mjs +1 -1
  16. package/node_modules/@agents-can-communicate/cli/src/runtime-paths.mjs +15 -2
  17. package/node_modules/@agents-can-communicate/core/package.json +1 -1
  18. package/node_modules/@agents-can-communicate/hook-runner/package.json +1 -1
  19. package/node_modules/@agents-can-communicate/installer/package.json +1 -1
  20. package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
  21. package/node_modules/@agents-can-communicate/protocol/package.json +1 -1
  22. package/node_modules/@agents-can-communicate/storage-filesystem/package.json +1 -1
  23. package/package.json +4 -2
package/README.md CHANGED
@@ -4,197 +4,166 @@
4
4
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
5
  [![Node](https://img.shields.io/badge/node-%E2%89%A524-brightgreen.svg)](https://nodejs.org)
6
6
 
7
- **AI agents that can ask each other for work.**
7
+ **Give every agent session a shared room for coordination. Keep your attention on the
8
+ work.**
8
9
 
9
- Codex on one branch, Claude Code on another, Kimi reviewing. Each in its own git worktree,
10
- each knowing what the others are doing and able to hand a piece over instead of waiting
11
- for you to carry the message.
10
+ ACC is a local-first coordination layer for the AI agent sessions you already opened. It
11
+ gives them shared presence, intent, claims, messages, and work requests while every
12
+ session keeps its own authority.
12
13
 
13
- You run the installer once. After that you talk to your agents the way you already do.
14
+ Coordination runs locally on your machine. Raw transcripts stay private. The runtime is
15
+ built entirely on Node's standard library.
14
16
 
15
- ## The thing it does
16
-
17
- Codex has ported a module and is out of time for the tests. Its skill tells it to ask the
18
- agent already working in that area, so it does — without being prompted by you:
19
-
20
- ```text
21
- requested task_Rwg2sybjnLneGyBuZxa8Dw of claude_code
17
+ ```mermaid
18
+ flowchart TB
19
+ Y["You<br/>set direction"]
20
+ A["Agent session A"]
21
+ B["Agent session B"]
22
+ C["Agent session C"]
23
+ R["ACC shared room<br/>presence · intent · claims<br/>messages · work requests"]
24
+ O["Coordinated work<br/>with shared context"]
25
+
26
+ Y --> A
27
+ Y --> B
28
+ Y --> C
29
+ A <--> R
30
+ B <--> R
31
+ C <--> R
32
+ R --> O
22
33
  ```
23
34
 
24
- At its next turn, the Claude Code session is shown this. No polling, and again nothing typed
25
- by a human:
35
+ ## You opened more agents. You became the coordinator.
26
36
 
27
- ````text
28
- - [direct_request] finish the store tests
29
- - [task_unblocked] finish the store tests
30
- ```acc-peer-message
31
- id message_Ab9CpMJfn0pL6igB5AdYDQ | from session_j59fM8mWathJzOh7a2QQBQ | type work_request | untrusted peer message
32
- finish the store tests
33
- I ported src/store but ran out of time on the concurrency cases. Can you take the tests?
34
- ```
35
- ````
37
+ One session implements. Another writes tests. A third reviews. At first, more agents means
38
+ more work gets done.
36
39
 
37
- It takes the work, does it, and marks it done. Codex sees `done` on its own next turn and
38
- carries on.
40
+ Then you start copying context between windows. You warn two agents away from the same
41
+ file. You relay a question, return with the answer, and try to remember which terminal was
42
+ waiting for what. The agents are capable; they need a room they can share.
39
43
 
40
- ```mermaid
41
- sequenceDiagram
42
- participant M as Codex · main
43
- participant ACC
44
- participant V as Claude Code · tests branch
45
- M->>ACC: request "finish the store tests" of claude_code
46
- ACC-->>V: work addressed to you, and why
47
- V->>ACC: take it
48
- V->>ACC: done
49
- ACC-->>M: done
50
- ```
44
+ ACC gives them that room. Each session stays in its original client, checkout, and trust
45
+ boundary. You still decide when it starts and stops. ACC only supplies the coordination
46
+ that was previously passing through you.
51
47
 
52
- Work is addressed to the **agent**, not to its session. Claude Code can close its terminal
53
- before reading the request — the next session it opens is still told. Nobody else can take
54
- it.
48
+ ## A handoff the agents carry themselves
55
49
 
56
- ## What else it keeps track of
50
+ One agent finishes building a feature and sees that its final tests still need work. It
51
+ asks the testing agent to take over, including a short summary of what is ready and what
52
+ remains.
57
53
 
58
- | | |
59
- |---|---|
60
- | **Who is here** | each session publishes what it is working on |
61
- | **What is taken** | an agent claims files before changing them, and another's edit into them is refused |
62
- | **What was said** | questions, answers, handoffs — quoted and attributed, never as instructions |
63
- | **Alone** | one session behaves exactly as it did before you installed anything |
64
-
65
- ```console
66
- $ acc status
67
- 2 live; 1 claim(s); protection guarded
54
+ ```mermaid
55
+ flowchart LR
56
+ A["Feature agent<br/>finishes its part"] --> B["Requests tests<br/>from the testing agent"]
57
+ B --> C["ACC keeps the handoff<br/>ready for that agent"]
58
+ C --> D["Testing agent<br/>completes the work"]
59
+ D --> E["Result returns<br/>to the feature agent"]
68
60
  ```
69
61
 
62
+ The request stays with the testing agent across terminal restarts. When that agent returns,
63
+ it receives the handoff, completes the tests, and sends the result back. You choose the
64
+ direction and review the outcome; the agents carry the context between them.
65
+
70
66
  ## Install
71
67
 
68
+ Run these commands in a terminal on each macOS or Linux machine where your agent clients
69
+ run:
70
+
72
71
  ```bash
73
72
  npm install -g agents-can-communicate
73
+ acc install
74
74
  ```
75
75
 
76
- Then wire up the clients you have:
76
+ The first command makes `acc` available across the machine. The second finds Codex,
77
+ Claude Code, Gemini CLI, and Kimi Code installations and activates the integrations that
78
+ are available. Codex completes activation after you trust the plugin; `acc doctor` shows
79
+ the current state.
77
80
 
78
- ```bash
79
- acc install
80
- ```
81
+ `acc install` names every client setting it activated and how to undo it.
81
82
 
82
- It names every file it wrote, in your own home-relative paths, and how to undo it. Open your
83
- clients in the project afterwards in one directory or in several worktrees and work
84
- normally.
83
+ Open or restart your agent client inside a project. Each new session joins that project's
84
+ room automatically. Open another session in the same project and the two can coordinate;
85
+ run `acc status` from the project directory whenever you want to see the room yourself.
85
86
 
86
- If you would rather look before it writes, `acc install --dry-run` prints the same list and
87
- changes nothing. `acc uninstall` takes it all back out.
87
+ ACC stores coordination data in the standard application-data location for your system.
88
+ The defaults are `~/Library/Application Support/acc` on macOS and `~/.local/share/acc` on
89
+ Linux. `XDG_DATA_HOME` relocates the Linux default; `ACC_DATA_HOME` overrides either
90
+ platform, as described in [configuration](docs/CONFIGURATION.md). Project files stay
91
+ unchanged. Git worktrees from one repository share a room, and plain folders receive the
92
+ same coordination experience.
88
93
 
89
- <!-- test:command -->
90
- ```bash
91
- acc install --dry-run
92
- ```
94
+ Keep ACC current with `acc update --apply`. It installs the latest release and refreshes
95
+ the client integrations together. `acc doctor` points to that action when their versions
96
+ drift.
93
97
 
94
- ## Keeping it current
98
+ Run `acc uninstall` to remove ACC's client integrations. Settings you changed remain
99
+ yours.
95
100
 
96
- ```bash
97
- acc update # asks npm; --apply installs it and re-wires the clients
98
- ```
101
+ ## What changes after installation
99
102
 
100
- An upgrade is two steps, because it lands in two places. `npm install -g` replaces the CLI
101
- and the hook runtime a client runs the runtime out of the npm directory rather than a copy
102
- — and leaves the bundle written into that client alone, including the skills the agents
103
- read. `acc install` refreshes it, and `acc doctor` says so when the two disagree:
103
+ **Agents know who is around.** Each session can see the other participants, their current
104
+ focus, and the files they have claimed.
104
105
 
105
- ```console
106
- $ acc doctor
107
- store healthy; 2 live session(s); protection guarded; 3 of 4 adapter(s) installed
108
- acc install --adapter claude_code # plugin is 0.1.1, acc is 0.2.0
109
- ```
106
+ **Parallel work becomes deliberate.** Agents claim shared files before editing. Supported
107
+ client edits respect those claims and identify the participant already working there.
110
108
 
111
- `acc update` is the only command that reaches the network. `acc doctor` reads what it
112
- remembered and asks at most once a day; nothing on the hook path ever asks, since a hook
113
- runs on every turn inside a five-second budget. `ACC_NO_UPDATE_CHECK=1` turns both off.
109
+ **Questions and work find their way back.** Requests, decisions, and handoffs stay with
110
+ the intended agent across session restarts, and results return to the agent that asked.
114
111
 
115
- ## Commands
112
+ **Human authority stays clear.** Peer messages arrive with attribution and remain peer
113
+ context. Your instructions and approved policy continue to set the boundaries.
116
114
 
117
- Coordination needs none from you. Requesting work, taking it, claiming files and messaging
118
- are things the agents do, taught by the skill each adapter installs.
115
+ **Solo work stays quiet.** A single session receives the familiar client experience.
116
+ Shared context appears when another participant or pending handoff makes it useful.
119
117
 
120
- What is left for a person is the install and looking in on it:
118
+ ## Fits the workflow you already have
121
119
 
122
- | | |
123
- |---|---|
124
- | `acc help` | every command, one line each |
125
- | `acc status` | who is here, what is claimed, what is in flight |
126
- | `acc doctor` | what is installed, what is missing, what to do next |
127
- | `acc install` · `acc uninstall` | wire clients up, or take it back out |
128
-
129
- Uninstall removes only files ACC wrote, and only where they still match what it wrote.
130
- Every operation is in the [CLI reference](docs/CLI.md) if you want to drive it yourself.
131
-
132
- ## Supported clients
133
-
134
- | | Sees others | Blocks edits | Receives work and updates |
135
- |---|---|---|---|
136
- | Codex | yes | yes¹ | yes |
137
- | Claude Code | yes | yes | yes |
138
- | Gemini CLI | yes | yes² | yes |
139
- | Kimi Code | yes | yes | yes |
140
- | Any MCP client | yes | – | yes, when it polls |
141
-
142
- ¹ models editing through `apply_patch` · ² approval modes that expose edit tools ·
143
- [what was measured](docs/CAPABILITIES.md)
144
-
145
- ## Limits
146
-
147
- - A claim blocks file edits. It does not block an agent that edits by running a shell
148
- command, since the command names no file.
149
- - `protection guarded` applies while every session present is one ACC can stop. One that
150
- cannot changes it to `advisory`.
151
- - Codex requires you to trust the plugin before its hooks run. `acc doctor` reports this.
152
- - Nothing is pruned yet. A workspace that has carried thousands of messages makes each turn
153
- slower to build; a project's worth of coordination is fine, an archive is not.
154
- - Windows does not work: the store fsyncs a directory after a rename, which Windows
155
- refuses, and `O_NOFOLLOW` does not hold there. Last measured at 86 failures out of 587
156
- tests; the suite has grown a good deal since and nobody has run it there again. macOS and
157
- Linux are supported and both run in CI.
158
-
159
- ## How it works
120
+ Your agent client remains the place where sessions start, permissions are granted, and
121
+ work happens. ACC joins at natural moments, shares the relevant context, and returns
122
+ control to the client. Forward progress stays the priority during any coordination delay.
160
123
 
161
- ```mermaid
162
- graph LR
163
- C[your client] -->|hook| H[acc-hook]
164
- H --> K["core<br/>sessions · work · claims · messages"]
165
- K --> S[(state, outside your repo)]
166
- K -->|answer| H
167
- H --> C
168
- ```
124
+ ACC currently connects directly to Codex, Claude Code, Gemini CLI, and Kimi Code. Other
125
+ clients that support MCP can join the same room, see its activity, and exchange work when
126
+ they sync.
169
127
 
170
- Clients call out when a session starts and ends, when a turn begins, and before a tool
171
- runs plus a heartbeat, on the one client that sends them. ACC answers at those and is
172
- idle otherwise. A hook that does not answer within five seconds lets the tool run, so ACC
173
- can be slow or broken without stopping anyone's work.
128
+ When a client exposes supported file edits, ACC can protect a claimed file before another
129
+ agent changes it. Shell commands and separate local applications rely on visible claims
130
+ instead. `acc status` explains the protection available in the current room.
174
131
 
175
- State lives beside your other tool settings, never inside the repository:
132
+ Current support focuses on multiple sessions working in one project on one machine, on
133
+ macOS or Linux. Each client retains its session lifecycle and full conversation history.
134
+ The [capability evidence](docs/CAPABILITIES.md) records exactly what each integration has
135
+ demonstrated in a real client.
176
136
 
177
- ```text
178
- ~/Library/Application Support/acc macOS
179
- ~/.local/share/acc Linux
180
- ```
137
+ ACC currently retains every coordination record. It is sized for an active project's
138
+ history; thousands of messages make each turn slower to assemble, so use your project
139
+ documentation for long-term archives.
140
+
141
+ ## Everyday controls
142
+
143
+ The installed guidance teaches agents how to claim files, ask questions, request work,
144
+ and complete handoffs. These commands give you a direct view and control when you want it:
145
+
146
+ | Command | What it is for |
147
+ |---|---|
148
+ | `acc status` | See active sessions, claimed work, and the room's protection level |
149
+ | `acc doctor` | Confirm which client integrations are active |
150
+ | `acc update --apply` | Install the latest release and refresh integrations |
151
+ | `acc uninstall` | Remove ACC's client integrations safely |
181
152
 
182
- Inside it, one directory per workspace — keyed by the repository rather than the folder, so
183
- every worktree of it is one workspace and two unrelated projects share nothing. Deleting a
184
- workspace directory loses that project's coordination history and nothing else.
153
+ Every operation is documented in the [CLI reference](docs/CLI.md).
185
154
 
186
- ## Documentation
155
+ ## Keep exploring
187
156
 
188
- | Using it | Understanding it | Building on it |
189
- |---|---|---|
190
- | [Getting started](docs/GETTING_STARTED.md) | [Concepts](docs/CONCEPTS.md) | [Writing an adapter](docs/ADAPTER_AUTHORING.md) |
191
- | [CLI](docs/CLI.md) | [Architecture](docs/ARCHITECTURE.md) | [Protocol](docs/PROTOCOL.md) |
192
- | [Configuration](docs/CONFIGURATION.md) | [Capabilities](docs/CAPABILITIES.md) | [Security](docs/SECURITY_MODEL.md) |
193
- | [MCP](docs/MCP.md) | [Decisions](docs/DESIGN_DECISIONS.md) | [Threat model](docs/THREAT_MODEL.md) |
194
- | [Troubleshooting](docs/TROUBLESHOOTING.md) | [Prior art](docs/PRIOR_ART.md) | [Contributing](AGENTS.md) |
157
+ - **Start using ACC:** [getting started](docs/GETTING_STARTED.md) ·
158
+ [configuration](docs/CONFIGURATION.md) · [troubleshooting](docs/TROUBLESHOOTING.md)
159
+ - **Understand the promise:** [why ACC](docs/WHY_ACC.md) · [concepts](docs/CONCEPTS.md) ·
160
+ [capabilities](docs/CAPABILITIES.md) · [security](docs/SECURITY_MODEL.md)
161
+ - **Build on ACC:** [MCP](docs/MCP.md) · [writing an adapter](docs/ADAPTER_AUTHORING.md) ·
162
+ [protocol](docs/PROTOCOL.md)
195
163
 
196
- Examples: [three workstreams](examples/three-workstreams.md) ·
197
- [research without Git](examples/non-git-research.md)
164
+ See it in action: [three workstreams](examples/three-workstreams.md) ·
165
+ [research in a plain directory](examples/non-git-research.md). Contributions start with
166
+ [Repository Guidelines](AGENTS.md).
198
167
 
199
168
  ## Requirements
200
169
 
@@ -202,4 +171,4 @@ Node 24+, macOS or Linux. Git optional.
202
171
 
203
172
  ## License
204
173
 
205
- MIT — see [LICENSE](LICENSE).
174
+ Free and MIT-licensed. Use it, fork it, keep it — see [LICENSE](LICENSE).
package/bin/acc-mcp.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { randomBytes } from "node:crypto";
3
3
 
4
- import { createId } from "@agents-can-communicate/protocol";
4
+ import { EXIT, createId } from "@agents-can-communicate/protocol";
5
5
  import { createCoordinationService } from "@agents-can-communicate/core";
6
6
  import { openFilesystemStore } from "@agents-can-communicate/storage-filesystem";
7
7
  import { createGitProbe, discoverWorkspace, platformDataHome, runtimePaths }
@@ -12,6 +12,18 @@ import { serve } from "@agents-can-communicate/mcp-server";
12
12
  // randomness. The participant name comes from configuration, never from the
13
13
  // client: the protocol says clientInfo is self-reported and must not drive
14
14
  // behaviour, and the session is derived from this configuration alone.
15
+ // Nothing is read from the command line, so nothing may be passed on it. It
16
+ // used to accept and ignore anything: writing `acc-mcp --cwd <project>` - the
17
+ // habit `acc` teaches - started a server rooted wherever the client happened to
18
+ // launch it, alone in a workspace nobody else was in, with no warning at all.
19
+ if (process.argv.length > 2) {
20
+ process.stderr.write("acc-mcp takes no arguments. It is configured by environment:\n"
21
+ + " ACC_MCP_PARTICIPANT who this server takes part as (default: mcp)\n"
22
+ + " ACC_MCP_WORKSPACE the project it joins (default: the working directory)\n"
23
+ + `refusing: ${process.argv.slice(2).join(" ")}\n`);
24
+ process.exit(EXIT.USAGE);
25
+ }
26
+
15
27
  const participantId = process.env.ACC_MCP_PARTICIPANT ?? "mcp";
16
28
  const clock = { now: () => new Date().toISOString() };
17
29
  const ids = { next: kind => createId(kind, randomBytes) };
@@ -1,5 +1,10 @@
1
1
  # Capabilities
2
2
 
3
+ Capability honesty is part of the product, not an implementation footnote. ACC coordinates
4
+ sessions it does not own, so the workspace can promise only what every session actually
5
+ exposes. One weaker participant lowers the reported protection level instead of inheriting
6
+ a stronger label from its peers.
7
+
3
8
  What each harness was **observed** doing, on the versions named here. Nothing in this
4
9
  table is inferred from documentation: every `yes` has a fixture captured from a real
5
10
  session, and every `no` means it was not seen, not that it is impossible.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/adapter-claude-code",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "agents-can-communicate",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Coordinate this Claude Code session with other AI agent sessions working in the same workspace: shared presence, resource claims, typed messages, and handoffs."
5
5
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/adapter-codex",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-can-communicate",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Coordinate this Codex session with other AI agent sessions working in the same workspace.",
5
5
  "license": "UNLICENSED",
6
6
  "keywords": ["coordination", "multi-agent", "claims", "handoff"],
@@ -1,4 +1,4 @@
1
- import { cp, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
1
+ import { cp, mkdir, readFile, rm, rmdir, stat, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
 
@@ -11,22 +11,35 @@ import { AccError, EXIT } from "@agents-can-communicate/protocol";
11
11
  const bundle = fileURLToPath(new URL("../plugin", import.meta.url));
12
12
  const PLUGIN_NAME = "agents-can-communicate";
13
13
 
14
- // The marketplace ACC owns. Registering a separate one rather than editing the
15
- // user's keeps the two apart: uninstall removes a marketplace ACC created and
16
- // never touches entries someone else put in theirs.
14
+ // The marketplace ACC owns, and its own root inside the agents home.
15
+ //
16
+ // Registering a separate one rather than joining the user's keeps the two
17
+ // apart. ACC used to write into `<home>/.agents/plugins/marketplace.json` -
18
+ // which is the marketplace this client discovers by itself, with no config
19
+ // entry at all, under whatever that manifest calls itself. So ACC merged its
20
+ // entry into someone else's marketplace and then enabled `…@acc-local`, an id
21
+ // this client never forms: `acc install` reported success and `codex plugin
22
+ // list` said `not installed`. Measured against Codex 0.147.0, then measured
23
+ // again to confirm a root of ACC's own is accepted and reported enabled.
17
24
  const MARKETPLACE = "acc-local";
18
25
  const QUALIFIED = `${PLUGIN_NAME}@${MARKETPLACE}`;
19
26
 
20
- // A marketplace is a directory whose manifest sits at
21
- // `<root>/.agents/plugins/marketplace.json`, and every `source.path` in that
22
- // manifest is relative to the manifest's own directory - `./plugins/<name>`,
23
- // as the client's own entries are written. Resolving the plugin from `root`
24
- // instead put the files two levels above where the manifest pointed, so the
25
- // entry named a directory that did not exist and the client loaded nothing.
26
- const marketplaceDir = root => path.join(root, ".agents", "plugins");
27
- const marketplacePath = root => path.join(marketplaceDir(root), "marketplace.json");
28
- const pluginPath = (root, name = PLUGIN_NAME) =>
29
- path.join(marketplaceDir(root), "plugins", name);
27
+ // A marketplace is a root holding `.agents/plugins/marketplace.json`, and every
28
+ // `source.path` in that manifest - `./plugins/<name>` - is resolved by this
29
+ // client against the *root*, not against the manifest's directory. Measured:
30
+ // `codex plugin list` prints the path it resolved, and for a plugin the user
31
+ // installed themselves it printed `<root>/plugins/x` from an entry spelled
32
+ // `./plugins/x`. The comment that used to be here said the opposite, and ACC
33
+ // wrote its tree two directories below where the client then looked.
34
+ //
35
+ // The root is under `.agents/` rather than the home itself: `<home>/plugins/`
36
+ // is where this client would put it, and nothing of ACC's belongs at the top of
37
+ // somebody's home.
38
+ const marketplaceRoot = agentsHome => path.join(agentsHome, ".agents", MARKETPLACE);
39
+ const marketplacePath = agentsHome =>
40
+ path.join(marketplaceRoot(agentsHome), ".agents", "plugins", "marketplace.json");
41
+ const pluginPath = (agentsHome, name = PLUGIN_NAME) =>
42
+ path.join(marketplaceRoot(agentsHome), "plugins", name);
30
43
  const configPath = codexHome => path.join(codexHome, "config.toml");
31
44
  // Where `codex plugin add` leaves the copy it actually runs. All three
32
45
  // components are ACC's own - the marketplace it created, the plugin name it
@@ -111,6 +124,11 @@ export async function installCodexPlugin({ home, agentsHome = home,
111
124
  // plugin tree is laid down that nothing will then be able to remove.
112
125
  const existing = await readJson(marketplacePath(agentsHome), { name: MARKETPLACE,
113
126
  interface: { displayName: "Agents Can Communicate" }, plugins: [] });
127
+ // The name in the manifest, which is the one this client forms plugin ids
128
+ // from. ACC used its own regardless, so on a machine that already had a
129
+ // marketplace at this root - discovered without any config entry, under
130
+ // whatever its manifest calls itself - the id ACC enabled was one the client
131
+ // never forms, and the plugin sat there listed and not installed.
114
132
  const before = await readFile(configPath(codexHome), "utf8").catch(() => "");
115
133
 
116
134
  const target = pluginPath(agentsHome);
@@ -142,7 +160,7 @@ export async function installCodexPlugin({ home, agentsHome = home,
142
160
  await writeTomlBlock(config, [
143
161
  `[marketplaces.${MARKETPLACE}]`,
144
162
  `source_type = "local"`,
145
- `source = ${tomlString(agentsHome)}`,
163
+ `source = ${tomlString(marketplaceRoot(agentsHome))}`,
146
164
  "",
147
165
  `[plugins.${tomlString(QUALIFIED)}]`,
148
166
  "enabled = true",
@@ -156,13 +174,26 @@ export async function installCodexPlugin({ home, agentsHome = home,
156
174
  await rm(cached, { recursive: true, force: true });
157
175
  await cp(target, cached, { recursive: true });
158
176
 
159
- // The cache *root* rather than the versioned directory inside it: that is
160
- // what ACC owns and what uninstall removes, and reporting the version would
177
+ // The plugin's own directory in the cache. Not the versioned one inside it,
178
+ // which goes stale the moment the version changes - and not the marketplace
179
+ // cache root above it, which belongs to whoever's marketplace this is: that
180
+ // root holds every plugin installed from it, and removing it took a plugin
181
+ // the user had installed themselves. Measured, on a real machine.
182
+ //
183
+ // The old comment here said the root was "what ACC owns", which was true only
184
+ // while ACC invented its own marketplace name and so had a root to itself.
161
185
  // make the record stale the moment the plugin version changes.
162
- return { ok: true, changes: [target, file, config, cacheRoot(codexHome)],
186
+ return { ok: true, changes: [target, file, config, cachePath(codexHome)],
163
187
  diagnostics: ["hooks require explicit trust in Codex before they run"] };
164
188
  }
165
189
 
190
+ /** Remove each directory that is empty, in the order given. */
191
+ async function removeEmptyDirs(directories) {
192
+ for (const directory of directories) {
193
+ await rmdir(directory).catch(() => {});
194
+ }
195
+ }
196
+
166
197
  export async function uninstallCodexPlugin({ home, agentsHome = home,
167
198
  codexHome = path.join(home, ".codex"), keep = [] }) {
168
199
  const file = marketplacePath(agentsHome);
@@ -190,8 +221,20 @@ export async function uninstallCodexPlugin({ home, agentsHome = home,
190
221
  return value?.name === MARKETPLACE && (value.plugins ?? []).length === 0;
191
222
  } });
192
223
 
193
- await removeInstalledTree(cacheRoot(codexHome), keep);
224
+ await removeInstalledTree(cachePath(codexHome), keep);
194
225
  await removeInstalledTree(pluginPath(agentsHome), keep);
226
+ // The directories ACC made to hold those, once nothing is in them. They are
227
+ // ACC's own - a marketplace root it created and the cache directory named
228
+ // after it - and an empty one left behind is litter in a home that did not
229
+ // have it. Anything the user put inside stops this: the directory is not
230
+ // empty, and it stays.
231
+ await removeEmptyDirs([
232
+ path.join(marketplaceRoot(agentsHome), "plugins"),
233
+ path.dirname(marketplacePath(agentsHome)),
234
+ path.dirname(path.dirname(marketplacePath(agentsHome))),
235
+ marketplaceRoot(agentsHome),
236
+ cacheRoot(codexHome),
237
+ ]);
195
238
  return { ok: true, changes, diagnostics: [] };
196
239
  }
197
240
 
@@ -202,7 +245,8 @@ export async function detectCodex({ home, agentsHome = home,
202
245
  const config = await readFile(configPath(codexHome), "utf8").catch(() => "");
203
246
  const registered = config.includes(`[marketplaces.${MARKETPLACE}]`);
204
247
  const enabled = config.includes(`[plugins."${QUALIFIED}"]`);
205
- const cached = await stat(cachePath(codexHome)).then(() => true).catch(() => false);
248
+ const cached = await stat(cachePath(codexHome))
249
+ .then(() => true).catch(() => false);
206
250
  return { ok: true, changes: [], diagnostics: [
207
251
  published ? "acc plugin published in the marketplace" : "acc plugin not registered",
208
252
  registered && enabled
@@ -228,7 +272,7 @@ export function planCodexInstall({ home, agentsHome = home,
228
272
  codexHome = path.join(home, ".codex") }) {
229
273
  return [
230
274
  { path: pluginPath(agentsHome), kind: "tree" },
231
- { path: cacheRoot(codexHome), kind: "tree" },
275
+ { path: cachePath(codexHome), kind: "tree" },
232
276
  { path: marketplacePath(agentsHome), kind: "merge" },
233
277
  { path: configPath(codexHome), kind: "merge" },
234
278
  ];
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-can-communicate",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Coordinate this Gemini CLI session with other AI agent sessions working in the same workspace.",
5
5
  "contextFileName": "skills/acc/SKILL.md"
6
6
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/adapter-gemini-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/adapter-kimi",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-can-communicate",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Coordinate this Kimi Code session with other AI agent sessions working in the same workspace: shared presence, resource claims, typed messages, and handoffs.",
5
5
  "skills": "./skills/",
6
6
  "sessionStart": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/adapter-sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -62,7 +62,7 @@ const DOCS = "https://github.com/automatis-tools/agents-can-communicate"
62
62
 
63
63
  export function helpText() {
64
64
  const width = Math.max(...Object.keys(COMMANDS).map(name => name.length)) + 4;
65
- const lines = ["acc - several agents in one workspace, none of them in charge", ""];
65
+ const lines = ["acc - coordinate the agent sessions you already opened", ""];
66
66
  for (const { heading, commands } of describeCommands()) {
67
67
  lines.push(heading);
68
68
  for (const { name, summary } of commands) {
@@ -23,11 +23,24 @@ export function runtimePaths({ dataHome, workspaceId, workspaceRoots = [] }) {
23
23
  // Enforced here rather than only asserted in a test, because "just put it in
24
24
  // .agents next to the project" is the exact regression this design exists to
25
25
  // prevent, and it would otherwise look like it works.
26
+ //
27
+ // The message names both paths and what to do, because the case a person
28
+ // actually meets is not the one this was written for. Running `acc` in a home
29
+ // directory makes that directory the workspace - it is no checkout, so
30
+ // discovery falls back to where you are - and the platform's own state
31
+ // directory is inside a home by definition. So `acc status` in `~` answered
32
+ // "runtime state must not live inside the workspace", which reads as a
33
+ // misconfiguration and tells the reader nothing they can act on.
26
34
  for (const workspaceRoot of workspaceRoots) {
27
35
  const relative = path.relative(workspaceRoot, root);
28
36
  if (relative === "" || (!path.isAbsolute(relative) && !relative.startsWith(".."))) {
29
- throw new AccError(EXIT.USAGE, "runtime state must not live inside the workspace",
30
- { root, workspaceRoot });
37
+ throw new AccError(EXIT.USAGE,
38
+ // The data home rather than the workspace's own directory inside it:
39
+ // that is the one a reader can move, and the one the remedy names.
40
+ `${workspaceRoot} holds ACC's own state at ${path.join(dataHome, "acc")}, `
41
+ + "so it cannot be a workspace. Run acc inside a project, or point "
42
+ + "ACC_DATA_HOME outside this directory.",
43
+ { root, dataHome, workspaceRoot });
31
44
  }
32
45
  }
33
46
  return Object.freeze(Object.fromEntries([["root", root],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/core",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/hook-runner",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/installer",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": { ".": "./src/index.mjs" },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/mcp-server",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/protocol",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/storage-filesystem",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "agents-can-communicate",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
- "description": "Model- and harness-agnostic coordination for independent AI agent sessions.",
5
+ "description": "Local-first coordination for independently opened AI agent sessions.",
6
6
  "keywords": [
7
7
  "agents",
8
8
  "coordination",
9
+ "local-first",
10
+ "agent-handoff",
9
11
  "mcp",
10
12
  "hooks",
11
13
  "codex",