agents-can-communicate 0.8.3 → 0.8.4
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-update-worker.mjs +4 -1
- package/bin/entrypoints/acc-mcp.mjs +3 -0
- package/bin/entrypoints/acc.mjs +5 -0
- package/docs/ARCHITECTURE.md +3 -1
- package/docs/CLI.md +7 -1
- package/docs/CONCEPTS.md +2 -2
- package/docs/CONFIGURATION.md +3 -2
- package/docs/GETTING_STARTED.md +12 -9
- package/docs/HOW_IT_WORKS.md +2 -1
- package/docs/TROUBLESHOOTING.md +11 -2
- package/docs/UPGRADING.md +18 -0
- package/node_modules/@agents-can-communicate/adapter-antigravity/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-antigravity/plugin/plugin.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/src/adapter.mjs +7 -1
- package/node_modules/@agents-can-communicate/adapter-codex/src/app-server-client.mjs +24 -1
- package/node_modules/@agents-can-communicate/adapter-codex/src/embedded-host.mjs +65 -0
- package/node_modules/@agents-can-communicate/adapter-codex/src/install.mjs +2 -0
- package/node_modules/@agents-can-communicate/adapter-codex/src/maintenance-host.mjs +33 -9
- package/node_modules/@agents-can-communicate/adapter-codex/src/native-delivery.mjs +38 -1
- package/node_modules/@agents-can-communicate/adapter-codex/src/native-discovery.mjs +82 -0
- package/node_modules/@agents-can-communicate/adapter-codex/src/service-setup.mjs +59 -5
- 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-grok/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-kimi/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-vocabulary.mjs +3 -0
- package/node_modules/@agents-can-communicate/cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/cli/src/doctor-command.mjs +2 -1
- package/node_modules/@agents-can-communicate/cli/src/main.mjs +18 -2
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/activation.mjs +25 -8
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/install.mjs +13 -3
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/schedule.mjs +19 -1
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/worker.mjs +38 -3
- package/node_modules/@agents-can-communicate/cli/src/native-delivery-status.mjs +5 -1
- package/node_modules/@agents-can-communicate/cli/src/native-session-diagnostics.mjs +3 -0
- package/node_modules/@agents-can-communicate/core/package.json +1 -1
- package/node_modules/@agents-can-communicate/delivery-router/package.json +1 -1
- package/node_modules/@agents-can-communicate/delivery-router/src/native-reason.mjs +18 -0
- package/node_modules/@agents-can-communicate/delivery-router/src/router.mjs +15 -3
- package/node_modules/@agents-can-communicate/hook-runner/package.json +1 -1
- package/node_modules/@agents-can-communicate/hook-runner/src/runner.mjs +110 -4
- package/node_modules/@agents-can-communicate/installer/package.json +1 -1
- package/node_modules/@agents-can-communicate/installer/src/delivery-diagnostics.mjs +2 -0
- package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
- package/node_modules/@agents-can-communicate/mcp-server/src/server.mjs +3 -0
- 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
|
@@ -5,12 +5,14 @@ import path from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
import { capabilityEvidence, clearNativeAttempt, clearSessionBinding, effectiveCapabilities,
|
|
8
|
-
loadSessionBinding, storeSessionBinding }
|
|
8
|
+
loadSessionBinding, normalizedEvent, storeSessionBinding }
|
|
9
9
|
from "@agents-can-communicate/adapter-sdk";
|
|
10
10
|
import { createCoordinationService } from "@agents-can-communicate/core";
|
|
11
|
+
import { loadOwnership } from "@agents-can-communicate/installer";
|
|
11
12
|
import { AccError, createId } from "@agents-can-communicate/protocol";
|
|
12
13
|
import { openFilesystemStore } from "@agents-can-communicate/storage-filesystem";
|
|
13
|
-
import { clearPin, createGitProbe, resolveHookWorkspace, platformDataHome, runtimePaths,
|
|
14
|
+
import { clearPin, createGitProbe, resolveHookWorkspace, platformDataHome, runtimePaths,
|
|
15
|
+
writePin as writeRuntimePin }
|
|
14
16
|
from "@agents-can-communicate/cli";
|
|
15
17
|
|
|
16
18
|
import { resolveClientPid } from "./client-pid.mjs";
|
|
@@ -388,11 +390,16 @@ async function projectActivation(input, offered) {
|
|
|
388
390
|
}
|
|
389
391
|
|
|
390
392
|
const HANDLERS = {
|
|
393
|
+
// `knownClientPid` and `pins: false` are registration's (#167): it runs in
|
|
394
|
+
// a peer's process, so the client's pid is handed over rather than found in
|
|
395
|
+
// this process's ancestry, and it pins nothing because no hook code of any
|
|
396
|
+
// generation runs for the session until its first real hook pins its own.
|
|
391
397
|
async sessionStart({ event, context, adapter, adapterId, binding, paths,
|
|
392
|
-
readProcessTable, probeClientVersion, platform, deadline }) {
|
|
398
|
+
readProcessTable, probeClientVersion, platform, deadline, knownClientPid, pins = true }) {
|
|
393
399
|
// Where this session's pin lives, resolved once and reused at every write
|
|
394
400
|
// below. Beside `paths.root`, never inside it: see `managerRootFor`.
|
|
395
401
|
const pinRoot = managerRootFor(context.dataHome);
|
|
402
|
+
const writePin = pins === false ? async () => {} : writeRuntimePin;
|
|
396
403
|
// A repeated start refreshes the client's version/platform. Remove the old
|
|
397
404
|
// certified facts before any probe, PID lookup, resume, or open can fail;
|
|
398
405
|
// keep only the generation identity needed for a successful resume.
|
|
@@ -414,7 +421,8 @@ const HANDLERS = {
|
|
|
414
421
|
// null, and the session is then judged by age alone - which is exactly the
|
|
415
422
|
// behaviour every session had before this existed.
|
|
416
423
|
const command = adapter.client?.command ?? null;
|
|
417
|
-
const pid =
|
|
424
|
+
const pid = Number.isInteger(knownClientPid) && knownClientPid > 0 ? knownClientPid
|
|
425
|
+
: command === null ? null
|
|
418
426
|
: resolveClientPid({ table: await readProcessTable({
|
|
419
427
|
timeoutMs: Math.max(1, Math.min(1_000, deadline - Date.now())) }), from: process.pid, command });
|
|
420
428
|
assertHookBudget(deadline);
|
|
@@ -767,3 +775,101 @@ export async function runHook({ adapterId, payload, adapters, dataHome, env,
|
|
|
767
775
|
clearTimeout(timer);
|
|
768
776
|
}
|
|
769
777
|
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Register a client session before its first turn (#167).
|
|
781
|
+
*
|
|
782
|
+
* Exactly what SessionStart writes - binding, session, native binding and its
|
|
783
|
+
* diagnostic - under the same per-session lifecycle lock, so the chat's first
|
|
784
|
+
* real hook finds the binding and resumes this session instead of opening a
|
|
785
|
+
* second one. The caller is a peer's process, so the client's pid is handed
|
|
786
|
+
* over, the caller's own ACC_PARTICIPANT never names the chat, and nothing is
|
|
787
|
+
* pinned or printed. A session that already has a binding is left alone.
|
|
788
|
+
* Never throws: status must not fail because of it.
|
|
789
|
+
*/
|
|
790
|
+
export async function registerNativeSession({ adapterId, adapters, event: target, clientPid,
|
|
791
|
+
workspaceId, dataHome, env = {}, runtime = defaultRuntime(), budgetMs = DEFAULT_BUDGET_MS,
|
|
792
|
+
probeClientVersion = defaultProbeClientVersion, platform = `${process.platform}-${process.arch}` }) {
|
|
793
|
+
const deadline = Date.now() + budgetMs;
|
|
794
|
+
const skipped = reason => ({ registered: false, reason });
|
|
795
|
+
const register = async () => {
|
|
796
|
+
const adapter = adapters?.[adapterId];
|
|
797
|
+
if (adapter === undefined || !Number.isInteger(clientPid) || clientPid <= 0) return skipped("unsupported");
|
|
798
|
+
const event = normalizedEvent({ kind: "sessionStart", sessionId: target?.sessionId, cwd: target?.cwd });
|
|
799
|
+
const { ACC_PARTICIPANT: _callerName, ...hostEnv } = env;
|
|
800
|
+
const context = await openContext({ event, adapterId, dataHome, runtime, env: hostEnv, deadline });
|
|
801
|
+
if (workspaceId !== undefined && context.descriptor.id !== workspaceId) return skipped("other_workspace");
|
|
802
|
+
return withSessionLifecycle({ root: context.paths.root, sessionId: event.sessionId,
|
|
803
|
+
clock: runtime.clock, deadlineAt: deadline }, async () => {
|
|
804
|
+
// As in runHook: recover a write a previous holder journalled before
|
|
805
|
+
// interpreting the binding.
|
|
806
|
+
const store = await openFilesystemStore({ root: context.paths.root, clock: runtime.clock,
|
|
807
|
+
ids: runtime.ids, workspaceId: context.descriptor.id, deadlineAt: deadline });
|
|
808
|
+
context.service = createCoordinationService({ store, clock: runtime.clock, ids: runtime.ids });
|
|
809
|
+
if (await loadSessionBinding({ runtimeDir: context.paths.root,
|
|
810
|
+
harnessSessionId: event.sessionId }) !== null) return skipped("known");
|
|
811
|
+
const started = await HANDLERS.sessionStart({ event, context, adapter, adapterId, binding: null,
|
|
812
|
+
paths: context.paths, readProcessTable: async () => new Map(), probeClientVersion,
|
|
813
|
+
platform, deadline, knownClientPid: clientPid, pins: false });
|
|
814
|
+
const session = await context.service.locateSession(started.accSessionId);
|
|
815
|
+
return { registered: true, sessionId: started.accSessionId,
|
|
816
|
+
participantId: session?.record.participantId ?? null, workspaceId: context.descriptor.id,
|
|
817
|
+
nativeBinding: started.nativeBinding };
|
|
818
|
+
});
|
|
819
|
+
};
|
|
820
|
+
let timer;
|
|
821
|
+
try {
|
|
822
|
+
return await Promise.race([register(), new Promise(resolve => {
|
|
823
|
+
timer = setTimeout(() => resolve(skipped("timed_out")), Math.max(0, deadline - Date.now()));
|
|
824
|
+
})]);
|
|
825
|
+
} catch {
|
|
826
|
+
return skipped("failed");
|
|
827
|
+
} finally {
|
|
828
|
+
clearTimeout(timer);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* What a peer runs before it reads status (#167): ask every installed adapter
|
|
834
|
+
* that can see its client's sessions for the ones ACC could register, and
|
|
835
|
+
* register those in this workspace. Bounded by one budget and fail-open; the
|
|
836
|
+
* answer lists what was registered and is otherwise informational.
|
|
837
|
+
*/
|
|
838
|
+
export async function registerDiscoveredSessions({ adapters, workspaceId, dataHome, env = {},
|
|
839
|
+
budgetMs = 2_500, runtime, probeClientVersion, platform }) {
|
|
840
|
+
const deadline = Date.now() + budgetMs;
|
|
841
|
+
const remaining = () => Math.max(0, deadline - Date.now());
|
|
842
|
+
// An adapter that ignores its timeout must not hold status: its answer is
|
|
843
|
+
// raced against what is left of the budget, and a late one is dropped.
|
|
844
|
+
const withinBudget = work => {
|
|
845
|
+
let timer;
|
|
846
|
+
return Promise.race([Promise.resolve().then(work).catch(() => []),
|
|
847
|
+
new Promise(resolve => { timer = setTimeout(resolve, remaining(), []); })])
|
|
848
|
+
.finally(() => clearTimeout(timer));
|
|
849
|
+
};
|
|
850
|
+
const registered = [];
|
|
851
|
+
let installed;
|
|
852
|
+
try {
|
|
853
|
+
installed = new Set((await loadOwnership({ dataHome })).installs.map(entry => entry.adapterId));
|
|
854
|
+
} catch {
|
|
855
|
+
return registered;
|
|
856
|
+
}
|
|
857
|
+
for (const adapter of Object.values(adapters ?? {})) {
|
|
858
|
+
if (typeof adapter?.discoverNativeSessions !== "function" || !installed.has(adapter.id)) continue;
|
|
859
|
+
const found = await withinBudget(() => adapter.discoverNativeSessions({ env,
|
|
860
|
+
timeoutMs: remaining() }));
|
|
861
|
+
for (const candidate of Array.isArray(found) ? found : []) {
|
|
862
|
+
if (remaining() === 0) return registered;
|
|
863
|
+
const result = await registerNativeSession({ adapterId: adapter.id, adapters,
|
|
864
|
+
event: { sessionId: candidate?.sessionId, cwd: candidate?.cwd }, clientPid: candidate?.clientPid,
|
|
865
|
+
workspaceId, dataHome, env, budgetMs: remaining(),
|
|
866
|
+
...(runtime ? { runtime } : {}), ...(probeClientVersion ? { probeClientVersion } : {}),
|
|
867
|
+
...(platform ? { platform } : {}) });
|
|
868
|
+
if (result.registered) {
|
|
869
|
+
registered.push({ adapterId: adapter.id, participantId: result.participantId,
|
|
870
|
+
sessionId: result.sessionId });
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return registered;
|
|
875
|
+
}
|
|
@@ -12,6 +12,8 @@ const REASONS = Object.freeze({
|
|
|
12
12
|
probe_version_mismatch: "the local service and CLI versions differ",
|
|
13
13
|
protocol_mismatch: "the local service did not confirm the required protocol",
|
|
14
14
|
unsupported_shell: "automatic launch setup requires zsh",
|
|
15
|
+
client_session_embedded: "the client runs this session on its own embedded service, which peers "
|
|
16
|
+
+ "cannot reach",
|
|
15
17
|
});
|
|
16
18
|
|
|
17
19
|
export function describeNativeReason(reason, { clientVersion, minimumVersion } = {}) {
|
|
@@ -118,6 +118,9 @@ async function callTool(name, args, context) {
|
|
|
118
118
|
|
|
119
119
|
switch (name) {
|
|
120
120
|
case "acc_status":
|
|
121
|
+
// Chats a client holds before their first turn, registered so this
|
|
122
|
+
// caller can address them (#167). Fail-open: status is read regardless.
|
|
123
|
+
await Promise.resolve().then(() => context.registerNativeSessions?.()).catch(() => null);
|
|
121
124
|
return service.collectStatus({});
|
|
122
125
|
case "acc_sync":
|
|
123
126
|
return service.sync({ ...owner, cursor: args.cursor ?? null,
|