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.
Files changed (22) hide show
  1. package/bin/acc-hook.mjs +0 -0
  2. package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +1 -1
  3. package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/.claude-plugin/plugin.json +1 -1
  4. package/node_modules/@agents-can-communicate/adapter-codex/package.json +1 -1
  5. package/node_modules/@agents-can-communicate/adapter-codex/plugin/.codex-plugin/plugin.json +1 -1
  6. package/node_modules/@agents-can-communicate/adapter-codex/src/install.mjs +37 -2
  7. package/node_modules/@agents-can-communicate/adapter-gemini-cli/extension/gemini-extension.json +1 -1
  8. package/node_modules/@agents-can-communicate/adapter-gemini-cli/package.json +1 -1
  9. package/node_modules/@agents-can-communicate/adapter-kimi/package.json +1 -1
  10. package/node_modules/@agents-can-communicate/adapter-kimi/plugin/.kimi-plugin/plugin.json +1 -1
  11. package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
  12. package/node_modules/@agents-can-communicate/cli/package.json +1 -1
  13. package/node_modules/@agents-can-communicate/cli/src/doctor-command.mjs +4 -2
  14. package/node_modules/@agents-can-communicate/cli/src/install-command.mjs +8 -2
  15. package/node_modules/@agents-can-communicate/cli/src/main.mjs +14 -1
  16. package/node_modules/@agents-can-communicate/core/package.json +1 -1
  17. package/node_modules/@agents-can-communicate/hook-runner/package.json +1 -1
  18. package/node_modules/@agents-can-communicate/installer/package.json +1 -1
  19. package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
  20. package/node_modules/@agents-can-communicate/protocol/package.json +1 -1
  21. package/node_modules/@agents-can-communicate/storage-filesystem/package.json +1 -1
  22. 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/adapter-claude-code",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
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.5",
3
+ "version": "0.1.6",
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.5",
3
+ "version": "0.1.6",
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.5",
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.5",
3
+ "version": "0.1.6",
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.5",
3
+ "version": "0.1.6",
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.5",
3
+ "version": "0.1.6",
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.5",
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/adapter-sdk",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
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.5",
3
+ "version": "0.1.6",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -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.live} live session(s); `
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
- const text = `${status.counts.live} live; ${status.counts.claims} claim(s); `
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
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/core",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
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.5",
3
+ "version": "0.1.6",
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.5",
3
+ "version": "0.1.6",
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.5",
3
+ "version": "0.1.6",
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.5",
3
+ "version": "0.1.6",
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.5",
3
+ "version": "0.1.6",
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.5",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "description": "Local-first coordination for independently opened AI agent sessions.",
6
6
  "keywords": [