agents-can-communicate 0.1.2 → 0.1.3

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 (19) hide show
  1. package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +1 -1
  2. package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/.claude-plugin/plugin.json +1 -1
  3. package/node_modules/@agents-can-communicate/adapter-codex/package.json +1 -1
  4. package/node_modules/@agents-can-communicate/adapter-codex/plugin/.codex-plugin/plugin.json +1 -1
  5. package/node_modules/@agents-can-communicate/adapter-codex/src/install.mjs +65 -21
  6. package/node_modules/@agents-can-communicate/adapter-gemini-cli/extension/gemini-extension.json +1 -1
  7. package/node_modules/@agents-can-communicate/adapter-gemini-cli/package.json +1 -1
  8. package/node_modules/@agents-can-communicate/adapter-kimi/package.json +1 -1
  9. package/node_modules/@agents-can-communicate/adapter-kimi/plugin/.kimi-plugin/plugin.json +1 -1
  10. package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
  11. package/node_modules/@agents-can-communicate/cli/package.json +1 -1
  12. package/node_modules/@agents-can-communicate/cli/src/runtime-paths.mjs +15 -2
  13. package/node_modules/@agents-can-communicate/core/package.json +1 -1
  14. package/node_modules/@agents-can-communicate/hook-runner/package.json +1 -1
  15. package/node_modules/@agents-can-communicate/installer/package.json +1 -1
  16. package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
  17. package/node_modules/@agents-can-communicate/protocol/package.json +1 -1
  18. package/node_modules/@agents-can-communicate/storage-filesystem/package.json +1 -1
  19. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/adapter-claude-code",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
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.3",
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.3",
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.3",
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.3",
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.3",
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.3",
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.3",
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.3",
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.3",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -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.3",
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.3",
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.3",
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.3",
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.3",
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.3",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-can-communicate",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "Model- and harness-agnostic coordination for independent AI agent sessions.",
6
6
  "keywords": [