agents-can-communicate 0.8.0 → 0.8.1

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 (57) hide show
  1. package/bin/entrypoints/acc-mcp.mjs +0 -1
  2. package/bin/entrypoints/acc.mjs +0 -1
  3. package/docs/ADAPTER_AUTHORING.md +36 -17
  4. package/docs/ARCHITECTURE.md +9 -6
  5. package/docs/CAPABILITIES.md +23 -13
  6. package/docs/CLI.md +6 -3
  7. package/docs/PROTOCOL.md +2 -2
  8. package/docs/RELEASING.md +6 -4
  9. package/docs/TROUBLESHOOTING.md +2 -2
  10. package/node_modules/@agents-can-communicate/adapter-antigravity/package.json +1 -1
  11. package/node_modules/@agents-can-communicate/adapter-antigravity/plugin/plugin.json +1 -1
  12. package/node_modules/@agents-can-communicate/adapter-antigravity/src/adapter.mjs +2 -3
  13. package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +1 -1
  14. package/node_modules/@agents-can-communicate/adapter-claude-code/src/adapter.mjs +5 -4
  15. package/node_modules/@agents-can-communicate/adapter-claude-code/src/inbox-delivery.mjs +12 -17
  16. package/node_modules/@agents-can-communicate/adapter-claude-code/src/inbox-endpoint.mjs +2 -2
  17. package/node_modules/@agents-can-communicate/adapter-codex/package.json +1 -1
  18. package/node_modules/@agents-can-communicate/adapter-codex/src/adapter.mjs +3 -4
  19. package/node_modules/@agents-can-communicate/adapter-codex/src/app-server-client.mjs +10 -13
  20. package/node_modules/@agents-can-communicate/adapter-codex/src/live-permissions.mjs +5 -5
  21. package/node_modules/@agents-can-communicate/adapter-codex/src/maintenance-host.mjs +46 -10
  22. package/node_modules/@agents-can-communicate/adapter-codex/src/maintenance.mjs +5 -7
  23. package/node_modules/@agents-can-communicate/adapter-codex/src/native-delivery.mjs +16 -12
  24. package/node_modules/@agents-can-communicate/adapter-codex/src/native-endpoint.mjs +12 -2
  25. package/node_modules/@agents-can-communicate/adapter-codex/src/service-setup.mjs +26 -9
  26. package/node_modules/@agents-can-communicate/adapter-codex/src/ws-json-rpc.mjs +12 -0
  27. package/node_modules/@agents-can-communicate/adapter-gemini-cli/extension/gemini-extension.json +1 -1
  28. package/node_modules/@agents-can-communicate/adapter-gemini-cli/package.json +1 -1
  29. package/node_modules/@agents-can-communicate/adapter-grok/package.json +1 -1
  30. package/node_modules/@agents-can-communicate/adapter-kimi/package.json +1 -1
  31. package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
  32. package/node_modules/@agents-can-communicate/adapter-sdk/src/certification.mjs +2 -2
  33. package/node_modules/@agents-can-communicate/adapter-sdk/src/index.mjs +3 -2
  34. package/node_modules/@agents-can-communicate/adapter-sdk/src/native-delivery.mjs +85 -77
  35. package/node_modules/@agents-can-communicate/adapter-sdk/src/native-vocabulary.mjs +4 -4
  36. package/node_modules/@agents-can-communicate/cli/package.json +1 -1
  37. package/node_modules/@agents-can-communicate/cli/src/main.mjs +5 -6
  38. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/maintenance.mjs +9 -15
  39. package/node_modules/@agents-can-communicate/cli/src/managed-runtime/pins.mjs +13 -4
  40. package/node_modules/@agents-can-communicate/core/package.json +1 -1
  41. package/node_modules/@agents-can-communicate/core/src/receipts.mjs +17 -2
  42. package/node_modules/@agents-can-communicate/core/src/status.mjs +14 -2
  43. package/node_modules/@agents-can-communicate/delivery-router/package.json +1 -1
  44. package/node_modules/@agents-can-communicate/delivery-router/src/refresh-binding.mjs +2 -2
  45. package/node_modules/@agents-can-communicate/delivery-router/src/router.mjs +9 -41
  46. package/node_modules/@agents-can-communicate/hook-runner/package.json +1 -1
  47. package/node_modules/@agents-can-communicate/hook-runner/src/client-pid.mjs +30 -1
  48. package/node_modules/@agents-can-communicate/hook-runner/src/native-binding.mjs +3 -4
  49. package/node_modules/@agents-can-communicate/hook-runner/src/process-table.mjs +30 -14
  50. package/node_modules/@agents-can-communicate/hook-runner/src/runner.mjs +3 -1
  51. package/node_modules/@agents-can-communicate/installer/package.json +1 -1
  52. package/node_modules/@agents-can-communicate/installer/src/delivery-diagnostics.mjs +0 -2
  53. package/node_modules/@agents-can-communicate/installer/src/detect.mjs +3 -4
  54. package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
  55. package/node_modules/@agents-can-communicate/protocol/package.json +1 -1
  56. package/node_modules/@agents-can-communicate/storage-filesystem/package.json +1 -1
  57. package/package.json +1 -1
@@ -4,6 +4,35 @@ import path from "node:path";
4
4
  // enough that a table which disagrees with itself cannot spin.
5
5
  const MAX_HOPS = 16;
6
6
 
7
+ // A client shipped as a node script runs as `node`, so its comm names the
8
+ // interpreter. Gemini CLI is one, measured on 0.60.0. For these, the script the
9
+ // interpreter was given names the client.
10
+ const SCRIPT_HOSTS = new Set(["node"]);
11
+ const SCRIPT_EXTENSION = /\.[mc]?js$/;
12
+
13
+ /** What the script a script host runs could be called, without extensions. */
14
+ function scriptsOf(entry) {
15
+ if (!SCRIPT_HOSTS.has(path.basename(entry.comm)) || typeof entry.args !== "string") return [];
16
+ // `args` starts with the interpreter as it was invoked, which is the comm
17
+ // itself on macOS; past it, the first word that is no option is the script -
18
+ // unless an option written without `=` came right before it, since that word
19
+ // may be the option's value (`--require ./preload.cjs`). Which options take
20
+ // one depends on the client's own node, so such a word is kept as a candidate
21
+ // and the search goes on; it ends at the first word no bare option precedes.
22
+ const rest = entry.args.startsWith(`${entry.comm} `) ? entry.args.slice(entry.comm.length)
23
+ : entry.args.replace(/^\S+/, "");
24
+ const candidates = [];
25
+ let afterBareOption = false;
26
+ for (const word of rest.trim().split(/\s+/)) {
27
+ if (word === "") continue;
28
+ if (word.startsWith("-")) { afterBareOption = !word.includes("="); continue; }
29
+ candidates.push(path.basename(word).replace(SCRIPT_EXTENSION, ""));
30
+ if (!afterBareOption) break;
31
+ afterBareOption = false;
32
+ }
33
+ return candidates;
34
+ }
35
+
7
36
  /**
8
37
  * The pid of the client this hook is running for, or null when nobody knows.
9
38
  *
@@ -24,7 +53,7 @@ export function resolveClientPid({ table, from, command, maxHops = MAX_HOPS }) {
24
53
  seen.add(current);
25
54
  // `ps` reports some entries bare (`claude`) and some with a path
26
55
  // (`/bin/zsh`), so the comparison has to be on the basename.
27
- if (path.basename(entry.comm) === command) return current;
56
+ if (path.basename(entry.comm) === command || scriptsOf(entry).includes(command)) return current;
28
57
  if (entry.ppid === current || entry.ppid <= 1) return null;
29
58
  current = entry.ppid;
30
59
  }
@@ -16,9 +16,8 @@ const DEFAULT_CADENCE_MS = 60_000;
16
16
 
17
17
  // Reasons the static rule refused: the client will not change within the
18
18
  // session, so the state is "unsupported" rather than a retryable "degraded".
19
- const STATIC_REASONS = new Set(["native_delivery_unsupported", "platform_not_captured",
20
- "version_unavailable", "prerelease_not_captured", "below_minimum_version",
21
- "known_bad_version"]);
19
+ const STATIC_REASONS = new Set(["native_delivery_unsupported", "version_unavailable",
20
+ "below_minimum_version", "known_bad_version"]);
22
21
 
23
22
  const isPid = value => Number.isInteger(value) && value > 0;
24
23
 
@@ -77,7 +76,7 @@ export async function establishNativeBinding({ adapter, event, hookBinding, clie
77
76
  }, budget);
78
77
  }),
79
78
  ]);
80
- const verdict = validateNativeHandshake(adapter, { clientVersion, platform, handshake });
79
+ const verdict = validateNativeHandshake(adapter, { clientVersion, handshake });
81
80
  if (!verdict.ok) {
82
81
  await discardEndpoint(adapter, handshake, runtimeDir);
83
82
  return outcome(STATIC_REASONS.has(verdict.reasonCode) ? "unsupported" : "degraded",
@@ -8,27 +8,43 @@ const run = promisify(execFile);
8
8
  const DEFAULT_TIMEOUT_MS = 1_000;
9
9
 
10
10
  const LINE = /^\s*(\d+)\s+(\d+)\s+(.+?)\s*$/;
11
+ // A pid, then the whole rest of the line: a command line has spaces anywhere.
12
+ const ARGS_LINE = /^\s*(\d+)\s+(.+?)\s*$/;
13
+ // Command lines are long; measured at 231 KB for 1,137 processes on a desktop
14
+ // Mac, a quarter of execFile's default buffer.
15
+ const ARGS_BUFFER = 16 * 1024 * 1024;
11
16
 
12
17
  /**
13
- * Every process on this machine, as pid -> parent and executable.
18
+ * Every process on this machine, as pid -> parent, executable and, when it
19
+ * could be read, command line.
14
20
  *
15
21
  * Returns an empty map rather than throwing when the platform has no `ps`
16
22
  * (Windows) or the call fails. An empty table resolves no client, which is the
17
- * "nobody knows" answer the caller already handles.
23
+ * "nobody knows" answer the caller already handles. The command lines are read
24
+ * at the same time and within the same timeout; they only name a client that
25
+ * is a script, so when that read fails every entry keeps what it had before.
18
26
  */
19
27
  export async function readProcessTable({ run: exec = run,
20
28
  timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
21
- try {
22
- const { stdout } = await exec("ps", ["-o", "pid=,ppid=,comm=", "-A"],
23
- { timeout: timeoutMs, killSignal: "SIGKILL" });
24
- const table = new Map();
25
- for (const line of stdout.split("\n")) {
26
- const match = LINE.exec(line);
27
- if (match === null) continue;
28
- table.set(Number(match[1]), { ppid: Number(match[2]), comm: match[3] });
29
- }
30
- return table;
31
- } catch {
32
- return new Map();
29
+ const options = { timeout: timeoutMs, killSignal: "SIGKILL" };
30
+ const [tree, commandLines] = await Promise.all([
31
+ exec("ps", ["-o", "pid=,ppid=,comm=", "-A"], options).then(({ stdout }) => stdout, () => null),
32
+ exec("ps", ["-A", "-o", "pid=,args="], { ...options, maxBuffer: ARGS_BUFFER })
33
+ .then(({ stdout }) => stdout, () => ""),
34
+ ]);
35
+ if (typeof tree !== "string") return new Map();
36
+ const args = new Map();
37
+ for (const line of String(commandLines).split("\n")) {
38
+ const match = ARGS_LINE.exec(line);
39
+ if (match !== null) args.set(Number(match[1]), match[2]);
33
40
  }
41
+ const table = new Map();
42
+ for (const line of tree.split("\n")) {
43
+ const match = LINE.exec(line);
44
+ if (match === null) continue;
45
+ const pid = Number(match[1]);
46
+ table.set(pid, { ppid: Number(match[2]), comm: match[3],
47
+ ...(args.has(pid) ? { args: args.get(pid) } : {}) });
48
+ }
49
+ return table;
34
50
  }
@@ -400,8 +400,10 @@ const HANDLERS = {
400
400
  const facts = await runtimeFacts(import.meta.url);
401
401
  await storeSessionBinding({ runtimeDir: paths.root, deadlineAt: deadline, harnessSessionId: event.sessionId,
402
402
  accSessionId: binding.accSessionId, generation: binding.generation, ...facts });
403
+ // The pid this binding already knew keeps the refreshed pin reapable if
404
+ // the lookup below cannot find the client.
403
405
  await writePin({ root: pinRoot, harnessSessionId: event.sessionId, runtimeRoot: facts.runtimeRoot,
404
- version: facts.version, storeVersion: facts.storeVersion });
406
+ version: facts.version, storeVersion: facts.storeVersion, clientPid: binding.clientPid });
405
407
  }
406
408
  const clientVersion = await probeClientVersion(adapter,
407
409
  { timeoutMs: Math.max(1, Math.min(1_000, deadline - Date.now())) });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/installer",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
@@ -2,9 +2,7 @@
2
2
  // reasons; installation and doctor must not turn a version floor into readiness.
3
3
  const REASONS = Object.freeze({
4
4
  native_delivery_unsupported: "this adapter has no native delivery channel",
5
- platform_not_captured: "native delivery is not verified on this platform",
6
5
  version_unavailable: "the client version could not be verified",
7
- prerelease_not_captured: "this client version is not a verified stable release",
8
6
  below_minimum_version: "the client is below the native delivery minimum version",
9
7
  known_bad_version: "this client version has a known native delivery failure",
10
8
  native_endpoint_unavailable: "the client's local delivery service is unavailable",
@@ -13,9 +13,8 @@ const run = promisify(execFile);
13
13
 
14
14
  // Reasons the client itself will not change within an install: unsupported,
15
15
  // as opposed to degraded, which a repaired probe or shell may lift.
16
- const STATIC_REASONS = new Set(["native_delivery_unsupported", "platform_not_captured",
17
- "version_unavailable", "prerelease_not_captured", "below_minimum_version",
18
- "known_bad_version"]);
16
+ const STATIC_REASONS = new Set(["native_delivery_unsupported", "version_unavailable",
17
+ "below_minimum_version", "known_bad_version"]);
19
18
 
20
19
  const DEFAULT_PROBE_TIMEOUT_MS = 3_000;
21
20
 
@@ -76,7 +75,7 @@ async function detectNative(adapter, entry, { context, platform, probeTimeoutMs,
76
75
  }
77
76
  try {
78
77
  facts.eligibility = evaluateNativeEligibility(adapter,
79
- { clientVersion: entry.version, platform, probe: facts.probe });
78
+ { clientVersion: entry.version, probe: facts.probe });
80
79
  } catch {
81
80
  facts.eligibility = { eligible: false, reasonCode: "feature_probe_failed" };
82
81
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agents-can-communicate/mcp-server",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
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.8.0",
3
+ "version": "0.8.1",
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.8.0",
3
+ "version": "0.8.1",
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.8.0",
3
+ "version": "0.8.1",
4
4
  "accManagedUpdateProtocol": 2,
5
5
  "accStoreVersion": 6,
6
6
  "type": "module",