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.
- package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/.claude-plugin/plugin.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/src/install.mjs +65 -21
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/extension/gemini-extension.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-kimi/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-kimi/plugin/.kimi-plugin/plugin.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
- package/node_modules/@agents-can-communicate/cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/cli/src/runtime-paths.mjs +15 -2
- package/node_modules/@agents-can-communicate/core/package.json +1 -1
- package/node_modules/@agents-can-communicate/hook-runner/package.json +1 -1
- package/node_modules/@agents-can-communicate/installer/package.json +1 -1
- package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
- package/node_modules/@agents-can-communicate/protocol/package.json +1 -1
- package/node_modules/@agents-can-communicate/storage-filesystem/package.json +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agents-can-communicate",
|
|
3
|
-
"version": "0.1.
|
|
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
|
|
15
|
-
//
|
|
16
|
-
//
|
|
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
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
160
|
-
//
|
|
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,
|
|
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(
|
|
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))
|
|
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:
|
|
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.
|
|
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": {
|
|
@@ -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,
|
|
30
|
-
|
|
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],
|