agents-can-communicate 0.3.1 → 0.4.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.
- package/README.md +95 -52
- package/SECURITY.md +1 -1
- package/bin/acc-bootstrap.mjs +5 -54
- package/bin/acc-claude-channel.mjs +8 -175
- package/bin/acc-hook.mjs +4 -133
- package/bin/acc-mcp.mjs +4 -59
- package/bin/acc-update-worker.mjs +8 -0
- package/bin/acc.mjs +5 -39
- package/bin/entrypoints/acc-bootstrap.mjs +47 -0
- package/bin/entrypoints/acc-claude-channel.mjs +149 -0
- package/bin/entrypoints/acc-hook.mjs +58 -0
- package/bin/entrypoints/acc-mcp.mjs +70 -0
- package/bin/entrypoints/acc.mjs +51 -0
- package/bin/entrypoints/claude-channel-stdio.mjs +29 -0
- package/bin/entrypoints/hook-output.mjs +77 -0
- package/docs/ADAPTER_AUTHORING.md +52 -8
- package/docs/ARCHITECTURE.md +64 -11
- package/docs/CAPABILITIES.md +41 -37
- package/docs/CLI.md +145 -25
- package/docs/CONCEPTS.md +13 -6
- package/docs/CONFIGURATION.md +37 -22
- package/docs/DESIGN_DECISIONS.md +16 -11
- package/docs/GETTING_STARTED.md +41 -7
- package/docs/GLOSSARY.md +9 -3
- package/docs/HOW_IT_WORKS.md +32 -11
- package/docs/MCP.md +91 -24
- package/docs/PROTOCOL.md +69 -12
- package/docs/RELEASING.md +28 -10
- package/docs/SECURITY_MODEL.md +47 -13
- package/docs/TROUBLESHOOTING.md +102 -20
- package/docs/UPGRADING.md +154 -0
- package/docs/WHY_ACC.md +5 -4
- package/docs/index.md +8 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/skills/acc/SKILL.md +106 -5
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/install.mjs +6 -6
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/native-delivery.mjs +2 -1
- package/node_modules/@agents-can-communicate/adapter-codex/certification.json +38 -13
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/certification-provenance.json +220 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-current-binding-product-evidence.json +1549 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-current-binding-product.json +27 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-product-evidence.json +1549 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-product.json +27 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-transport-evidence.json +259 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-local-daemon-transport.json +21 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-current-binding-product-evidence.json +1549 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-current-binding-product.json +27 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-product-evidence.json +1549 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-product.json +27 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-transport-evidence.json +259 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.153.4-local-daemon-transport.json +21 -0
- package/node_modules/@agents-can-communicate/adapter-codex/package.json +14 -2
- package/node_modules/@agents-can-communicate/adapter-codex/plugin/skills/acc/SKILL.md +106 -5
- package/node_modules/@agents-can-communicate/adapter-codex/src/adapter.mjs +26 -44
- package/node_modules/@agents-can-communicate/adapter-codex/src/app-server-client.mjs +70 -17
- package/node_modules/@agents-can-communicate/adapter-codex/src/config-block.mjs +231 -0
- package/node_modules/@agents-can-communicate/adapter-codex/src/hooks.mjs +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/src/install.mjs +71 -63
- package/node_modules/@agents-can-communicate/adapter-codex/src/native-delivery.mjs +107 -103
- package/node_modules/@agents-can-communicate/adapter-codex/src/native-endpoint.mjs +96 -0
- package/node_modules/@agents-can-communicate/adapter-codex/src/ws-json-rpc.mjs +3 -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/extension/skills/acc/SKILL.md +106 -5
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/src/install.mjs +2 -2
- package/node_modules/@agents-can-communicate/adapter-grok/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-grok/plugin/skills/acc/SKILL.md +122 -8
- package/node_modules/@agents-can-communicate/adapter-grok/src/adapter.mjs +5 -3
- package/node_modules/@agents-can-communicate/adapter-grok/src/hooks.mjs +9 -0
- package/node_modules/@agents-can-communicate/adapter-grok/src/install.mjs +2 -2
- package/node_modules/@agents-can-communicate/adapter-kimi/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-kimi/plugin/skills/acc/SKILL.md +106 -5
- package/node_modules/@agents-can-communicate/adapter-kimi/src/hooks.mjs +1 -1
- package/node_modules/@agents-can-communicate/adapter-kimi/src/install.mjs +2 -2
- package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-sdk/src/capabilities.mjs +5 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/context-projector.mjs +26 -4
- package/node_modules/@agents-can-communicate/adapter-sdk/src/decision-text.mjs +19 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/hook-shim.mjs +5 -3
- package/node_modules/@agents-can-communicate/adapter-sdk/src/index.mjs +1 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-delivery.mjs +9 -2
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-vocabulary.mjs +2 -6
- package/node_modules/@agents-can-communicate/adapter-sdk/src/session-binding.mjs +29 -9
- package/node_modules/@agents-can-communicate/adapter-sdk/src/toml-block.mjs +8 -6
- package/node_modules/@agents-can-communicate/cli/package.json +4 -2
- package/node_modules/@agents-can-communicate/cli/src/args.mjs +49 -22
- package/node_modules/@agents-can-communicate/cli/src/doctor-command.mjs +11 -4
- package/node_modules/@agents-can-communicate/cli/src/git-probe.mjs +9 -3
- package/node_modules/@agents-can-communicate/cli/src/help.mjs +109 -14
- package/node_modules/@agents-can-communicate/cli/src/install-command.mjs +24 -5
- package/node_modules/@agents-can-communicate/cli/src/main.mjs +35 -12
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/activation.mjs +110 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/command.mjs +64 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/download.mjs +71 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/entry.mjs +110 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/generation.mjs +167 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/install.mjs +78 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/launchers.mjs +60 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/leases.mjs +60 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/location.mjs +55 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/mutex.mjs +145 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/policy.mjs +4 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/refresh.mjs +40 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/schedule.mjs +21 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/state.mjs +123 -0
- package/node_modules/@agents-can-communicate/cli/src/managed-runtime/worker.mjs +65 -0
- package/node_modules/@agents-can-communicate/cli/src/runtime-paths.mjs +1 -1
- package/node_modules/@agents-can-communicate/cli/src/session-owner.mjs +33 -108
- package/node_modules/@agents-can-communicate/cli/src/update-command.mjs +25 -7
- package/node_modules/@agents-can-communicate/core/package.json +1 -1
- package/node_modules/@agents-can-communicate/core/src/attention.mjs +3 -0
- package/node_modules/@agents-can-communicate/core/src/claims.mjs +13 -8
- package/node_modules/@agents-can-communicate/core/src/conversations.mjs +9 -25
- package/node_modules/@agents-can-communicate/core/src/decision-changes.mjs +36 -0
- package/node_modules/@agents-can-communicate/core/src/decision-state.mjs +42 -0
- package/node_modules/@agents-can-communicate/core/src/delivery-bindings.mjs +11 -4
- package/node_modules/@agents-can-communicate/core/src/inbox.mjs +48 -17
- package/node_modules/@agents-can-communicate/core/src/intents.mjs +35 -15
- package/node_modules/@agents-can-communicate/core/src/materialisation.mjs +7 -5
- package/node_modules/@agents-can-communicate/core/src/message-pages.mjs +75 -0
- package/node_modules/@agents-can-communicate/core/src/next-turn-delivery.mjs +31 -0
- package/node_modules/@agents-can-communicate/core/src/session-opening.mjs +128 -0
- package/node_modules/@agents-can-communicate/core/src/sessions.mjs +58 -132
- package/node_modules/@agents-can-communicate/core/src/sync.mjs +31 -2
- package/node_modules/@agents-can-communicate/delivery-router/package.json +1 -1
- package/node_modules/@agents-can-communicate/delivery-router/src/refresh-binding.mjs +38 -0
- package/node_modules/@agents-can-communicate/delivery-router/src/router.mjs +86 -10
- package/node_modules/@agents-can-communicate/hook-runner/package.json +1 -1
- package/node_modules/@agents-can-communicate/hook-runner/src/client-version.mjs +1 -0
- package/node_modules/@agents-can-communicate/hook-runner/src/native-binding.mjs +6 -4
- package/node_modules/@agents-can-communicate/hook-runner/src/native-retirement.mjs +48 -0
- package/node_modules/@agents-can-communicate/hook-runner/src/owner-context.mjs +30 -0
- package/node_modules/@agents-can-communicate/hook-runner/src/process-table.mjs +1 -1
- package/node_modules/@agents-can-communicate/hook-runner/src/runner.mjs +172 -67
- package/node_modules/@agents-can-communicate/hook-runner/src/session-lifecycle.mjs +13 -0
- package/node_modules/@agents-can-communicate/installer/package.json +1 -1
- package/node_modules/@agents-can-communicate/installer/src/apply.mjs +17 -2
- package/node_modules/@agents-can-communicate/installer/src/bootstrap-runtime.mjs +11 -5
- package/node_modules/@agents-can-communicate/installer/src/detect.mjs +1 -1
- package/node_modules/@agents-can-communicate/installer/src/index.mjs +1 -0
- package/node_modules/@agents-can-communicate/installer/src/live-policy.mjs +21 -0
- package/node_modules/@agents-can-communicate/installer/src/native-activation.mjs +20 -6
- package/node_modules/@agents-can-communicate/installer/src/ownership.mjs +10 -4
- package/node_modules/@agents-can-communicate/installer/src/plan.mjs +3 -3
- package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
- package/node_modules/@agents-can-communicate/mcp-server/src/protocol.mjs +85 -0
- package/node_modules/@agents-can-communicate/mcp-server/src/resources.mjs +3 -2
- package/node_modules/@agents-can-communicate/mcp-server/src/server.mjs +59 -57
- package/node_modules/@agents-can-communicate/mcp-server/src/tools.mjs +47 -33
- package/node_modules/@agents-can-communicate/protocol/package.json +1 -1
- package/node_modules/@agents-can-communicate/protocol/src/conversations.mjs +3 -0
- package/node_modules/@agents-can-communicate/protocol/src/decision-changes.mjs +15 -0
- package/node_modules/@agents-can-communicate/protocol/src/index.mjs +3 -1
- package/node_modules/@agents-can-communicate/protocol/src/schema.mjs +14 -5
- package/node_modules/@agents-can-communicate/storage-filesystem/package.json +1 -1
- package/node_modules/@agents-can-communicate/storage-filesystem/src/atomic-json.mjs +5 -1
- package/node_modules/@agents-can-communicate/storage-filesystem/src/deadline.mjs +7 -0
- package/node_modules/@agents-can-communicate/storage-filesystem/src/index.mjs +2 -0
- package/node_modules/@agents-can-communicate/storage-filesystem/src/session-read.mjs +25 -0
- package/node_modules/@agents-can-communicate/storage-filesystem/src/store.mjs +28 -25
- package/package.json +2 -1
|
@@ -6,6 +6,7 @@ import { defaultBootstrap } from "@agents-can-communicate/adapter-sdk";
|
|
|
6
6
|
|
|
7
7
|
import { installShellBootstrap, planShellBootstrap, uninstallShellBootstrap }
|
|
8
8
|
from "./shell-bootstrap.mjs";
|
|
9
|
+
import { LIVE_POLICIES, livePolicyOf } from "./live-policy.mjs";
|
|
9
10
|
|
|
10
11
|
// The installer's side of a native activation: which owned mechanisms an
|
|
11
12
|
// eligible adapter asked for, how they are applied in a fixed order, what is
|
|
@@ -14,13 +15,10 @@ import { installShellBootstrap, planShellBootstrap, uninstallShellBootstrap }
|
|
|
14
15
|
// adapter's own install; a vendor service is started only here, only during
|
|
15
16
|
// apply, and only when it did not already exist.
|
|
16
17
|
|
|
17
|
-
export
|
|
18
|
+
export { LIVE_POLICIES, livePolicyOf };
|
|
18
19
|
const MECHANISM_ORDER = ["native-config", "native-service", "shell-bootstrap"];
|
|
19
20
|
const SERVICE_TIMEOUT_MS = 15_000;
|
|
20
21
|
|
|
21
|
-
export const livePolicyOf = install => (LIVE_POLICIES.includes(install?.nativeActivation?.livePolicy)
|
|
22
|
-
? install.nativeActivation.livePolicy : "off");
|
|
23
|
-
|
|
24
22
|
export const shellOf = env => {
|
|
25
23
|
const shell = env?.SHELL;
|
|
26
24
|
return typeof shell === "string" && shell !== "" ? path.basename(shell) : null;
|
|
@@ -124,7 +122,7 @@ export async function applyNativeActivation({ adapter, activation, dataHome,
|
|
|
124
122
|
const result = await installShellBootstrap({ plan });
|
|
125
123
|
if (!result.ok) throw new Error(`shell bootstrap refused: ${result.reasonCode}`);
|
|
126
124
|
shell = result;
|
|
127
|
-
record.mechanisms.push({ kind: mechanism.kind, shimDir: activation.shimDir,
|
|
125
|
+
record.mechanisms.push({ kind: mechanism.kind, command: mechanism.command, shimDir: activation.shimDir,
|
|
128
126
|
ownedFiles: result.shims.map(shim => ({ path: shim.path, sha256: shim.sha256 })),
|
|
129
127
|
rcFile: result.rcFile });
|
|
130
128
|
}
|
|
@@ -141,12 +139,28 @@ export async function applyNativeActivation({ adapter, activation, dataHome,
|
|
|
141
139
|
}
|
|
142
140
|
}
|
|
143
141
|
|
|
142
|
+
// Compare planned mechanisms with the deletion authority saved by older installs.
|
|
143
|
+
export function planActivationRetirements({ previous, desired }) {
|
|
144
|
+
const wanted = desired?.mechanisms ?? [];
|
|
145
|
+
const matches = (old, next) => old.kind === next.kind && (old.kind === "native-service"
|
|
146
|
+
? old.serviceId === next.serviceId : old.kind === "native-config"
|
|
147
|
+
? JSON.stringify([...old.artifactIds].sort()) === JSON.stringify([...next.artifactIds].sort())
|
|
148
|
+
: typeof old.command === "string" ? old.command === next.command
|
|
149
|
+
: old.ownedFiles?.length > 0
|
|
150
|
+
&& old.ownedFiles.every(file => path.basename(file.path) === next.command));
|
|
151
|
+
return (previous?.mechanisms ?? []).filter(old => !wanted.some(next => matches(old, next)));
|
|
152
|
+
}
|
|
153
|
+
|
|
144
154
|
export async function deactivateNative({ nativeActivation, exec = defaultExec }) {
|
|
145
|
-
const report = { shell: null, services: [] };
|
|
155
|
+
const report = { shell: null, services: [], retainedMechanisms: [] };
|
|
146
156
|
for (const mechanism of nativeActivation?.mechanisms ?? []) {
|
|
147
157
|
if (mechanism.kind === "shell-bootstrap") {
|
|
148
158
|
report.shell = await uninstallShellBootstrap({ ownership: { shims: mechanism.ownedFiles,
|
|
149
159
|
shimDir: mechanism.shimDir, rcFile: mechanism.rcFile } });
|
|
160
|
+
if (report.shell.keptShims.length > 0 || report.shell.rcBlock === "modified") {
|
|
161
|
+
report.retainedMechanisms.push({ ...mechanism,
|
|
162
|
+
ownedFiles: mechanism.ownedFiles.filter(file => report.shell.keptShims.includes(file.path)) });
|
|
163
|
+
}
|
|
150
164
|
} else if (mechanism.kind === "native-service") {
|
|
151
165
|
if (mechanism.createdByAcc && mechanism.teardownCommand !== null) {
|
|
152
166
|
await exec(mechanism.teardownCommand.executable, mechanism.teardownCommand.args);
|
|
@@ -110,7 +110,7 @@ async function saveOwnership({ dataHome, record }) {
|
|
|
110
110
|
* runtime, and leaves the bundle inside the client exactly where it was.
|
|
111
111
|
*/
|
|
112
112
|
export async function recordInstall({ dataHome, adapterId, version, accVersion = null,
|
|
113
|
-
artifacts, createdDirectories = [], nativeActivation = null }) {
|
|
113
|
+
artifacts, createdDirectories = [], deliveryPolicy, nativeActivation = null }) {
|
|
114
114
|
const stamped = await Promise.all(artifacts.map(async artifact => ({
|
|
115
115
|
path: artifact.path,
|
|
116
116
|
kind: artifact.kind ?? "file",
|
|
@@ -128,6 +128,7 @@ export async function recordInstall({ dataHome, adapterId, version, accVersion =
|
|
|
128
128
|
installs: [...record.installs.filter(install => install.adapterId !== adapterId),
|
|
129
129
|
{ adapterId, version, accVersion, artifacts: stamped,
|
|
130
130
|
...(directories.length === 0 ? {} : { createdDirectories: directories }),
|
|
131
|
+
...(deliveryPolicy === undefined ? {} : { deliveryPolicy }),
|
|
131
132
|
// Present only for a consented native activation. A record without it
|
|
132
133
|
// - every 0.2 install - reads as live policy off and is never rewritten
|
|
133
134
|
// merely to add the field.
|
|
@@ -221,7 +222,10 @@ export async function verifyOwned({ dataHome, adapterId }) {
|
|
|
221
222
|
const install = installFor(await loadOwnership({ dataHome }), adapterId);
|
|
222
223
|
const result = { adapterId, present: install !== null, modified: [], missing: [],
|
|
223
224
|
intact: [], delegated: [] };
|
|
224
|
-
|
|
225
|
+
const nativeFiles = (install?.nativeActivation?.mechanisms ?? [])
|
|
226
|
+
.filter(item => item.kind === "shell-bootstrap").flatMap(item => item.ownedFiles)
|
|
227
|
+
.map(file => ({ ...file, kind: "file" }));
|
|
228
|
+
for (const artifact of [...(install?.artifacts ?? []), ...nativeFiles]) {
|
|
225
229
|
if (artifact.kind === "merge") { result.delegated.push(artifact.path); continue; }
|
|
226
230
|
const current = await fingerprintFor(artifact);
|
|
227
231
|
if (current === null) result.missing.push(artifact.path);
|
|
@@ -252,12 +256,14 @@ export async function removeOwnedArtifacts({ dataHome, adapterId }) {
|
|
|
252
256
|
}
|
|
253
257
|
|
|
254
258
|
/** Forget one install only after every adapter-owned cleanup step succeeded. */
|
|
255
|
-
export async function finalizeRemoval({ dataHome, adapterId }) {
|
|
259
|
+
export async function finalizeRemoval({ dataHome, adapterId, retainedNativeActivation = null }) {
|
|
256
260
|
const record = await loadOwnership({ dataHome });
|
|
257
261
|
if (installFor(record, adapterId) === null) return false;
|
|
258
262
|
|
|
259
263
|
await saveOwnership({ dataHome, record: { schemaVersion: SCHEMA_VERSION,
|
|
260
|
-
installs: record.installs.
|
|
264
|
+
installs: record.installs.flatMap(entry => entry.adapterId !== adapterId ? [entry]
|
|
265
|
+
: retainedNativeActivation === null ? [] : [{ ...entry, artifacts: [], createdDirectories: [],
|
|
266
|
+
deliveryPolicy: "off", nativeActivation: retainedNativeActivation }]) } });
|
|
261
267
|
return true;
|
|
262
268
|
}
|
|
263
269
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AccError, EXIT } from "@agents-can-communicate/protocol";
|
|
2
2
|
|
|
3
|
-
import { LIVE_POLICIES, describeActivation, describeDeactivation, rcFileFor, shimDirFor }
|
|
3
|
+
import { LIVE_POLICIES, describeActivation, describeDeactivation, planActivationRetirements, rcFileFor, shimDirFor }
|
|
4
4
|
from "./native-activation.mjs";
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -124,8 +124,8 @@ export function planInstallation({ adapters, detected, context, action = "instal
|
|
|
124
124
|
shimDir: typeof context?.stateRoot === "string" ? shimDirFor(context.stateRoot) : null,
|
|
125
125
|
mechanisms: native.activationPlan.mechanisms }
|
|
126
126
|
: null;
|
|
127
|
-
const
|
|
128
|
-
|
|
127
|
+
const retirements = planActivationRetirements({ previous, desired: nativeActivation });
|
|
128
|
+
const deactivation = retirements.length > 0 ? { ...previous, mechanisms: retirements } : null;
|
|
129
129
|
const artifacts = (record?.artifacts ?? adapter.planInstall(installContext))
|
|
130
130
|
.map(artifact => ({ path: artifact.path, kind: artifact.kind ?? "file" }))
|
|
131
131
|
.sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
import { MCP_CAPABILITIES } from "./tools.mjs";
|
|
4
|
+
|
|
5
|
+
export const PROTOCOL_VERSION = "2026-07-28";
|
|
6
|
+
export const INITIALIZED_VERSIONS = Object.freeze(["2025-11-25", "2025-06-18"]);
|
|
7
|
+
export const SUPPORTED_VERSIONS = Object.freeze([PROTOCOL_VERSION, ...INITIALIZED_VERSIONS]);
|
|
8
|
+
const PACKAGE_VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
9
|
+
const SERVER_INFO = Object.freeze({ name: "agents-can-communicate", version: PACKAGE_VERSION });
|
|
10
|
+
const CAPABILITIES = Object.freeze({ tools: {}, resources: {} });
|
|
11
|
+
const VERSION_KEY = "io.modelcontextprotocol/protocolVersion";
|
|
12
|
+
const CAPABILITIES_KEY = "io.modelcontextprotocol/clientCapabilities";
|
|
13
|
+
const isObject = value => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
14
|
+
const rpcError = (rpcCode, message, rpcData) =>
|
|
15
|
+
Object.assign(new Error(message), { rpcCode, rpcData });
|
|
16
|
+
|
|
17
|
+
const complete = result => ({ resultType: "complete",
|
|
18
|
+
_meta: { "io.modelcontextprotocol/serverInfo": SERVER_INFO }, ...result });
|
|
19
|
+
|
|
20
|
+
function requireProtocolMeta(params) {
|
|
21
|
+
const meta = params?._meta ?? {};
|
|
22
|
+
const version = meta[VERSION_KEY];
|
|
23
|
+
if (typeof version !== "string" || !isObject(meta[CAPABILITIES_KEY])) {
|
|
24
|
+
throw rpcError(-32602,
|
|
25
|
+
"each request requires _meta protocolVersion and clientCapabilities");
|
|
26
|
+
}
|
|
27
|
+
if (version !== PROTOCOL_VERSION) {
|
|
28
|
+
throw rpcError(-32022, `unsupported protocol version: ${version}`,
|
|
29
|
+
{ supported: [...SUPPORTED_VERSIONS], requested: version });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Dual-era stdio: 2025 clients initialize their transport; 2026 requests carry
|
|
35
|
+
* their own metadata. Only protocol readiness is connection-local. ACC identity
|
|
36
|
+
* and durable session continuity always come from the server's launch config.
|
|
37
|
+
*/
|
|
38
|
+
export function createProtocol() {
|
|
39
|
+
let phase = "uninitialized";
|
|
40
|
+
return {
|
|
41
|
+
notify(message) {
|
|
42
|
+
if (message.method === "notifications/initialized" && phase === "initializing") {
|
|
43
|
+
phase = "ready";
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
async request({ method, params }, execute) {
|
|
47
|
+
if (method === "initialize") {
|
|
48
|
+
if (phase !== "uninitialized") throw rpcError(-32600, "already initialized");
|
|
49
|
+
if (typeof params?.protocolVersion !== "string" || !isObject(params.capabilities)
|
|
50
|
+
|| !isObject(params.clientInfo) || typeof params.clientInfo.name !== "string"
|
|
51
|
+
|| typeof params.clientInfo.version !== "string") {
|
|
52
|
+
throw rpcError(-32602, "initialize requires protocolVersion, capabilities and clientInfo");
|
|
53
|
+
}
|
|
54
|
+
const protocolVersion = INITIALIZED_VERSIONS.includes(params.protocolVersion)
|
|
55
|
+
? params.protocolVersion : INITIALIZED_VERSIONS[0];
|
|
56
|
+
phase = "initializing";
|
|
57
|
+
return { protocolVersion, capabilities: CAPABILITIES, serverInfo: SERVER_INFO };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const meta = params?._meta;
|
|
61
|
+
const modern = method === "server/discover"
|
|
62
|
+
|| (isObject(meta) && (Object.hasOwn(meta, VERSION_KEY)
|
|
63
|
+
|| Object.hasOwn(meta, CAPABILITIES_KEY)));
|
|
64
|
+
if (modern) {
|
|
65
|
+
// Never inherit even one missing metadata field from an initialize.
|
|
66
|
+
requireProtocolMeta(params);
|
|
67
|
+
} else if (method !== "ping" && phase !== "ready") {
|
|
68
|
+
if (phase === "initializing") throw rpcError(-32600, "client has not sent initialized");
|
|
69
|
+
requireProtocolMeta(params);
|
|
70
|
+
}
|
|
71
|
+
const result = method === "server/discover"
|
|
72
|
+
? { supportedVersions: [...SUPPORTED_VERSIONS], capabilities: CAPABILITIES,
|
|
73
|
+
serverInfo: SERVER_INFO, accCapabilities: MCP_CAPABILITIES }
|
|
74
|
+
: method === "ping" ? {} : await execute();
|
|
75
|
+
if (modern) return complete(result);
|
|
76
|
+
// 2025 structuredContent only accepts objects. Preserve the original
|
|
77
|
+
// JSON in text content for array/scalar tools such as acc_inbox.
|
|
78
|
+
if (Object.hasOwn(result, "structuredContent") && !isObject(result.structuredContent)) {
|
|
79
|
+
const { structuredContent, ...legacy } = result;
|
|
80
|
+
return legacy;
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -43,9 +43,10 @@ export async function readResource(uri, { service, participantId, workspaceId, s
|
|
|
43
43
|
throw new AccError(EXIT.DATA, "the inbox resource requires a resolved session",
|
|
44
44
|
{ participantId });
|
|
45
45
|
}
|
|
46
|
-
const inbox = await service.
|
|
46
|
+
const inbox = await service.listInbox({ workspaceId, sessionId: session.sessionId,
|
|
47
47
|
generation: session.generation });
|
|
48
|
-
return inbox.map(item =>
|
|
48
|
+
return { ...inbox, items: inbox.items.map(item => ({ ...item,
|
|
49
|
+
message: attributedMessage(item.message) })) };
|
|
49
50
|
}
|
|
50
51
|
default:
|
|
51
52
|
throw new AccError(EXIT.DATA, `unknown resource: ${uri}`, { uri });
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
|
|
3
1
|
import { AccError, EXIT, GENERIC_MESSAGE_KINDS, VALID_OBLIGATIONS }
|
|
4
2
|
from "@agents-can-communicate/protocol";
|
|
5
3
|
import { clearSessionBinding, loadSessionBinding, storeSessionBinding }
|
|
@@ -7,52 +5,34 @@ import { clearSessionBinding, loadSessionBinding, storeSessionBinding }
|
|
|
7
5
|
|
|
8
6
|
import { readResource } from "./resources.mjs";
|
|
9
7
|
import { validateToolInput } from "./input-validator.mjs";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
export const PROTOCOL_VERSION = "2026-07-28";
|
|
13
|
-
export const SUPPORTED_VERSIONS = Object.freeze([PROTOCOL_VERSION]);
|
|
14
|
-
const PACKAGE_VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
15
|
-
const SERVER_INFO = Object.freeze({ name: "agents-can-communicate", version: PACKAGE_VERSION });
|
|
8
|
+
import { PUBLIC_TOOLS, RESOURCES } from "./tools.mjs";
|
|
9
|
+
import { createProtocol } from "./protocol.mjs";
|
|
16
10
|
|
|
17
|
-
|
|
11
|
+
export { PROTOCOL_VERSION, SUPPORTED_VERSIONS } from "./protocol.mjs";
|
|
18
12
|
const HEARTBEAT_CADENCE_MS = 60_000;
|
|
19
13
|
|
|
20
|
-
const complete = result => ({ resultType: "complete",
|
|
21
|
-
_meta: { [`${META}/serverInfo`]: SERVER_INFO }, ...result });
|
|
22
|
-
|
|
23
|
-
function requireProtocolMeta(params) {
|
|
24
|
-
const meta = params?._meta ?? {};
|
|
25
|
-
const version = meta[`${META}/protocolVersion`];
|
|
26
|
-
const capabilities = meta[`${META}/clientCapabilities`];
|
|
27
|
-
// The revision requires both on every request and mandates -32602 when one is
|
|
28
|
-
// missing. No prior request may be used to supply them.
|
|
29
|
-
if (typeof version !== "string" || capabilities === undefined) {
|
|
30
|
-
throw Object.assign(new Error(
|
|
31
|
-
"each request requires _meta protocolVersion and clientCapabilities"),
|
|
32
|
-
{ rpcCode: -32602 });
|
|
33
|
-
}
|
|
34
|
-
if (!SUPPORTED_VERSIONS.includes(version)) {
|
|
35
|
-
throw Object.assign(new Error(`unsupported protocol version: ${version}`),
|
|
36
|
-
{ rpcCode: -32022, rpcData: { supported: [...SUPPORTED_VERSIONS] } });
|
|
37
|
-
}
|
|
38
|
-
return { version, capabilities };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
14
|
/**
|
|
42
15
|
* Resolve the ACC session for this server from its own launch configuration.
|
|
43
16
|
*
|
|
44
|
-
* Approved 2026-08-16. The
|
|
17
|
+
* Approved 2026-08-16. The 2026 interface forbids treating process or
|
|
45
18
|
* connection identity as session continuity, so the session cannot be anchored
|
|
46
19
|
* to the stdio process. It is derived from the participant and workspace this
|
|
47
20
|
* server was configured with - available identically on every request - and
|
|
48
21
|
* persisted through a binding so a restarted process resolves to the same
|
|
49
22
|
* session instead of creating a second participant.
|
|
50
23
|
*/
|
|
51
|
-
async function resolveSession(context) {
|
|
24
|
+
async function resolveSession(context, { forFinish = false } = {}) {
|
|
52
25
|
const key = `mcp:${context.participantId}:${context.workspaceId}`;
|
|
53
26
|
const existing = await loadSessionBinding({ runtimeDir: context.runtimeDir,
|
|
54
27
|
harnessSessionId: key });
|
|
55
28
|
if (existing !== null) {
|
|
29
|
+
if (forFinish) {
|
|
30
|
+
const current = await context.service.locateSession(existing.accSessionId, context.workspaceId);
|
|
31
|
+
// Core validates and closes this exact generation, including retries.
|
|
32
|
+
// A prior heartbeat could race another finish and reopen a different owner.
|
|
33
|
+
if (current !== null
|
|
34
|
+
&& current.record.generation === existing.generation) return current.record;
|
|
35
|
+
}
|
|
56
36
|
try {
|
|
57
37
|
return await context.service.heartbeatSession({ sessionId: existing.accSessionId,
|
|
58
38
|
generation: existing.generation, workspaceId: context.workspaceId });
|
|
@@ -131,7 +111,7 @@ function obligationFor(kind, explicit, addressed) {
|
|
|
131
111
|
* agreeing to it.
|
|
132
112
|
*/
|
|
133
113
|
async function callTool(name, args, context) {
|
|
134
|
-
const session = await resolveSession(context);
|
|
114
|
+
const session = await resolveSession(context, { forFinish: name === "acc_finish" });
|
|
135
115
|
const owner = { sessionId: session.sessionId, generation: session.generation,
|
|
136
116
|
workspaceId: context.workspaceId, descriptor: context.descriptor };
|
|
137
117
|
const service = context.service;
|
|
@@ -141,7 +121,7 @@ async function callTool(name, args, context) {
|
|
|
141
121
|
return service.collectStatus({});
|
|
142
122
|
case "acc_sync":
|
|
143
123
|
return service.sync({ ...owner, cursor: args.cursor ?? null,
|
|
144
|
-
scope: args.scope, limit: args.limit });
|
|
124
|
+
scope: args.scope, limit: args.limit, messageId: args.messageId, kind: args.kind, current: args.current });
|
|
145
125
|
case "acc_work":
|
|
146
126
|
if (args.clear === true) {
|
|
147
127
|
await service.clearIntent({ ...owner });
|
|
@@ -164,11 +144,19 @@ async function callTool(name, args, context) {
|
|
|
164
144
|
const routed = await recordAndOffer({ router: context.deliveryRouter, record: () =>
|
|
165
145
|
service.sendMessage({ ...owner, clientMessageId: clientMessageId(args, service),
|
|
166
146
|
toParticipantIds, subject: args.subject, body: args.body, kind,
|
|
167
|
-
|
|
147
|
+
supersedes: args.supersedes, withdraws: args.withdraws,
|
|
148
|
+
obligation: obligationFor(kind, args.obligation, toParticipantIds.length > 0
|
|
149
|
+
|| args.supersedes !== undefined || args.withdraws !== undefined) }) });
|
|
168
150
|
const message = routed.recorded;
|
|
169
151
|
return { message, delivery: routed.delivery };
|
|
170
152
|
}
|
|
171
153
|
case "acc_inbox":
|
|
154
|
+
if (args.messageId === undefined) {
|
|
155
|
+
return service.listInbox({ ...owner, cursor: args.cursor, limit: args.limit });
|
|
156
|
+
}
|
|
157
|
+
if (args.cursor !== undefined || args.limit !== undefined) {
|
|
158
|
+
throw new AccError(EXIT.USAGE, "an exact inbox read cannot use cursor or limit");
|
|
159
|
+
}
|
|
172
160
|
return service.readInbox({ ...owner, messageId: args.messageId });
|
|
173
161
|
case "acc_reply": {
|
|
174
162
|
const routed = await recordAndOffer({ router: context.deliveryRouter,
|
|
@@ -176,7 +164,8 @@ async function callTool(name, args, context) {
|
|
|
176
164
|
record: () => service.replyToMessage({ ...owner, messageId: args.messageId,
|
|
177
165
|
body: args.body, subject: args.subject,
|
|
178
166
|
clientMessageId: clientMessageId(args, service) }) });
|
|
179
|
-
return { message: routed.recorded.reply,
|
|
167
|
+
return { message: routed.recorded.reply, receipt: routed.recorded.receipt,
|
|
168
|
+
delivery: routed.delivery };
|
|
180
169
|
}
|
|
181
170
|
case "acc_request": {
|
|
182
171
|
const routed = await recordAndOffer({ router: context.deliveryRouter,
|
|
@@ -204,27 +193,20 @@ async function callTool(name, args, context) {
|
|
|
204
193
|
|
|
205
194
|
async function handle(message, context) {
|
|
206
195
|
const { method, params } = message;
|
|
207
|
-
if (method === "server/discover") {
|
|
208
|
-
requireProtocolMeta(params);
|
|
209
|
-
return complete({ supportedVersions: [...SUPPORTED_VERSIONS], capabilities: {
|
|
210
|
-
tools: {}, resources: {} }, serverInfo: SERVER_INFO, accCapabilities: MCP_CAPABILITIES });
|
|
211
|
-
}
|
|
212
|
-
requireProtocolMeta(params);
|
|
213
196
|
switch (method) {
|
|
214
197
|
case "tools/list":
|
|
215
|
-
return
|
|
198
|
+
return { tools: [...PUBLIC_TOOLS] };
|
|
216
199
|
case "resources/list":
|
|
217
|
-
return
|
|
200
|
+
return { resources: [...RESOURCES] };
|
|
218
201
|
case "resources/read": {
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
// inbox service record that the returned bodies were retrieved.
|
|
202
|
+
// Inbox discovery uses this configured participant's current owner but
|
|
203
|
+
// returns summaries only. Reading this resource never retrieves bodies.
|
|
222
204
|
const resourceContext = params.uri === "acc://inbox"
|
|
223
205
|
? { ...context, session: await resolveSession(context) }
|
|
224
206
|
: context;
|
|
225
207
|
const value = await readResource(params.uri, resourceContext);
|
|
226
|
-
return
|
|
227
|
-
text: JSON.stringify(value, null, 2) }] }
|
|
208
|
+
return { contents: [{ uri: params.uri, mimeType: "application/json",
|
|
209
|
+
text: JSON.stringify(value, null, 2) }] };
|
|
228
210
|
}
|
|
229
211
|
case "tools/call": {
|
|
230
212
|
try {
|
|
@@ -235,13 +217,13 @@ async function handle(message, context) {
|
|
|
235
217
|
}
|
|
236
218
|
validateToolInput(tool.inputSchema, args);
|
|
237
219
|
const value = await callTool(params.name, args, context);
|
|
238
|
-
return
|
|
239
|
-
structuredContent: value }
|
|
220
|
+
return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
221
|
+
structuredContent: value };
|
|
240
222
|
} catch (error) {
|
|
241
223
|
// A failing operation is a tool result, not a transport failure: the
|
|
242
224
|
// model must see it and be able to react.
|
|
243
|
-
return
|
|
244
|
-
content: [{ type: "text", text: `${params.name}: ${error.message}` }] }
|
|
225
|
+
return { isError: true,
|
|
226
|
+
content: [{ type: "text", text: `${params.name}: ${error.message}` }] };
|
|
245
227
|
}
|
|
246
228
|
}
|
|
247
229
|
default:
|
|
@@ -255,6 +237,7 @@ async function handle(message, context) {
|
|
|
255
237
|
*/
|
|
256
238
|
export async function serve({ input, output, log, context }) {
|
|
257
239
|
const write = value => output.write(`${JSON.stringify(value)}\n`);
|
|
240
|
+
const protocol = createProtocol();
|
|
258
241
|
let buffer = "";
|
|
259
242
|
|
|
260
243
|
for await (const chunk of input) {
|
|
@@ -269,13 +252,32 @@ export async function serve({ input, output, log, context }) {
|
|
|
269
252
|
try {
|
|
270
253
|
message = JSON.parse(line);
|
|
271
254
|
} catch {
|
|
272
|
-
write({ jsonrpc: "2.0", error: { code: -32700, message: "parse error" } });
|
|
255
|
+
write({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error" } });
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
// Validate the envelope before accessing fields or treating it as a notification.
|
|
259
|
+
const id = typeof message?.id === "string" || typeof message?.id === "number"
|
|
260
|
+
? message.id : null;
|
|
261
|
+
if (message === null || typeof message !== "object" || Array.isArray(message)
|
|
262
|
+
|| message.jsonrpc !== "2.0" || typeof message.method !== "string"
|
|
263
|
+
|| (message.id !== undefined && message.id !== null
|
|
264
|
+
&& typeof message.id !== "string" && typeof message.id !== "number")) {
|
|
265
|
+
write({ jsonrpc: "2.0", id, error: { code: -32600, message: "invalid request" } });
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
// Once the envelope is valid, notifications get no reply, even for invalid params.
|
|
269
|
+
if (message.id === undefined || message.id === null) {
|
|
270
|
+
protocol.notify(message);
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (message.params !== undefined
|
|
274
|
+
&& (message.params === null || typeof message.params !== "object")) {
|
|
275
|
+
write({ jsonrpc: "2.0", id, error: { code: -32602, message: "invalid params" } });
|
|
273
276
|
continue;
|
|
274
277
|
}
|
|
275
|
-
// Notifications get no reply, by rule.
|
|
276
|
-
if (message.id === undefined || message.id === null) continue;
|
|
277
278
|
try {
|
|
278
|
-
write({ jsonrpc: "2.0", id: message.id,
|
|
279
|
+
write({ jsonrpc: "2.0", id: message.id,
|
|
280
|
+
result: await protocol.request(message, () => handle(message, context)) });
|
|
279
281
|
} catch (error) {
|
|
280
282
|
log?.(`${message.method}: ${error.message}`);
|
|
281
283
|
write({ jsonrpc: "2.0", id: message.id, error: {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { GENERIC_MESSAGE_KINDS } from "@agents-can-communicate/protocol";
|
|
1
|
+
import { GENERIC_MESSAGE_KINDS, MESSAGE_KINDS } from "@agents-can-communicate/protocol";
|
|
2
2
|
|
|
3
3
|
// The model-facing surface stays at a small set of high-level operations.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
5
|
+
// MCP receives by polling. Outgoing messages can use the recipient's native
|
|
6
|
+
// adapter without giving this MCP client native lifecycle, context, or guards.
|
|
7
|
+
// Polling guidance must never tell the model to repeat a mutation to read state.
|
|
8
|
+
const POLLED = "Poll for changes; MCP has no incoming push or wake.";
|
|
9
|
+
const MUTATION_POLL = "Poll acc_inbox, acc_status, or acc_sync for changes instead of repeating this mutation.";
|
|
10
|
+
const OUTGOING = "Commits first; eligible, opted-in native offers are possible. Pending messages otherwise stay queued. "
|
|
11
|
+
+ "Inspect delivery; retry with the same clientMessageId and payload.";
|
|
11
12
|
|
|
12
13
|
const object = (properties, required = []) => ({
|
|
13
14
|
type: "object",
|
|
@@ -29,21 +30,25 @@ export const PUBLIC_TOOLS = Object.freeze([
|
|
|
29
30
|
{
|
|
30
31
|
name: "acc_sync",
|
|
31
32
|
description: `Read coordination state for this workspace: roster, attention items, and `
|
|
32
|
-
+ `events since a cursor. Use scope "
|
|
33
|
-
+ `
|
|
34
|
-
+ `
|
|
33
|
+
+ `events since a cursor. Use scope "history" for bounded message summaries, then `
|
|
34
|
+
+ `messageId to read one complete historical message without changing receipts. `
|
|
35
|
+
+ `Scope "full" is an unbounded forensic workspace snapshot. Use acc_inbox for `
|
|
36
|
+
+ `your addressed messages. ${POLLED}`,
|
|
35
37
|
inputSchema: object({
|
|
36
|
-
cursor: string("
|
|
37
|
-
scope: { type: "string", enum: ["delta", "full"],
|
|
38
|
-
description: "delta
|
|
38
|
+
cursor: string("Event sequence for delta/full; nextCursor message id for history. Omit for the newest history page."),
|
|
39
|
+
scope: { type: "string", enum: ["delta", "full", "history"],
|
|
40
|
+
description: "delta: events; history: read-only message discovery; full: complete forensic snapshot." },
|
|
39
41
|
limit: { type: "integer", minimum: 1, maximum: 500,
|
|
40
|
-
description: "Maximum
|
|
42
|
+
description: "Maximum events (default 100), or history summaries (default 20, also byte-bounded)." },
|
|
43
|
+
kind: { type: "string", enum: [...MESSAGE_KINDS], description: "Filter history before paging." },
|
|
44
|
+
current: { type: "boolean", description: "With history and kind decision, list only terminal decisions/withdrawals; conflicts remain visible." },
|
|
45
|
+
messageId: string("Read this complete history message; requires history scope and no list controls."),
|
|
41
46
|
}),
|
|
42
47
|
},
|
|
43
48
|
{
|
|
44
49
|
name: "acc_work",
|
|
45
50
|
description: `Publish what this session is doing now as one concise Intent. Intent is `
|
|
46
|
-
+ `awareness, not authorisation: it never reserves a resource. ${
|
|
51
|
+
+ `awareness, not authorisation: it never reserves a resource. ${MUTATION_POLL}`,
|
|
47
52
|
inputSchema: { ...object({
|
|
48
53
|
summary: string("One line describing the current work."),
|
|
49
54
|
mode: { type: "string",
|
|
@@ -63,7 +68,7 @@ export const PUBLIC_TOOLS = Object.freeze([
|
|
|
63
68
|
name: "acc_claim",
|
|
64
69
|
description: `Acquire or renew a claim on a resource URI. Claims are `
|
|
65
70
|
+ `workspace-wide and advisory here: this client has no write guard, so a claim `
|
|
66
|
-
+ `informs peers rather than preventing an edit. ${
|
|
71
|
+
+ `informs peers rather than preventing an edit. ${MUTATION_POLL}`,
|
|
67
72
|
inputSchema: { ...object({
|
|
68
73
|
resource: string("Resource URI, for example file:packages/core/**."),
|
|
69
74
|
action: { type: "string", enum: ["acquire", "renew"] },
|
|
@@ -82,15 +87,14 @@ export const PUBLIC_TOOLS = Object.freeze([
|
|
|
82
87
|
},
|
|
83
88
|
{
|
|
84
89
|
name: "acc_release",
|
|
85
|
-
description: `Release a claim this session owns. ${
|
|
90
|
+
description: `Release a claim this session owns. ${MUTATION_POLL}`,
|
|
86
91
|
inputSchema: object({
|
|
87
92
|
claimId: string("The claim to release."),
|
|
88
93
|
}, ["claimId"]),
|
|
89
94
|
},
|
|
90
95
|
{
|
|
91
96
|
name: "acc_message",
|
|
92
|
-
description: `Durably record a typed message to other participants.
|
|
93
|
-
+ `when they next poll; there is no push guarantee and no wake. ${POLLED}`,
|
|
97
|
+
description: `Durably record a typed message to other participants. ${OUTGOING} ${MUTATION_POLL}`,
|
|
94
98
|
inputSchema: object({
|
|
95
99
|
to: stringList("Recipient participant ids."),
|
|
96
100
|
subject: string("Short subject line."),
|
|
@@ -99,28 +103,38 @@ export const PUBLIC_TOOLS = Object.freeze([
|
|
|
99
103
|
enum: [...GENERIC_MESSAGE_KINDS] },
|
|
100
104
|
obligation: { type: "string", enum: ["none", "acknowledge", "reply"],
|
|
101
105
|
description: "Override only where the kind/obligation matrix permits it." },
|
|
102
|
-
clientMessageId: string("Retry key;
|
|
106
|
+
clientMessageId: string("Retry key; supply before sending if needed after a lost response. Omit to generate one returned in message."),
|
|
107
|
+
supersedes: { ...stringList("Decision IDs this new decision replaces. Inherits recipients and authors. Exclusive with withdraws."),
|
|
108
|
+
minItems: 1, maxItems: 16, uniqueItems: true },
|
|
109
|
+
withdraws: { ...stringList("Decision IDs explicitly withdrawn, with body as reason. Decision kind only; inherits recipients and authors."),
|
|
110
|
+
minItems: 1, maxItems: 16, uniqueItems: true },
|
|
103
111
|
}, ["to", "subject", "body"]),
|
|
104
112
|
},
|
|
105
113
|
{
|
|
106
114
|
name: "acc_inbox",
|
|
107
|
-
description: `
|
|
108
|
-
+ `
|
|
115
|
+
description: `List a bounded page of unresolved message summaries, newest first, without `
|
|
116
|
+
+ `changing receipts. Replaced decisions remain available by exact ID and history. Use nextCursor for older entries; omit it to see new arrivals. `
|
|
117
|
+
+ `Use messageId to retrieve exactly one complete addressed message. An acknowledged `
|
|
118
|
+
+ `message can be inspected without changing its receipt. `
|
|
109
119
|
+ `${POLLED}`,
|
|
110
120
|
inputSchema: object({
|
|
111
|
-
messageId: string("Read
|
|
121
|
+
messageId: string("Read this complete addressed message; omit for read-only summaries. Cannot combine with cursor/limit."),
|
|
122
|
+
cursor: string("nextCursor from a previous inbox page; omit for the newest pending messages."),
|
|
123
|
+
limit: { type: "integer", minimum: 1, maximum: 500,
|
|
124
|
+
description: "Maximum summaries, default 20; pages are also byte-bounded." },
|
|
112
125
|
}),
|
|
113
126
|
},
|
|
114
127
|
{
|
|
115
128
|
name: "acc_reply",
|
|
116
129
|
description: `Reply to one addressed message and acknowledge the original in the same `
|
|
117
|
-
+ `operation. The reply is attributed
|
|
118
|
-
+
|
|
130
|
+
+ `operation. The reply is attributed and linked with inReplyTo. `
|
|
131
|
+
+ `Returns message and delivery for the outgoing reply, plus receipt for the original. `
|
|
132
|
+
+ `${OUTGOING} ${MUTATION_POLL}`,
|
|
119
133
|
inputSchema: object({
|
|
120
134
|
messageId: string("The addressed message being answered."),
|
|
121
135
|
body: string("Concise answer; peer content is treated as data."),
|
|
122
136
|
subject: string("Optional subject; defaults to Re: the original subject."),
|
|
123
|
-
clientMessageId: string("Retry key;
|
|
137
|
+
clientMessageId: string("Retry key; supply before sending if needed after a lost response. Omit to generate one returned in message."),
|
|
124
138
|
}, ["messageId", "body"]),
|
|
125
139
|
},
|
|
126
140
|
{
|
|
@@ -128,18 +142,18 @@ export const PUBLIC_TOOLS = Object.freeze([
|
|
|
128
142
|
description: `Ask another agent to do something in a reply-required message. Use this `
|
|
129
143
|
+ `when you need a piece finished that is `
|
|
130
144
|
+ `not yours to do - a review, a port, tests for something you just wrote. `
|
|
131
|
-
+ `${
|
|
145
|
+
+ `${OUTGOING} ${MUTATION_POLL}`,
|
|
132
146
|
inputSchema: object({
|
|
133
147
|
toParticipantId: string("The agent being asked."),
|
|
134
148
|
title: string("What needs doing, in one line."),
|
|
135
149
|
detail: string("Context the other agent needs to start."),
|
|
136
|
-
clientMessageId: string("Retry key;
|
|
150
|
+
clientMessageId: string("Retry key; supply before sending if needed after a lost response. Omit to generate one returned in message."),
|
|
137
151
|
}, ["toParticipantId", "title"]),
|
|
138
152
|
},
|
|
139
153
|
{
|
|
140
154
|
name: "acc_ack",
|
|
141
155
|
description: `Answer a message that asked for an acknowledgement, so it stops `
|
|
142
|
-
+ `demanding one. ${
|
|
156
|
+
+ `demanding one. ${MUTATION_POLL}`,
|
|
143
157
|
inputSchema: object({
|
|
144
158
|
messageId: string("The message being answered."),
|
|
145
159
|
}, ["messageId"]),
|
|
@@ -148,7 +162,7 @@ export const PUBLIC_TOOLS = Object.freeze([
|
|
|
148
162
|
name: "acc_finish",
|
|
149
163
|
description: `Record a handoff describing what was completed and what remains, and `
|
|
150
164
|
+ `release the claims this session owns. Call it while still working, not after: `
|
|
151
|
-
+ `nothing else writes the summary for you. ${
|
|
165
|
+
+ `nothing else writes the summary for you. ${OUTGOING} ${MUTATION_POLL}`,
|
|
152
166
|
inputSchema: object({
|
|
153
167
|
goal: string("What this stretch of work was for."),
|
|
154
168
|
status: { type: "string", enum: ["complete", "partial", "blocked"] },
|
|
@@ -156,7 +170,7 @@ export const PUBLIC_TOOLS = Object.freeze([
|
|
|
156
170
|
remaining: stringList("What is left."),
|
|
157
171
|
blockers: stringList("What is in the way."),
|
|
158
172
|
toParticipantId: string("Participant taking over, if any."),
|
|
159
|
-
clientMessageId: string("Retry key;
|
|
173
|
+
clientMessageId: string("Retry key; supply before sending if needed after a lost response. Omit to generate one returned in message."),
|
|
160
174
|
}, ["goal"]),
|
|
161
175
|
},
|
|
162
176
|
]);
|
|
@@ -165,9 +179,9 @@ export const RESOURCES = Object.freeze([
|
|
|
165
179
|
{ uri: "acc://snapshot", name: "Workspace snapshot", mimeType: "application/json",
|
|
166
180
|
description: "The whole coordination state: participants, intents, claims, and messages." },
|
|
167
181
|
{ uri: "acc://roster", name: "Participant roster", mimeType: "application/json",
|
|
168
|
-
description: "Sessions with their harness and presence
|
|
182
|
+
description: "Sessions with their harness and presence." },
|
|
169
183
|
{ uri: "acc://inbox", name: "Inbox", mimeType: "application/json",
|
|
170
|
-
description: "
|
|
184
|
+
description: "Read-only pending message headers. Continue with acc_inbox cursor; retrieve a body with messageId." },
|
|
171
185
|
]);
|
|
172
186
|
|
|
173
187
|
// Declared, not assumed. Manual MCP tool polling is not next-turn injection,
|