agent-comms 3.10.0 → 3.11.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/bridges/user/tui.js +2 -0
- package/dist/bridges/user/tui.js.map +1 -1
- package/dist/core/bridge.d.ts +7 -0
- package/dist/core/bridge.d.ts.map +1 -1
- package/dist/core/bridge.js +36 -0
- package/dist/core/bridge.js.map +1 -1
- package/dist/core/capability-ask.d.ts +51 -0
- package/dist/core/capability-ask.d.ts.map +1 -0
- package/dist/core/capability-ask.js +92 -0
- package/dist/core/capability-ask.js.map +1 -0
- package/dist/core/mcp-registry-retry.d.ts +1 -1
- package/dist/core/mcp-registry-retry.d.ts.map +1 -1
- package/dist/core/mcp-registry-retry.js +15 -2
- package/dist/core/mcp-registry-retry.js.map +1 -1
- package/dist/core/mesh-store.d.ts +25 -0
- package/dist/core/mesh-store.d.ts.map +1 -1
- package/dist/core/mesh-store.js +35 -0
- package/dist/core/mesh-store.js.map +1 -1
- package/dist/core/tool.d.ts +16 -0
- package/dist/core/tool.d.ts.map +1 -1
- package/dist/core/tool.js +44 -0
- package/dist/core/tool.js.map +1 -1
- package/dist/core/types.d.ts +38 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +21 -0
- package/dist/core/types.js.map +1 -1
- package/dist/test/capability-admission-tool-actions.test.d.ts +5 -0
- package/dist/test/capability-admission-tool-actions.test.d.ts.map +1 -0
- package/dist/test/capability-admission-tool-actions.test.js +198 -0
- package/dist/test/capability-admission-tool-actions.test.js.map +1 -0
- package/dist/test/capability-ask.test.d.ts +2 -0
- package/dist/test/capability-ask.test.d.ts.map +1 -0
- package/dist/test/capability-ask.test.js +300 -0
- package/dist/test/capability-ask.test.js.map +1 -0
- package/dist/test/mcp-registry-retry.test.js +16 -0
- package/dist/test/mcp-registry-retry.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the capability_pending/capability_accept/capability_reject CommsTool actions -- the human-facing wrapper around MeshStore's own createCapabilityAskHandler/listPendingCapabilityRequests/acceptCapabilityRequest/rejectCapabilityRequest (agent-comms#165), mirroring room-admission-tool-actions.test.ts's own real-MeshStore integration style rather than a fully faked harness.
|
|
3
|
+
*/
|
|
4
|
+
import { webcrypto } from "node:crypto";
|
|
5
|
+
import { describe, it, expect } from "vitest";
|
|
6
|
+
import { createNodeIdentity } from "wire-mesh-core/adapters/node-identity";
|
|
7
|
+
import { verifyCapabilityToken } from "wire-mesh-core/domain/tokens";
|
|
8
|
+
import { createRevocationView } from "wire-mesh-core/domain/revocation-view";
|
|
9
|
+
import { MeshStore } from "../core/mesh-store.js";
|
|
10
|
+
import { CommsTool } from "../core/tool.js";
|
|
11
|
+
import { buildAction } from "../core/bridge.js";
|
|
12
|
+
import { toIdentityPort } from "../core/wire-mesh-identity.js";
|
|
13
|
+
import { loadOrCreateIdentity } from "../core/identity-store.js";
|
|
14
|
+
import { wireTestTransport } from "./test-transport.js";
|
|
15
|
+
const ES256 = -7;
|
|
16
|
+
const HOUR_MS = 3_600_000;
|
|
17
|
+
const CAPABILITY = "dm:send";
|
|
18
|
+
async function generateEs256Identity() {
|
|
19
|
+
const keyPair = await webcrypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
|
|
20
|
+
const publicKeyBytes = new Uint8Array(await webcrypto.subtle.exportKey("raw", keyPair.publicKey));
|
|
21
|
+
return createNodeIdentity(keyPair.privateKey, publicKeyBytes, ES256);
|
|
22
|
+
}
|
|
23
|
+
function fakeCapabilityRequest() {
|
|
24
|
+
return {
|
|
25
|
+
requestId: 1,
|
|
26
|
+
command: {
|
|
27
|
+
verb: CAPABILITY,
|
|
28
|
+
params: { verb: "capability.request", capability: CAPABILITY },
|
|
29
|
+
},
|
|
30
|
+
scope: { kind: "user" },
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
describe("capability admission CommsTool actions", () => {
|
|
34
|
+
it("capability_pending lists a held-open capability request", async () => {
|
|
35
|
+
const store = new MeshStore();
|
|
36
|
+
await wireTestTransport(store);
|
|
37
|
+
const owner = await store.registerAgent({
|
|
38
|
+
name: "owner",
|
|
39
|
+
harness: "pi",
|
|
40
|
+
cwd: "/tmp/p",
|
|
41
|
+
pid: process.pid,
|
|
42
|
+
visibility: "visible",
|
|
43
|
+
tags: [],
|
|
44
|
+
});
|
|
45
|
+
const requester = await generateEs256Identity();
|
|
46
|
+
const handler = store.createCapabilityAskHandler({
|
|
47
|
+
capability: CAPABILITY,
|
|
48
|
+
bearerDevice: requester.deviceId,
|
|
49
|
+
timeoutMs: HOUR_MS,
|
|
50
|
+
});
|
|
51
|
+
void handler({
|
|
52
|
+
...fakeCapabilityRequest(),
|
|
53
|
+
respond: async () => { },
|
|
54
|
+
});
|
|
55
|
+
const tool = new CommsTool(store);
|
|
56
|
+
const ctx = {
|
|
57
|
+
agentId: owner.id,
|
|
58
|
+
harness: "pi",
|
|
59
|
+
cwd: "/tmp/p",
|
|
60
|
+
pid: process.pid,
|
|
61
|
+
};
|
|
62
|
+
const pendingResult = await tool.handle(ctx, buildAction({ action: "capability_pending" }));
|
|
63
|
+
expect(pendingResult.isError).toBe(false);
|
|
64
|
+
expect(pendingResult.content.includes(CAPABILITY)).toBeTruthy();
|
|
65
|
+
});
|
|
66
|
+
it("capability_accept mints and returns a granted token to the requester", async () => {
|
|
67
|
+
const store = new MeshStore();
|
|
68
|
+
const slot = await wireTestTransport(store);
|
|
69
|
+
const owner = await store.registerAgent({
|
|
70
|
+
name: "owner",
|
|
71
|
+
harness: "pi",
|
|
72
|
+
cwd: "/tmp/p",
|
|
73
|
+
pid: process.pid,
|
|
74
|
+
visibility: "visible",
|
|
75
|
+
tags: [],
|
|
76
|
+
});
|
|
77
|
+
const requester = await generateEs256Identity();
|
|
78
|
+
const handler = store.createCapabilityAskHandler({
|
|
79
|
+
capability: CAPABILITY,
|
|
80
|
+
bearerDevice: requester.deviceId,
|
|
81
|
+
timeoutMs: HOUR_MS,
|
|
82
|
+
});
|
|
83
|
+
let respondedWith;
|
|
84
|
+
const outcomePromise = handler({
|
|
85
|
+
...fakeCapabilityRequest(),
|
|
86
|
+
respond: async (outcome) => {
|
|
87
|
+
respondedWith = outcome;
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
const tool = new CommsTool(store);
|
|
91
|
+
const ctx = {
|
|
92
|
+
agentId: owner.id,
|
|
93
|
+
harness: "pi",
|
|
94
|
+
cwd: "/tmp/p",
|
|
95
|
+
pid: process.pid,
|
|
96
|
+
};
|
|
97
|
+
const requestId = store.listPendingCapabilityRequests()[0]?.requestId;
|
|
98
|
+
if (requestId === undefined)
|
|
99
|
+
throw new Error("no pending request found");
|
|
100
|
+
const expires = Date.now() + HOUR_MS;
|
|
101
|
+
const acceptResult = await tool.handle(ctx, buildAction({
|
|
102
|
+
action: "capability_accept",
|
|
103
|
+
requestId,
|
|
104
|
+
expires,
|
|
105
|
+
}));
|
|
106
|
+
expect(acceptResult.isError).toBe(false);
|
|
107
|
+
await outcomePromise;
|
|
108
|
+
const outcome = respondedWith;
|
|
109
|
+
expect(outcome.result).toBe("ok");
|
|
110
|
+
const identity = await toIdentityPort(loadOrCreateIdentity(slot));
|
|
111
|
+
const verdict = await verifyCapabilityToken(outcome["granted-token"], {
|
|
112
|
+
identity,
|
|
113
|
+
clock: { now: () => Date.now() },
|
|
114
|
+
revocation: createRevocationView(),
|
|
115
|
+
expectedBearer: requester.deviceId,
|
|
116
|
+
});
|
|
117
|
+
expect(verdict.ok).toBe(true);
|
|
118
|
+
if (verdict.ok) {
|
|
119
|
+
expect(verdict.claims.capability).toBe(CAPABILITY);
|
|
120
|
+
}
|
|
121
|
+
const pendingAfter = await tool.handle(ctx, buildAction({ action: "capability_pending" }));
|
|
122
|
+
expect(pendingAfter.content).toBe("No pending capability requests.");
|
|
123
|
+
});
|
|
124
|
+
it("capability_reject denies the pending request with an optional reason", async () => {
|
|
125
|
+
const store = new MeshStore();
|
|
126
|
+
await wireTestTransport(store);
|
|
127
|
+
const owner = await store.registerAgent({
|
|
128
|
+
name: "owner",
|
|
129
|
+
harness: "pi",
|
|
130
|
+
cwd: "/tmp/p",
|
|
131
|
+
pid: process.pid,
|
|
132
|
+
visibility: "visible",
|
|
133
|
+
tags: [],
|
|
134
|
+
});
|
|
135
|
+
const requester = await generateEs256Identity();
|
|
136
|
+
const handler = store.createCapabilityAskHandler({
|
|
137
|
+
capability: CAPABILITY,
|
|
138
|
+
bearerDevice: requester.deviceId,
|
|
139
|
+
timeoutMs: HOUR_MS,
|
|
140
|
+
});
|
|
141
|
+
let respondedWith;
|
|
142
|
+
const outcomePromise = handler({
|
|
143
|
+
...fakeCapabilityRequest(),
|
|
144
|
+
respond: async (outcome) => {
|
|
145
|
+
respondedWith = outcome;
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
const tool = new CommsTool(store);
|
|
149
|
+
const ctx = {
|
|
150
|
+
agentId: owner.id,
|
|
151
|
+
harness: "pi",
|
|
152
|
+
cwd: "/tmp/p",
|
|
153
|
+
pid: process.pid,
|
|
154
|
+
};
|
|
155
|
+
const pendingList = store.listPendingCapabilityRequests();
|
|
156
|
+
const requestId = pendingList[0]?.requestId;
|
|
157
|
+
if (requestId === undefined)
|
|
158
|
+
throw new Error("no pending request found");
|
|
159
|
+
const rejectResult = await tool.handle(ctx, buildAction({
|
|
160
|
+
action: "capability_reject",
|
|
161
|
+
requestId,
|
|
162
|
+
reason: "not now",
|
|
163
|
+
}));
|
|
164
|
+
expect(rejectResult.isError).toBe(false);
|
|
165
|
+
await outcomePromise;
|
|
166
|
+
expect(respondedWith).toEqual({
|
|
167
|
+
result: "error",
|
|
168
|
+
code: "denied",
|
|
169
|
+
message: "not now",
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
it("capability_accept on a store with no matching pending request reports failure, not a crash", async () => {
|
|
173
|
+
const store = new MeshStore();
|
|
174
|
+
await wireTestTransport(store);
|
|
175
|
+
const owner = await store.registerAgent({
|
|
176
|
+
name: "owner",
|
|
177
|
+
harness: "pi",
|
|
178
|
+
cwd: "/tmp/p",
|
|
179
|
+
pid: process.pid,
|
|
180
|
+
visibility: "visible",
|
|
181
|
+
tags: [],
|
|
182
|
+
});
|
|
183
|
+
const tool = new CommsTool(store);
|
|
184
|
+
const ctx = {
|
|
185
|
+
agentId: owner.id,
|
|
186
|
+
harness: "pi",
|
|
187
|
+
cwd: "/tmp/p",
|
|
188
|
+
pid: process.pid,
|
|
189
|
+
};
|
|
190
|
+
const result = await tool.handle(ctx, buildAction({
|
|
191
|
+
action: "capability_accept",
|
|
192
|
+
requestId: "not-a-real-request",
|
|
193
|
+
expires: Date.now() + HOUR_MS,
|
|
194
|
+
}));
|
|
195
|
+
expect(result.isError).toBe(true);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
//# sourceMappingURL=capability-admission-tool-actions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-admission-tool-actions.test.js","sourceRoot":"","sources":["../../src/test/capability-admission-tool-actions.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-ask.test.d.ts","sourceRoot":"","sources":["../../src/test/capability-ask.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct, DI-based unit tests for CapabilityAskAdmission -- mirrors connection-approval.test.ts's own approach (a narrow, injectable deps surface, fake collaborators via vi.fn()) so every branch of the ask tier's tool-layer surfacing (agent-comms#165) is asserted on directly rather than only indirectly through a live transport.
|
|
3
|
+
*/
|
|
4
|
+
import { webcrypto } from "node:crypto";
|
|
5
|
+
import { describe, it, expect, vi } from "vitest";
|
|
6
|
+
import { createNodeIdentity } from "wire-mesh-core/adapters/node-identity";
|
|
7
|
+
import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
|
|
8
|
+
import { verifyCapabilityToken } from "wire-mesh-core/domain/tokens";
|
|
9
|
+
import { createRevocationView } from "wire-mesh-core/domain/revocation-view";
|
|
10
|
+
import { CapabilityAskAdmission, } from "../core/capability-ask.js";
|
|
11
|
+
const ES256 = -7;
|
|
12
|
+
const NOW_MS = 1_893_456_000_000;
|
|
13
|
+
const HOUR_MS = 3_600_000;
|
|
14
|
+
const EXPIRES_MS = NOW_MS + HOUR_MS;
|
|
15
|
+
const CAPABILITY = "dm:send";
|
|
16
|
+
const SCOPE = { kind: "user" };
|
|
17
|
+
const OWNER_ID = "owner-peer";
|
|
18
|
+
async function generateEs256Identity() {
|
|
19
|
+
const keyPair = await webcrypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
|
|
20
|
+
const publicKeyBytes = new Uint8Array(await webcrypto.subtle.exportKey("raw", keyPair.publicKey));
|
|
21
|
+
return createNodeIdentity(keyPair.privateKey, publicKeyBytes, ES256);
|
|
22
|
+
}
|
|
23
|
+
function fixedClock(atMs) {
|
|
24
|
+
return { now: () => atMs };
|
|
25
|
+
}
|
|
26
|
+
function makeHarness(options = {}) {
|
|
27
|
+
const queueDelivery = vi.fn();
|
|
28
|
+
const onDelivery = options.withOnDelivery === true ? vi.fn() : undefined;
|
|
29
|
+
const deps = {
|
|
30
|
+
getPeerId: () => OWNER_ID,
|
|
31
|
+
getOnDelivery: () => onDelivery,
|
|
32
|
+
queueDelivery,
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
deps,
|
|
36
|
+
admission: new CapabilityAskAdmission(deps),
|
|
37
|
+
queueDelivery,
|
|
38
|
+
onDelivery,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
let nextRequestId = 0;
|
|
42
|
+
function fakeIncoming(params) {
|
|
43
|
+
nextRequestId += 1;
|
|
44
|
+
return {
|
|
45
|
+
requestId: nextRequestId,
|
|
46
|
+
command: { verb: CAPABILITY, params },
|
|
47
|
+
scope: SCOPE,
|
|
48
|
+
respond: vi.fn(async () => undefined),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
describe("CapabilityAskAdmission — createAskHandler", () => {
|
|
52
|
+
it("holds the request open, queues a capability_request delivery event, and touches neither respond nor onDelivery's own callback synchronously", async () => {
|
|
53
|
+
const h = makeHarness({ withOnDelivery: true });
|
|
54
|
+
const identity = await generateEs256Identity();
|
|
55
|
+
const requester = await generateEs256Identity();
|
|
56
|
+
const handler = h.admission.createAskHandler({
|
|
57
|
+
capability: CAPABILITY,
|
|
58
|
+
identity,
|
|
59
|
+
clock: fixedClock(NOW_MS),
|
|
60
|
+
bearerDevice: requester.deviceId,
|
|
61
|
+
timeoutMs: HOUR_MS,
|
|
62
|
+
});
|
|
63
|
+
const incoming = fakeIncoming({
|
|
64
|
+
verb: "capability.request",
|
|
65
|
+
capability: CAPABILITY,
|
|
66
|
+
});
|
|
67
|
+
// The handler awaits only until onRequest is called, never until decide() settles -- the ask stays held open, so this resolves without ever calling respond().
|
|
68
|
+
await handler(incoming);
|
|
69
|
+
expect(incoming.respond).not.toHaveBeenCalled();
|
|
70
|
+
expect(h.queueDelivery).toHaveBeenCalledTimes(1);
|
|
71
|
+
const [deliveredTo, event] = h.queueDelivery.mock.calls[0];
|
|
72
|
+
expect(deliveredTo).toBe(OWNER_ID);
|
|
73
|
+
expect(event).toEqual({
|
|
74
|
+
type: "capability_request",
|
|
75
|
+
requestId: expect.any(String),
|
|
76
|
+
capability: CAPABILITY,
|
|
77
|
+
scopeKind: "user",
|
|
78
|
+
requesterDevice: deviceIdToHex(requester.deviceId),
|
|
79
|
+
});
|
|
80
|
+
expect(h.onDelivery).toHaveBeenCalledTimes(1);
|
|
81
|
+
});
|
|
82
|
+
it("includes scopePath in the delivery event when the incoming request's own scope carries one", async () => {
|
|
83
|
+
const h = makeHarness();
|
|
84
|
+
const identity = await generateEs256Identity();
|
|
85
|
+
const requester = await generateEs256Identity();
|
|
86
|
+
const handler = h.admission.createAskHandler({
|
|
87
|
+
capability: CAPABILITY,
|
|
88
|
+
identity,
|
|
89
|
+
clock: fixedClock(NOW_MS),
|
|
90
|
+
bearerDevice: requester.deviceId,
|
|
91
|
+
timeoutMs: HOUR_MS,
|
|
92
|
+
});
|
|
93
|
+
nextRequestId += 1;
|
|
94
|
+
const incoming = {
|
|
95
|
+
requestId: nextRequestId,
|
|
96
|
+
command: {
|
|
97
|
+
verb: CAPABILITY,
|
|
98
|
+
params: { verb: "capability.request", capability: CAPABILITY },
|
|
99
|
+
},
|
|
100
|
+
scope: { kind: "room", path: "abc/general" },
|
|
101
|
+
respond: vi.fn(async () => undefined),
|
|
102
|
+
};
|
|
103
|
+
await handler(incoming);
|
|
104
|
+
const [, event] = h.queueDelivery.mock.calls[0];
|
|
105
|
+
expect(event).toMatchObject({
|
|
106
|
+
scopeKind: "room",
|
|
107
|
+
scopePath: "abc/general",
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
it("responds malformed for a request naming a different capability, and never surfaces it as a pending ask", async () => {
|
|
111
|
+
const h = makeHarness();
|
|
112
|
+
const identity = await generateEs256Identity();
|
|
113
|
+
const requester = await generateEs256Identity();
|
|
114
|
+
const handler = h.admission.createAskHandler({
|
|
115
|
+
capability: CAPABILITY,
|
|
116
|
+
identity,
|
|
117
|
+
clock: fixedClock(NOW_MS),
|
|
118
|
+
bearerDevice: requester.deviceId,
|
|
119
|
+
timeoutMs: HOUR_MS,
|
|
120
|
+
});
|
|
121
|
+
const incoming = fakeIncoming({
|
|
122
|
+
verb: "capability.request",
|
|
123
|
+
capability: "room:member",
|
|
124
|
+
});
|
|
125
|
+
await handler(incoming);
|
|
126
|
+
expect(incoming.respond).toHaveBeenCalledWith({
|
|
127
|
+
result: "error",
|
|
128
|
+
code: "malformed",
|
|
129
|
+
});
|
|
130
|
+
expect(h.queueDelivery).not.toHaveBeenCalled();
|
|
131
|
+
expect(h.admission.listPendingCapabilityRequests()).toEqual([]);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe("CapabilityAskAdmission — listPendingCapabilityRequests / acceptCapabilityRequest / rejectCapabilityRequest", () => {
|
|
135
|
+
it("lists every still-held ask with its capability, scope, and requester", async () => {
|
|
136
|
+
const h = makeHarness();
|
|
137
|
+
const identity = await generateEs256Identity();
|
|
138
|
+
const requester = await generateEs256Identity();
|
|
139
|
+
const handler = h.admission.createAskHandler({
|
|
140
|
+
capability: CAPABILITY,
|
|
141
|
+
identity,
|
|
142
|
+
clock: fixedClock(NOW_MS),
|
|
143
|
+
bearerDevice: requester.deviceId,
|
|
144
|
+
timeoutMs: HOUR_MS,
|
|
145
|
+
});
|
|
146
|
+
await handler(fakeIncoming({ verb: "capability.request", capability: CAPABILITY }));
|
|
147
|
+
expect(h.admission.listPendingCapabilityRequests()).toEqual([
|
|
148
|
+
{
|
|
149
|
+
requestId: expect.any(String),
|
|
150
|
+
capability: CAPABILITY,
|
|
151
|
+
scopeKind: "user",
|
|
152
|
+
requesterDevice: deviceIdToHex(requester.deviceId),
|
|
153
|
+
},
|
|
154
|
+
]);
|
|
155
|
+
});
|
|
156
|
+
it("accepting a pending ask mints a granted token, responds ok, and removes it from the pending list", async () => {
|
|
157
|
+
const h = makeHarness();
|
|
158
|
+
const identity = await generateEs256Identity();
|
|
159
|
+
const requester = await generateEs256Identity();
|
|
160
|
+
const handler = h.admission.createAskHandler({
|
|
161
|
+
capability: CAPABILITY,
|
|
162
|
+
identity,
|
|
163
|
+
clock: fixedClock(NOW_MS),
|
|
164
|
+
bearerDevice: requester.deviceId,
|
|
165
|
+
timeoutMs: HOUR_MS,
|
|
166
|
+
});
|
|
167
|
+
const incoming = fakeIncoming({
|
|
168
|
+
verb: "capability.request",
|
|
169
|
+
capability: CAPABILITY,
|
|
170
|
+
});
|
|
171
|
+
await handler(incoming);
|
|
172
|
+
const [requestId] = h.admission
|
|
173
|
+
.listPendingCapabilityRequests()
|
|
174
|
+
.map((p) => p.requestId);
|
|
175
|
+
if (requestId === undefined)
|
|
176
|
+
throw new Error("no pending request");
|
|
177
|
+
await h.admission.acceptCapabilityRequest(requestId, {
|
|
178
|
+
expires: EXPIRES_MS,
|
|
179
|
+
delegationsRemaining: 1,
|
|
180
|
+
});
|
|
181
|
+
expect(incoming.respond).toHaveBeenCalledTimes(1);
|
|
182
|
+
const outcome = incoming.respond.mock.calls[0]?.[0];
|
|
183
|
+
expect(outcome.result).toBe("ok");
|
|
184
|
+
const verdict = await verifyCapabilityToken(outcome["granted-token"], {
|
|
185
|
+
identity,
|
|
186
|
+
clock: fixedClock(NOW_MS),
|
|
187
|
+
revocation: createRevocationView(),
|
|
188
|
+
expectedBearer: requester.deviceId,
|
|
189
|
+
});
|
|
190
|
+
expect(verdict.ok).toBe(true);
|
|
191
|
+
if (verdict.ok) {
|
|
192
|
+
expect(verdict.claims.capability).toBe(CAPABILITY);
|
|
193
|
+
}
|
|
194
|
+
expect(h.admission.listPendingCapabilityRequests()).toEqual([]);
|
|
195
|
+
});
|
|
196
|
+
it("rejecting a pending ask responds denied with the given reason and removes it from the pending list", async () => {
|
|
197
|
+
const h = makeHarness();
|
|
198
|
+
const identity = await generateEs256Identity();
|
|
199
|
+
const requester = await generateEs256Identity();
|
|
200
|
+
const handler = h.admission.createAskHandler({
|
|
201
|
+
capability: CAPABILITY,
|
|
202
|
+
identity,
|
|
203
|
+
clock: fixedClock(NOW_MS),
|
|
204
|
+
bearerDevice: requester.deviceId,
|
|
205
|
+
timeoutMs: HOUR_MS,
|
|
206
|
+
});
|
|
207
|
+
const incoming = fakeIncoming({
|
|
208
|
+
verb: "capability.request",
|
|
209
|
+
capability: CAPABILITY,
|
|
210
|
+
});
|
|
211
|
+
await handler(incoming);
|
|
212
|
+
const [requestId] = h.admission
|
|
213
|
+
.listPendingCapabilityRequests()
|
|
214
|
+
.map((p) => p.requestId);
|
|
215
|
+
if (requestId === undefined)
|
|
216
|
+
throw new Error("no pending request");
|
|
217
|
+
await h.admission.rejectCapabilityRequest(requestId, "not right now");
|
|
218
|
+
expect(incoming.respond).toHaveBeenCalledWith({
|
|
219
|
+
result: "error",
|
|
220
|
+
code: "denied",
|
|
221
|
+
message: "not right now",
|
|
222
|
+
});
|
|
223
|
+
expect(h.admission.listPendingCapabilityRequests()).toEqual([]);
|
|
224
|
+
});
|
|
225
|
+
it("rejects with no message field when no reason is given", async () => {
|
|
226
|
+
const h = makeHarness();
|
|
227
|
+
const identity = await generateEs256Identity();
|
|
228
|
+
const requester = await generateEs256Identity();
|
|
229
|
+
const handler = h.admission.createAskHandler({
|
|
230
|
+
capability: CAPABILITY,
|
|
231
|
+
identity,
|
|
232
|
+
clock: fixedClock(NOW_MS),
|
|
233
|
+
bearerDevice: requester.deviceId,
|
|
234
|
+
timeoutMs: HOUR_MS,
|
|
235
|
+
});
|
|
236
|
+
const incoming = fakeIncoming({
|
|
237
|
+
verb: "capability.request",
|
|
238
|
+
capability: CAPABILITY,
|
|
239
|
+
});
|
|
240
|
+
await handler(incoming);
|
|
241
|
+
const [requestId] = h.admission
|
|
242
|
+
.listPendingCapabilityRequests()
|
|
243
|
+
.map((p) => p.requestId);
|
|
244
|
+
if (requestId === undefined)
|
|
245
|
+
throw new Error("no pending request");
|
|
246
|
+
await h.admission.rejectCapabilityRequest(requestId);
|
|
247
|
+
expect(incoming.respond).toHaveBeenCalledWith({
|
|
248
|
+
result: "error",
|
|
249
|
+
code: "denied",
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
it("throws naming the exact unknown request id when accepting, and never touches decide", async () => {
|
|
253
|
+
const h = makeHarness();
|
|
254
|
+
await expect(h.admission.acceptCapabilityRequest("no-such-request", {
|
|
255
|
+
expires: EXPIRES_MS,
|
|
256
|
+
})).rejects.toThrow("No pending capability request no-such-request");
|
|
257
|
+
});
|
|
258
|
+
it("throws naming the exact unknown request id when rejecting", async () => {
|
|
259
|
+
const h = makeHarness();
|
|
260
|
+
await expect(h.admission.rejectCapabilityRequest("no-such-request")).rejects.toThrow("No pending capability request no-such-request");
|
|
261
|
+
});
|
|
262
|
+
it("grants a narrower capability than requested when the accept decision names one explicitly", async () => {
|
|
263
|
+
const h = makeHarness();
|
|
264
|
+
const identity = await generateEs256Identity();
|
|
265
|
+
const requester = await generateEs256Identity();
|
|
266
|
+
const handler = h.admission.createAskHandler({
|
|
267
|
+
capability: CAPABILITY,
|
|
268
|
+
identity,
|
|
269
|
+
clock: fixedClock(NOW_MS),
|
|
270
|
+
bearerDevice: requester.deviceId,
|
|
271
|
+
timeoutMs: HOUR_MS,
|
|
272
|
+
});
|
|
273
|
+
const incoming = fakeIncoming({
|
|
274
|
+
verb: "capability.request",
|
|
275
|
+
capability: CAPABILITY,
|
|
276
|
+
});
|
|
277
|
+
await handler(incoming);
|
|
278
|
+
const [requestId] = h.admission
|
|
279
|
+
.listPendingCapabilityRequests()
|
|
280
|
+
.map((p) => p.requestId);
|
|
281
|
+
if (requestId === undefined)
|
|
282
|
+
throw new Error("no pending request");
|
|
283
|
+
await h.admission.acceptCapabilityRequest(requestId, {
|
|
284
|
+
expires: EXPIRES_MS,
|
|
285
|
+
capability: "dm:send-narrow",
|
|
286
|
+
});
|
|
287
|
+
const outcome = incoming.respond.mock.calls[0]?.[0];
|
|
288
|
+
const verdict = await verifyCapabilityToken(outcome["granted-token"], {
|
|
289
|
+
identity,
|
|
290
|
+
clock: fixedClock(NOW_MS),
|
|
291
|
+
revocation: createRevocationView(),
|
|
292
|
+
expectedBearer: requester.deviceId,
|
|
293
|
+
});
|
|
294
|
+
expect(verdict.ok).toBe(true);
|
|
295
|
+
if (verdict.ok) {
|
|
296
|
+
expect(verdict.claims.capability).toBe("dm:send-narrow");
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
//# sourceMappingURL=capability-ask.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-ask.test.js","sourceRoot":"","sources":["../../src/test/capability-ask.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -47,5 +47,21 @@ describe("isRetryablePublishFailure", () => {
|
|
|
47
47
|
it("does not misclassify an unrelated failure as retryable", () => {
|
|
48
48
|
expect(isRetryablePublishFailure("permission denied")).toBe(false);
|
|
49
49
|
});
|
|
50
|
+
it("recognises a raw connection-level failure with no HTTP status, confirmed in production (ExaDev/agent-comms, run 35249771867, v3.9.0): a dial-tcp i/o timeout reaching the registry, which matches neither the propagation-lag text nor the 5xx status pattern above since no HTTP response was ever received", () => {
|
|
51
|
+
const output = 'Error: publish failed: error sending request: Post "https://registry.modelcontextprotocol.io/v0/publish": dial tcp 34.61.200.254:443: i/o timeout';
|
|
52
|
+
expect(isRetryablePublishFailure(output)).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
it("recognises a connection-refused failure as the same class of connection-level error", () => {
|
|
55
|
+
const output = 'Error: publish failed: error sending request: Post "https://registry.modelcontextprotocol.io/v0/publish": dial tcp 34.61.200.254:443: connect: connection refused';
|
|
56
|
+
expect(isRetryablePublishFailure(output)).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
it("recognises a 401 caused by the login JWT expiring mid-retry-window, confirmed in production (ExaDev/agent-comms, run 35239013253, v3.9.0): mcp-publisher logs in once before the retry loop starts, and a retry attempt late in the 15-minute budget can present a token that has since expired", () => {
|
|
59
|
+
const output = 'Error: publish failed: server returned status 401: {"title":"Unauthorized","status":401,"detail":"Invalid or expired Registry JWT token","instance":"/v0/publish"}';
|
|
60
|
+
expect(isRetryablePublishFailure(output)).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
it("does not retry a 401 that is a genuine authorisation failure rather than an expired token, since retrying an unrecognised or wrongly-scoped credential can never fix it", () => {
|
|
63
|
+
const output = 'Error: publish failed: server returned status 401: {"title":"Unauthorized","status":401,"detail":"Registry JWT token does not grant publish access to this namespace","instance":"/v0/publish"}';
|
|
64
|
+
expect(isRetryablePublishFailure(output)).toBe(false);
|
|
65
|
+
});
|
|
50
66
|
});
|
|
51
67
|
//# sourceMappingURL=mcp-registry-retry.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-registry-retry.test.js","sourceRoot":"","sources":["../../src/test/mcp-registry-retry.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAChC,MAAM,gCAAgC,GAAG,EAAE,CAAC;AAC5C,MAAM,aAAa,GAAG,IAAI,CAAC;AAC3B,MAAM,8BAA8B,GAAG,EAAE,CAAC;AAC1C,MAAM,8BAA8B,GAAG,EAAE,CAAC;AAC1C,MAAM,8BAA8B,GAAG,EAAE,CAAC;AAC1C,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAE3C,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,gBAAgB,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CACnD,sBAAsB,GAAG,qBAAqB,CAC/C,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACtE,MAAM,CACJ,gBAAgB,CAAC,kBAAkB,GAAG,gCAAgC,CAAC,CACxE,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wPAAwP,EAAE,GAAG,EAAE;QAChQ,MAAM,gBAAgB,GACpB,CAAC,8BAA8B;YAC7B,8BAA8B;YAC9B,8BAA8B;YAC9B,8BAA8B,CAAC;YACjC,aAAa,CAAC;QAChB,IAAI,OAAO,GAAG,sBAAsB,CAAC;QACrC,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,OAAO,OAAO,GAAG,kBAAkB,EAAE,CAAC;YACpC,OAAO,IAAI,OAAO,CAAC;YACnB,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;QACD,MAAM,CAAC,OAAO,GAAG,gBAAgB,CAAC,CAAC,UAAU,EAAE,CAAC;QAChD,MAAM,CAAC,OAAO,IAAI,kBAAkB,CAAC,CAAC,UAAU,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,MAAM,GACV,sRAAsR,CAAC;QACzR,MAAM,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gZAAgZ,EAAE,GAAG,EAAE;QACxZ,MAAM,MAAM,GAAG,2DAA2D,CAAC;QAC3E,MAAM,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uIAAuI,EAAE,GAAG,EAAE;QAC/I,MAAM,MAAM,GACV,gEAAgE,CAAC;QACnE,MAAM,CAAC,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,yBAAyB,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"mcp-registry-retry.test.js","sourceRoot":"","sources":["../../src/test/mcp-registry-retry.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-comms",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.0",
|
|
4
4
|
"description": "Cross-harness communication mesh for LLM agents — rooms, DMs, presence, and real-time push delivery over TCP",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c",
|