agents-can-communicate 0.1.5 → 0.1.6
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/bin/acc-hook.mjs +0 -0
- 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 +37 -2
- 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/doctor-command.mjs +4 -2
- package/node_modules/@agents-can-communicate/cli/src/install-command.mjs +8 -2
- package/node_modules/@agents-can-communicate/cli/src/main.mjs +14 -1
- 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
package/bin/acc-hook.mjs
CHANGED
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agents-can-communicate",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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"],
|
|
@@ -118,8 +118,37 @@ const entryFor = () => ({
|
|
|
118
118
|
* an install that writes files alone leaves a plugin the client never sees and
|
|
119
119
|
* a hook that never runs - while reporting success.
|
|
120
120
|
*/
|
|
121
|
+
/**
|
|
122
|
+
* The table that lets an agent here record anything at all.
|
|
123
|
+
*
|
|
124
|
+
* This client sandboxes the shell commands a model runs to the workspace, and
|
|
125
|
+
* ACC keeps its state outside every workspace on purpose - so an agent could
|
|
126
|
+
* read the roster and write nothing: `acc claim`, `acc work`, `acc message` all
|
|
127
|
+
* failed with `EPERM ... locks/writer.lock`. Measured with `codex exec`, which
|
|
128
|
+
* is how an agent actually runs, and confirmed by watching the same commands
|
|
129
|
+
* succeed once this root was declared.
|
|
130
|
+
*
|
|
131
|
+
* Left out when the user declares the table themselves. Declaring it twice is
|
|
132
|
+
* what makes this client refuse the whole config, and their setting is theirs -
|
|
133
|
+
* the diagnostic says what to add rather than adding it for them.
|
|
134
|
+
*/
|
|
135
|
+
const sandboxTable = (stateRoot, theirs) => {
|
|
136
|
+
if (typeof stateRoot !== "string" || stateRoot === "") return [];
|
|
137
|
+
if (theirs) return [];
|
|
138
|
+
return ["", "[sandbox_workspace_write]",
|
|
139
|
+
`writable_roots = [${tomlString(stateRoot)}]`];
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Every spelling TOML allows for the same table: the header, a sub-table
|
|
143
|
+
// header, a dotted key, and an inline table on one line. Missing one means ACC
|
|
144
|
+
// appends a second declaration - which is the duplicate this exists to avoid,
|
|
145
|
+
// and the client refuses the whole config over it.
|
|
146
|
+
const declaresSandbox = config =>
|
|
147
|
+
/^\s*\[sandbox_workspace_write[\].]/m.test(config)
|
|
148
|
+
|| /^\s*sandbox_workspace_write\s*[.=]/m.test(config);
|
|
149
|
+
|
|
121
150
|
export async function installCodexPlugin({ home, agentsHome = home,
|
|
122
|
-
codexHome = path.join(home, ".codex"), runner, node }) {
|
|
151
|
+
codexHome = path.join(home, ".codex"), stateRoot, runner, node }) {
|
|
123
152
|
// Read before writing, so a manifest that will not parse is found before a
|
|
124
153
|
// plugin tree is laid down that nothing will then be able to remove.
|
|
125
154
|
const existing = await readJson(marketplacePath(agentsHome), { name: MARKETPLACE,
|
|
@@ -157,6 +186,7 @@ export async function installCodexPlugin({ home, agentsHome = home,
|
|
|
157
186
|
`marketplace ${MARKETPLACE} is already registered in this config; `
|
|
158
187
|
+ "remove it and install again", { config });
|
|
159
188
|
}
|
|
189
|
+
const theirSandbox = declaresSandbox(stripBlock(before));
|
|
160
190
|
await writeTomlBlock(config, [
|
|
161
191
|
`[marketplaces.${MARKETPLACE}]`,
|
|
162
192
|
`source_type = "local"`,
|
|
@@ -164,6 +194,7 @@ export async function installCodexPlugin({ home, agentsHome = home,
|
|
|
164
194
|
"",
|
|
165
195
|
`[plugins.${tomlString(QUALIFIED)}]`,
|
|
166
196
|
"enabled = true",
|
|
197
|
+
...sandboxTable(stateRoot, theirSandbox),
|
|
167
198
|
]);
|
|
168
199
|
|
|
169
200
|
// The client runs the cached copy, so this has to happen after the shim and
|
|
@@ -184,7 +215,11 @@ export async function installCodexPlugin({ home, agentsHome = home,
|
|
|
184
215
|
// while ACC invented its own marketplace name and so had a root to itself.
|
|
185
216
|
// make the record stale the moment the plugin version changes.
|
|
186
217
|
return { ok: true, changes: [target, file, config, cachePath(codexHome)],
|
|
187
|
-
diagnostics: ["hooks require explicit trust in Codex before they run"
|
|
218
|
+
diagnostics: ["hooks require explicit trust in Codex before they run",
|
|
219
|
+
...(theirSandbox
|
|
220
|
+
? [`this config sets its own sandbox_workspace_write; add ${stateRoot} to `
|
|
221
|
+
+ "writable_roots, or an agent here can read the roster and record nothing"]
|
|
222
|
+
: [])] };
|
|
188
223
|
}
|
|
189
224
|
|
|
190
225
|
/** Remove each directory that is empty, in the order given. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agents-can-communicate",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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,11 +1,13 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
3
4
|
|
|
4
5
|
import { detectInstallation, loadOwnership, verifyOwned }
|
|
5
6
|
from "@agents-can-communicate/installer";
|
|
6
7
|
import { AccError, EXIT } from "@agents-can-communicate/protocol";
|
|
7
8
|
|
|
8
9
|
import { ALL_ADAPTERS, clientContext, probeTimeout } from "./install-command.mjs";
|
|
10
|
+
import { describePresence } from "./main.mjs";
|
|
9
11
|
import { platformPaths } from "./platform-paths.mjs";
|
|
10
12
|
import { noticeUpdate } from "./update-check.mjs";
|
|
11
13
|
import { diagnoseFilesystemStore, repairFilesystemStore }
|
|
@@ -37,9 +39,9 @@ async function diagnoseAdapters({ options, runtime }) {
|
|
|
37
39
|
// The same home `acc install --home` writes to, or the real one. Reading a
|
|
38
40
|
// different home than install wrote to reports every adapter as missing.
|
|
39
41
|
const home = options?.home ?? runtime?.env?.HOME ?? homedir();
|
|
40
|
-
const clients = clientContext(home);
|
|
41
42
|
const { data: dataHome } = platformPaths({ platform: runtime?.platform,
|
|
42
43
|
env: runtime?.env ?? {} });
|
|
44
|
+
const clients = clientContext(home, path.join(dataHome, "acc"));
|
|
43
45
|
const adapters = ALL_ADAPTERS();
|
|
44
46
|
const detected = await detectInstallation({ adapters, context: clients,
|
|
45
47
|
probeTimeoutMs: probeTimeout(runtime?.env) });
|
|
@@ -129,7 +131,7 @@ export async function runDoctor({ options, context, runtime }) {
|
|
|
129
131
|
// command documented as saying "what to run next" said it only to `--json`.
|
|
130
132
|
// A person running `acc doctor` on a client wired to an older plugin was told
|
|
131
133
|
// the store was healthy and nothing else.
|
|
132
|
-
const text = [`store healthy; ${status.counts
|
|
134
|
+
const text = [`store healthy; ${describePresence(status.counts)}; `
|
|
133
135
|
+ `protection ${status.protection}; ${installed} of ${adapters.length} adapter(s) installed`,
|
|
134
136
|
...data.remediation.map(line => ` ${line}`)].join("\n");
|
|
135
137
|
return { data, text };
|
|
@@ -17,12 +17,18 @@ import { platformPaths } from "./platform-paths.mjs";
|
|
|
17
17
|
// Each client keeps its own directory under the user's home, and an adapter
|
|
18
18
|
// pointed at the home itself writes beside them rather than inside them. That
|
|
19
19
|
// install reports success and the client never reads a byte of it.
|
|
20
|
-
export const clientContext = home => ({
|
|
20
|
+
export const clientContext = (home, stateRoot) => ({
|
|
21
21
|
home,
|
|
22
22
|
configDir: path.join(home, ".claude"),
|
|
23
23
|
agentsHome: home,
|
|
24
24
|
codexHome: path.join(home, ".codex"),
|
|
25
25
|
kimiHome: path.join(home, ".kimi-code"),
|
|
26
|
+
// Where ACC keeps its own state, for the client that has to be told. Codex
|
|
27
|
+
// sandboxes the commands a model runs to the workspace, and ACC's state is
|
|
28
|
+
// outside every workspace on purpose - so an agent there could read the
|
|
29
|
+
// roster and record nothing, every write failing with EPERM on the writer
|
|
30
|
+
// lock. Measured with `codex exec`, which is how an agent actually runs.
|
|
31
|
+
stateRoot,
|
|
26
32
|
});
|
|
27
33
|
|
|
28
34
|
export const ALL_ADAPTERS = () => [createClaudeCodeAdapter(), createCodexAdapter(),
|
|
@@ -153,9 +159,9 @@ export function actedOn(result) {
|
|
|
153
159
|
export async function runInstallCommand({ options, runtime, action = "install" }) {
|
|
154
160
|
const adapters = selectAdapters(options.adapter);
|
|
155
161
|
const home = options.home ?? runtime.env?.HOME ?? homedir();
|
|
156
|
-
const context = clientContext(home);
|
|
157
162
|
const { data: dataHome } = platformPaths({ platform: runtime.platform,
|
|
158
163
|
env: runtime.env ?? {} });
|
|
164
|
+
const context = clientContext(home, path.join(dataHome, "acc"));
|
|
159
165
|
|
|
160
166
|
const detected = await detectInstallation({ adapters, context,
|
|
161
167
|
probeTimeoutMs: probeTimeout(runtime.env) });
|
|
@@ -42,6 +42,14 @@ async function openContext(options, runtime) {
|
|
|
42
42
|
|
|
43
43
|
const human = value => (typeof value === "string" ? value : JSON.stringify(value, null, 2));
|
|
44
44
|
|
|
45
|
+
/** How many are here, and how many only look it. */
|
|
46
|
+
export function describePresence({ live = 0, stale = 0 } = {}) {
|
|
47
|
+
if (live === 0) return "0 live";
|
|
48
|
+
if (stale === 0) return `${live} live`;
|
|
49
|
+
if (stale === live) return `${live} present, none answering`;
|
|
50
|
+
return `${live} live (${stale} not answering)`;
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
const HANDLERS = Object.freeze({
|
|
46
54
|
attach: async ({ options, context }) => {
|
|
47
55
|
const session = await context.service.openSession({
|
|
@@ -241,7 +249,12 @@ const HANDLERS = Object.freeze({
|
|
|
241
249
|
status: async ({ options, context }) => {
|
|
242
250
|
const status = await context.service.collectStatus({
|
|
243
251
|
participantId: options.participant, all: options.all === true });
|
|
244
|
-
|
|
252
|
+
// `live` counts everyone present, which includes sessions gone stale - a
|
|
253
|
+
// client that exited without its session being closed keeps a record that
|
|
254
|
+
// stops being answered but does not disappear. Printing the number alone
|
|
255
|
+
// said "1 live" about a workspace where the last agent had left minutes
|
|
256
|
+
// before, which is the one thing a person reads this line to find out.
|
|
257
|
+
const text = `${describePresence(status.counts)}; ${status.counts.claims} claim(s); `
|
|
245
258
|
+ `protection ${status.protection}`;
|
|
246
259
|
return { data: status, text };
|
|
247
260
|
},
|