@mcpmesh/sdk 2.7.0 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/calling-job.test.d.ts +2 -0
- package/dist/__tests__/calling-job.test.d.ts.map +1 -0
- package/dist/__tests__/calling-job.test.js +154 -0
- package/dist/__tests__/calling-job.test.js.map +1 -0
- package/dist/__tests__/claim-dispatcher-required-gate.test.d.ts +2 -0
- package/dist/__tests__/claim-dispatcher-required-gate.test.d.ts.map +1 -0
- package/dist/__tests__/claim-dispatcher-required-gate.test.js +101 -0
- package/dist/__tests__/claim-dispatcher-required-gate.test.js.map +1 -0
- package/dist/__tests__/inbound-job-dispatch.spec.js +21 -8
- package/dist/__tests__/inbound-job-dispatch.spec.js.map +1 -1
- package/dist/__tests__/llm-agent-content-recovery.test.d.ts +17 -0
- package/dist/__tests__/llm-agent-content-recovery.test.d.ts.map +1 -0
- package/dist/__tests__/llm-agent-content-recovery.test.js +203 -0
- package/dist/__tests__/llm-agent-content-recovery.test.js.map +1 -0
- package/dist/__tests__/llm-provider-multistep.test.js +30 -0
- package/dist/__tests__/llm-provider-multistep.test.js.map +1 -1
- package/dist/__tests__/proxy-empty-content.test.d.ts +16 -0
- package/dist/__tests__/proxy-empty-content.test.d.ts.map +1 -0
- package/dist/__tests__/proxy-empty-content.test.js +124 -0
- package/dist/__tests__/proxy-empty-content.test.js.map +1 -0
- package/dist/__tests__/proxy-resource-link.test.js +4 -2
- package/dist/__tests__/proxy-resource-link.test.js.map +1 -1
- package/dist/__tests__/proxy-tool-error.test.js +3 -2
- package/dist/__tests__/proxy-tool-error.test.js.map +1 -1
- package/dist/__tests__/resolver-meshjob.spec.js +2 -2
- package/dist/__tests__/resolver-meshjob.spec.js.map +1 -1
- package/dist/__tests__/route-required.spec.d.ts +9 -0
- package/dist/__tests__/route-required.spec.d.ts.map +1 -0
- package/dist/__tests__/route-required.spec.js +154 -0
- package/dist/__tests__/route-required.spec.js.map +1 -0
- package/dist/agent.d.ts +7 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +37 -5
- package/dist/agent.js.map +1 -1
- package/dist/api-runtime.d.ts.map +1 -1
- package/dist/api-runtime.js +3 -0
- package/dist/api-runtime.js.map +1 -1
- package/dist/claim-dispatcher.d.ts +18 -1
- package/dist/claim-dispatcher.d.ts.map +1 -1
- package/dist/claim-dispatcher.js +82 -3
- package/dist/claim-dispatcher.js.map +1 -1
- package/dist/express.d.ts.map +1 -1
- package/dist/express.js +3 -0
- package/dist/express.js.map +1 -1
- package/dist/inbound-job-dispatch.d.ts +9 -6
- package/dist/inbound-job-dispatch.d.ts.map +1 -1
- package/dist/inbound-job-dispatch.js +35 -10
- package/dist/inbound-job-dispatch.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/job-context.d.ts +9 -0
- package/dist/job-context.d.ts.map +1 -1
- package/dist/job-context.js.map +1 -1
- package/dist/llm-agent.d.ts +3 -0
- package/dist/llm-agent.d.ts.map +1 -1
- package/dist/llm-agent.js +72 -7
- package/dist/llm-agent.js.map +1 -1
- package/dist/llm-provider.d.ts.map +1 -1
- package/dist/llm-provider.js +13 -10
- package/dist/llm-provider.js.map +1 -1
- package/dist/proxy.d.ts +43 -2
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +144 -11
- package/dist/proxy.js.map +1 -1
- package/dist/route.d.ts +3 -0
- package/dist/route.d.ts.map +1 -1
- package/dist/route.js +42 -0
- package/dist/route.js.map +1 -1
- package/dist/tracing.d.ts.map +1 -1
- package/dist/tracing.js +13 -0
- package/dist/tracing.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calling-job.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/calling-job.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue #1263: propagate the CALLING job's identity on outbound tool calls via
|
|
3
|
+
* a dedicated header pair, and expose a provider-side accessor.
|
|
4
|
+
*
|
|
5
|
+
* Carrier: `x-mesh-calling-job-id` / `x-mesh-calling-claim-epoch` — distinct
|
|
6
|
+
* from the push-mode dispatch protocol's `x-mesh-job-id` / `x-mesh-claim-epoch`
|
|
7
|
+
* (x-mesh-job-id doubles as the dispatch discriminator and cannot carry calling
|
|
8
|
+
* identity).
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
11
|
+
// Keep the REAL core (real matchesPropagateHeader / injectTraceContext); only
|
|
12
|
+
// silence the span publisher and the never-resolving cancel await.
|
|
13
|
+
vi.mock("@mcpmesh/core", async (importOriginal) => {
|
|
14
|
+
const actual = await importOriginal();
|
|
15
|
+
return {
|
|
16
|
+
...actual,
|
|
17
|
+
publishSpan: vi.fn(async () => false),
|
|
18
|
+
awaitJobCancel: vi.fn(() => new Promise(() => { })),
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
vi.mock("../http-pool.js", () => ({ getDispatcher: () => undefined }));
|
|
22
|
+
import { callingJob, callMcpTool, DEFAULT_CALL_OPTIONS, runWithPropagatedHeaders, } from "../proxy.js";
|
|
23
|
+
import { matchesPropagateHeader } from "../tracing.js";
|
|
24
|
+
import { CURRENT_JOB } from "../job-context.js";
|
|
25
|
+
describe("allowlist (issue #1263)", () => {
|
|
26
|
+
it("always propagates the calling-* pair", () => {
|
|
27
|
+
expect(matchesPropagateHeader("x-mesh-calling-job-id")).toBe(true);
|
|
28
|
+
expect(matchesPropagateHeader("x-mesh-calling-claim-epoch")).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
it("does NOT allowlist the dispatch pair (reverted)", () => {
|
|
31
|
+
expect(matchesPropagateHeader("x-mesh-job-id")).toBe(false);
|
|
32
|
+
expect(matchesPropagateHeader("x-mesh-claim-epoch")).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
it("is case-insensitive", () => {
|
|
35
|
+
expect(matchesPropagateHeader("X-Mesh-Calling-Job-Id")).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe("callingJob() accessor (issue #1263)", () => {
|
|
39
|
+
it("returns null outside a job", () => {
|
|
40
|
+
expect(callingJob()).toBeNull();
|
|
41
|
+
});
|
|
42
|
+
it("returns null when no calling job id", () => {
|
|
43
|
+
runWithPropagatedHeaders({ "x-mesh-calling-claim-epoch": "7" }, () => {
|
|
44
|
+
expect(callingJob()).toBeNull();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
it("does NOT read the dispatch pair", () => {
|
|
48
|
+
runWithPropagatedHeaders({ "x-mesh-job-id": "job-self", "x-mesh-claim-epoch": "3" }, () => {
|
|
49
|
+
expect(callingJob()).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
it("returns jobId + claimEpoch from the calling-* pair", () => {
|
|
53
|
+
runWithPropagatedHeaders({
|
|
54
|
+
"x-mesh-calling-job-id": "job-abc",
|
|
55
|
+
"x-mesh-calling-claim-epoch": "5",
|
|
56
|
+
}, () => {
|
|
57
|
+
expect(callingJob()).toEqual({ jobId: "job-abc", claimEpoch: 5 });
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
it("claimEpoch is null when only calling job id present", () => {
|
|
61
|
+
runWithPropagatedHeaders({ "x-mesh-calling-job-id": "job-xyz" }, () => {
|
|
62
|
+
expect(callingJob()).toEqual({ jobId: "job-xyz", claimEpoch: null });
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
it("malformed epoch degrades to null", () => {
|
|
66
|
+
runWithPropagatedHeaders({ "x-mesh-calling-job-id": "job-1", "x-mesh-calling-claim-epoch": "5.5" }, () => {
|
|
67
|
+
expect(callingJob()?.claimEpoch).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe("outbound overlay (issue #1263)", () => {
|
|
72
|
+
let captured;
|
|
73
|
+
function mockFetch() {
|
|
74
|
+
captured = undefined;
|
|
75
|
+
globalThis.fetch = vi.fn(async (_url, init) => {
|
|
76
|
+
captured = init;
|
|
77
|
+
const body = JSON.stringify({
|
|
78
|
+
jsonrpc: "2.0",
|
|
79
|
+
id: "x",
|
|
80
|
+
result: { content: [{ type: "text", text: "{}" }] },
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
ok: true,
|
|
84
|
+
status: 200,
|
|
85
|
+
statusText: "OK",
|
|
86
|
+
text: async () => body,
|
|
87
|
+
headers: {
|
|
88
|
+
get: (n) => n.toLowerCase() === "content-type" ? "application/json" : null,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function capturedHeaders() {
|
|
94
|
+
return (captured?.headers ?? {});
|
|
95
|
+
}
|
|
96
|
+
function capturedMeshHeaders() {
|
|
97
|
+
const body = JSON.parse(captured?.body ?? "{}");
|
|
98
|
+
return (body?.params?.arguments?._mesh_headers ?? {});
|
|
99
|
+
}
|
|
100
|
+
function snap(jobId, claimEpoch) {
|
|
101
|
+
return { jobId, deadlineSecsRemaining: null, claimEpoch };
|
|
102
|
+
}
|
|
103
|
+
afterEach(() => {
|
|
104
|
+
vi.restoreAllMocks();
|
|
105
|
+
});
|
|
106
|
+
it("no active job → no calling-* seeded", async () => {
|
|
107
|
+
mockFetch();
|
|
108
|
+
await callMcpTool("http://d:9000", "t", { a: 1 }, DEFAULT_CALL_OPTIONS, "cap");
|
|
109
|
+
expect(capturedHeaders()["x-mesh-calling-job-id"]).toBeUndefined();
|
|
110
|
+
});
|
|
111
|
+
it("seeds both from the active job snapshot", async () => {
|
|
112
|
+
mockFetch();
|
|
113
|
+
await CURRENT_JOB.run(snap("job-A", 4), async () => {
|
|
114
|
+
await callMcpTool("http://d:9000", "t", {}, DEFAULT_CALL_OPTIONS, "cap");
|
|
115
|
+
});
|
|
116
|
+
expect(capturedHeaders()["x-mesh-calling-job-id"]).toBe("job-A");
|
|
117
|
+
expect(capturedHeaders()["x-mesh-calling-claim-epoch"]).toBe("4");
|
|
118
|
+
// Also carried in the _mesh_headers args (FastMCP-visible path).
|
|
119
|
+
expect(capturedMeshHeaders()["x-mesh-calling-job-id"]).toBe("job-A");
|
|
120
|
+
});
|
|
121
|
+
it("seeds id only when the snapshot has no epoch", async () => {
|
|
122
|
+
mockFetch();
|
|
123
|
+
await CURRENT_JOB.run(snap("job-A", null), async () => {
|
|
124
|
+
await callMcpTool("http://d:9000", "t", {}, DEFAULT_CALL_OPTIONS, "cap");
|
|
125
|
+
});
|
|
126
|
+
expect(capturedHeaders()["x-mesh-calling-job-id"]).toBe("job-A");
|
|
127
|
+
expect(capturedHeaders()["x-mesh-calling-claim-epoch"]).toBeUndefined();
|
|
128
|
+
});
|
|
129
|
+
it("replaces an inherited pair entirely — no stale epoch rides along", async () => {
|
|
130
|
+
mockFetch();
|
|
131
|
+
await runWithPropagatedHeaders({
|
|
132
|
+
"x-mesh-calling-job-id": "job-OLD",
|
|
133
|
+
"x-mesh-calling-claim-epoch": "99",
|
|
134
|
+
}, async () => {
|
|
135
|
+
await CURRENT_JOB.run(snap("job-NEW", null), async () => {
|
|
136
|
+
await callMcpTool("http://d:9000", "t", {}, DEFAULT_CALL_OPTIONS, "cap");
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
expect(capturedHeaders()["x-mesh-calling-job-id"]).toBe("job-NEW");
|
|
140
|
+
expect(capturedHeaders()["x-mesh-calling-claim-epoch"]).toBeUndefined();
|
|
141
|
+
});
|
|
142
|
+
it("passes an inherited pair through when there is no active job", async () => {
|
|
143
|
+
mockFetch();
|
|
144
|
+
await runWithPropagatedHeaders({
|
|
145
|
+
"x-mesh-calling-job-id": "job-UP",
|
|
146
|
+
"x-mesh-calling-claim-epoch": "2",
|
|
147
|
+
}, async () => {
|
|
148
|
+
await callMcpTool("http://d:9000", "t", {}, DEFAULT_CALL_OPTIONS, "cap");
|
|
149
|
+
});
|
|
150
|
+
expect(capturedHeaders()["x-mesh-calling-job-id"]).toBe("job-UP");
|
|
151
|
+
expect(capturedHeaders()["x-mesh-calling-claim-epoch"]).toBe("2");
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=calling-job.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calling-job.test.js","sourceRoot":"","sources":["../../src/__tests__/calling-job.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-dispatcher-required-gate.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/claim-dispatcher-required-gate.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue #1268: claim dispatch must gate on locally-resolved required deps.
|
|
3
|
+
*
|
|
4
|
+
* - Pre-claim skip: the dispatcher must NOT POST /jobs/claim while a required
|
|
5
|
+
* dependency slot is unresolved locally.
|
|
6
|
+
* - Pre-invoke guard: a still-unresolved required slot at invoke time must
|
|
7
|
+
* release the lease (retryable), never terminal-fail, and must NOT run the
|
|
8
|
+
* handler.
|
|
9
|
+
* - Optional deps (no probe) run as before.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
12
|
+
// Fake JobController captured per-test so assertions can inspect it.
|
|
13
|
+
let lastController = null;
|
|
14
|
+
vi.mock("../inbound-job-dispatch.js", () => ({
|
|
15
|
+
makeJobController: vi.fn(() => {
|
|
16
|
+
lastController = {
|
|
17
|
+
releaseLease: vi.fn(async () => { }),
|
|
18
|
+
fail: vi.fn(async () => { }),
|
|
19
|
+
};
|
|
20
|
+
return lastController;
|
|
21
|
+
}),
|
|
22
|
+
// Pass-through: invoke the thunk so the handler runs when the guard allows.
|
|
23
|
+
runWithJobContext: vi.fn(async (_jobId, _dl, _controller, invoke) => invoke()),
|
|
24
|
+
}));
|
|
25
|
+
vi.mock("../proxy.js", () => ({
|
|
26
|
+
runWithPropagatedHeaders: (_headers, fn) => fn(),
|
|
27
|
+
}));
|
|
28
|
+
import { ClaimDispatcher } from "../claim-dispatcher.js";
|
|
29
|
+
function makeDispatcher(handler, requiredProbe) {
|
|
30
|
+
return new ClaimDispatcher("test_cap", "test-instance", "http://registry:8000", handler, undefined, requiredProbe);
|
|
31
|
+
}
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
lastController = null;
|
|
34
|
+
vi.clearAllMocks();
|
|
35
|
+
});
|
|
36
|
+
describe("pre-invoke guard (issue #1268)", () => {
|
|
37
|
+
it("unresolved required dep → releaseLease, no handler, no fail", async () => {
|
|
38
|
+
const handlerCalls = [];
|
|
39
|
+
const handler = async (payload) => {
|
|
40
|
+
handlerCalls.push(payload);
|
|
41
|
+
};
|
|
42
|
+
const d = makeDispatcher(handler, () => "cap_a");
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
await d._dispatch({ id: "job-1", submitted_payload: {} });
|
|
45
|
+
expect(handlerCalls).toEqual([]);
|
|
46
|
+
expect(lastController).not.toBeNull();
|
|
47
|
+
expect(lastController.releaseLease).toHaveBeenCalledTimes(1);
|
|
48
|
+
expect(lastController.fail).not.toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
it("resolved required dep → handler runs, no release", async () => {
|
|
51
|
+
const handlerCalls = [];
|
|
52
|
+
const handler = async (payload) => {
|
|
53
|
+
handlerCalls.push(payload);
|
|
54
|
+
};
|
|
55
|
+
const d = makeDispatcher(handler, () => null);
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
+
await d._dispatch({ id: "job-2", submitted_payload: { x: 1 } });
|
|
58
|
+
expect(handlerCalls).toEqual([{ x: 1 }]);
|
|
59
|
+
expect(lastController.releaseLease).not.toHaveBeenCalled();
|
|
60
|
+
expect(lastController.fail).not.toHaveBeenCalled();
|
|
61
|
+
});
|
|
62
|
+
it("optional deps (no probe) → handler runs (regression)", async () => {
|
|
63
|
+
const handlerCalls = [];
|
|
64
|
+
const handler = async (payload) => {
|
|
65
|
+
handlerCalls.push(payload);
|
|
66
|
+
};
|
|
67
|
+
const d = makeDispatcher(handler); // no requiredProbe
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
69
|
+
await d._dispatch({ id: "job-3", submitted_payload: {} });
|
|
70
|
+
expect(handlerCalls).toEqual([{}]);
|
|
71
|
+
expect(lastController.releaseLease).not.toHaveBeenCalled();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe("pre-claim skip (issue #1268)", () => {
|
|
75
|
+
it("does not claim while a required dep is unresolved", async () => {
|
|
76
|
+
const d = makeDispatcher(async () => { }, () => "cap_a");
|
|
77
|
+
const claimSpy = vi.fn(async () => []);
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
+
d._claimOnce = claimSpy;
|
|
80
|
+
d.start();
|
|
81
|
+
await new Promise((r) => setTimeout(r, 60));
|
|
82
|
+
await d.stop(0);
|
|
83
|
+
expect(claimSpy).not.toHaveBeenCalled();
|
|
84
|
+
});
|
|
85
|
+
it("claims once the required dep is resolved", async () => {
|
|
86
|
+
let resolved = false;
|
|
87
|
+
const d = makeDispatcher(async () => { }, () => (resolved ? null : "cap_a"));
|
|
88
|
+
const claimSpy = vi.fn(async () => []);
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
90
|
+
d._claimOnce = claimSpy;
|
|
91
|
+
d.start();
|
|
92
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
93
|
+
resolved = true;
|
|
94
|
+
// Wait past one base poll interval (500ms) so the loop wakes, re-probes
|
|
95
|
+
// (now resolved), and issues a claim.
|
|
96
|
+
await new Promise((r) => setTimeout(r, 650));
|
|
97
|
+
await d.stop(0);
|
|
98
|
+
expect(claimSpy).toHaveBeenCalled();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
//# sourceMappingURL=claim-dispatcher-required-gate.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-dispatcher-required-gate.test.js","sourceRoot":"","sources":["../../src/__tests__/claim-dispatcher-required-gate.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,36 +18,49 @@ import { describe, it, expect, vi } from "vitest";
|
|
|
18
18
|
import { readJobHeaders, runWithJobContext, } from "../inbound-job-dispatch.js";
|
|
19
19
|
import { currentJob } from "../job-context.js";
|
|
20
20
|
describe("readJobHeaders", () => {
|
|
21
|
-
it("returns [null, null] when headers is null/undefined", () => {
|
|
22
|
-
expect(readJobHeaders(null)).toEqual([null, null]);
|
|
23
|
-
expect(readJobHeaders(undefined)).toEqual([null, null]);
|
|
24
|
-
expect(readJobHeaders({})).toEqual([null, null]);
|
|
21
|
+
it("returns [null, null, null] when headers is null/undefined", () => {
|
|
22
|
+
expect(readJobHeaders(null)).toEqual([null, null, null]);
|
|
23
|
+
expect(readJobHeaders(undefined)).toEqual([null, null, null]);
|
|
24
|
+
expect(readJobHeaders({})).toEqual([null, null, null]);
|
|
25
25
|
});
|
|
26
26
|
it("extracts job id only when X-Mesh-Timeout is absent", () => {
|
|
27
27
|
expect(readJobHeaders({ "x-mesh-job-id": "job-123" })).toEqual([
|
|
28
28
|
"job-123",
|
|
29
29
|
null,
|
|
30
|
+
null,
|
|
30
31
|
]);
|
|
31
32
|
});
|
|
32
33
|
it("extracts both headers and parses timeout as float", () => {
|
|
33
34
|
expect(readJobHeaders({
|
|
34
35
|
"x-mesh-job-id": "job-456",
|
|
35
36
|
"x-mesh-timeout": "12.5",
|
|
36
|
-
})).toEqual(["job-456", 12.5]);
|
|
37
|
+
})).toEqual(["job-456", 12.5, null]);
|
|
37
38
|
});
|
|
38
39
|
it("ignores malformed timeout (non-numeric / non-positive)", () => {
|
|
39
40
|
expect(readJobHeaders({
|
|
40
41
|
"x-mesh-job-id": "j1",
|
|
41
42
|
"x-mesh-timeout": "abc",
|
|
42
|
-
})).toEqual(["j1", null]);
|
|
43
|
+
})).toEqual(["j1", null, null]);
|
|
43
44
|
expect(readJobHeaders({
|
|
44
45
|
"x-mesh-job-id": "j2",
|
|
45
46
|
"x-mesh-timeout": "0",
|
|
46
|
-
})).toEqual(["j2", null]);
|
|
47
|
+
})).toEqual(["j2", null, null]);
|
|
47
48
|
expect(readJobHeaders({
|
|
48
49
|
"x-mesh-job-id": "j3",
|
|
49
50
|
"x-mesh-timeout": "-1.5",
|
|
50
|
-
})).toEqual(["j3", null]);
|
|
51
|
+
})).toEqual(["j3", null, null]);
|
|
52
|
+
});
|
|
53
|
+
it("parses X-Mesh-Claim-Epoch (issue #1252); rejects negatives/malformed", () => {
|
|
54
|
+
// Valid epoch (including a legitimate 0 the registry minted).
|
|
55
|
+
expect(readJobHeaders({ "x-mesh-job-id": "j", "x-mesh-claim-epoch": "5" })).toEqual(["j", null, 5]);
|
|
56
|
+
expect(readJobHeaders({ "x-mesh-job-id": "j", "x-mesh-claim-epoch": "0" })).toEqual(["j", null, 0]);
|
|
57
|
+
// Malformed / negative ⇒ null (legacy owner-only, never a fabricated 0).
|
|
58
|
+
expect(readJobHeaders({ "x-mesh-job-id": "j", "x-mesh-claim-epoch": "-1" })).toEqual(["j", null, null]);
|
|
59
|
+
expect(readJobHeaders({ "x-mesh-job-id": "j", "x-mesh-claim-epoch": "abc" })).toEqual(["j", null, null]);
|
|
60
|
+
// Strict integer parse: partial-numeric strings must NOT slip through as
|
|
61
|
+
// a truncated epoch (Number.parseInt would accept these as 5).
|
|
62
|
+
expect(readJobHeaders({ "x-mesh-job-id": "j", "x-mesh-claim-epoch": "5.5" })).toEqual(["j", null, null]);
|
|
63
|
+
expect(readJobHeaders({ "x-mesh-job-id": "j", "x-mesh-claim-epoch": "5abc" })).toEqual(["j", null, null]);
|
|
51
64
|
});
|
|
52
65
|
});
|
|
53
66
|
describe("runWithJobContext", () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbound-job-dispatch.spec.js","sourceRoot":"","sources":["../../src/__tests__/inbound-job-dispatch.spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EACL,cAAc,EACd,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"inbound-job-dispatch.spec.js","sourceRoot":"","sources":["../../src/__tests__/inbound-job-dispatch.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consumer-side recovery + honest diagnostics for the mesh provider's
|
|
3
|
+
* ``{ role, content }`` reply envelope (llm-agent.ts).
|
|
4
|
+
*
|
|
5
|
+
* Providers should send the answer as a string. Two malformed-but-recoverable
|
|
6
|
+
* shapes occur in the field:
|
|
7
|
+
* 1. ``content`` is a raw object — the structured answer leaked unserialized.
|
|
8
|
+
* ``complete()`` serializes it so downstream schema parsing sees JSON.
|
|
9
|
+
* 2. the map carries neither ``content`` nor ``role`` — a bare structured
|
|
10
|
+
* answer without the envelope; the whole map is treated as the answer.
|
|
11
|
+
*
|
|
12
|
+
* When content resolves to empty under a response schema, ``run()`` blames the
|
|
13
|
+
* empty reply and surfaces the raw payload — not an opaque "could not extract
|
|
14
|
+
* JSON".
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=llm-agent-content-recovery.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-agent-content-recovery.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/llm-agent-content-recovery.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consumer-side recovery + honest diagnostics for the mesh provider's
|
|
3
|
+
* ``{ role, content }`` reply envelope (llm-agent.ts).
|
|
4
|
+
*
|
|
5
|
+
* Providers should send the answer as a string. Two malformed-but-recoverable
|
|
6
|
+
* shapes occur in the field:
|
|
7
|
+
* 1. ``content`` is a raw object — the structured answer leaked unserialized.
|
|
8
|
+
* ``complete()`` serializes it so downstream schema parsing sees JSON.
|
|
9
|
+
* 2. the map carries neither ``content`` nor ``role`` — a bare structured
|
|
10
|
+
* answer without the envelope; the whole map is treated as the answer.
|
|
11
|
+
*
|
|
12
|
+
* When content resolves to empty under a response schema, ``run()`` blames the
|
|
13
|
+
* empty reply and surfaces the raw payload — not an opaque "could not extract
|
|
14
|
+
* JSON".
|
|
15
|
+
*/
|
|
16
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
vi.mock("@mcpmesh/core", () => ({
|
|
19
|
+
generateTraceId: () => "trace-mock",
|
|
20
|
+
generateSpanId: () => "span-mock",
|
|
21
|
+
injectTraceContext: (argsJson) => argsJson,
|
|
22
|
+
publishSpan: vi.fn(async () => false),
|
|
23
|
+
parseSseResponse: (s) => s,
|
|
24
|
+
parseSseResponseToObject: (s) => JSON.parse(s),
|
|
25
|
+
extractJson: (s) => s,
|
|
26
|
+
}));
|
|
27
|
+
vi.mock("../http-pool.js", () => ({
|
|
28
|
+
getDispatcher: () => undefined,
|
|
29
|
+
}));
|
|
30
|
+
import { MeshDelegatedProvider, MeshLlmAgent } from "../llm-agent.js";
|
|
31
|
+
import { ResponseParseError } from "../errors.js";
|
|
32
|
+
const ENDPOINT = "http://provider.local:9001";
|
|
33
|
+
const FN_BUFFERED = "process_chat";
|
|
34
|
+
function mcpJsonResponse(payload) {
|
|
35
|
+
const envelope = {
|
|
36
|
+
jsonrpc: "2.0",
|
|
37
|
+
id: 1,
|
|
38
|
+
result: {
|
|
39
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
ok: true,
|
|
44
|
+
status: 200,
|
|
45
|
+
statusText: "OK",
|
|
46
|
+
headers: {
|
|
47
|
+
get: (name) => name.toLowerCase() === "content-type" ? "application/json" : null,
|
|
48
|
+
},
|
|
49
|
+
text: async () => JSON.stringify(envelope),
|
|
50
|
+
json: async () => envelope,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
describe("MeshDelegatedProvider.complete() — content normalization", () => {
|
|
54
|
+
let originalFetch;
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
originalFetch = globalThis.fetch;
|
|
57
|
+
});
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
globalThis.fetch = originalFetch;
|
|
60
|
+
vi.restoreAllMocks();
|
|
61
|
+
});
|
|
62
|
+
function stub(payload) {
|
|
63
|
+
globalThis.fetch = vi.fn(async () => mcpJsonResponse(payload));
|
|
64
|
+
}
|
|
65
|
+
async function completeWith(payload) {
|
|
66
|
+
stub(payload);
|
|
67
|
+
const provider = new MeshDelegatedProvider(ENDPOINT, FN_BUFFERED, false);
|
|
68
|
+
const messages = [{ role: "user", content: "hi" }];
|
|
69
|
+
const res = await provider.complete("anthropic/claude-sonnet-4-5", messages);
|
|
70
|
+
return res.choices[0]?.message.content;
|
|
71
|
+
}
|
|
72
|
+
it("string content passes through unchanged (regression)", async () => {
|
|
73
|
+
expect(await completeWith({ role: "assistant", content: "ok" })).toBe("ok");
|
|
74
|
+
});
|
|
75
|
+
it("object content is serialized to a JSON string", async () => {
|
|
76
|
+
const answer = { count: 5, name: "x" };
|
|
77
|
+
expect(await completeWith({ role: "assistant", content: answer })).toBe(JSON.stringify(answer));
|
|
78
|
+
});
|
|
79
|
+
it("bare map without content/role keys is serialized whole", async () => {
|
|
80
|
+
const bare = { verdict: "BLOCK", reason: "policy" };
|
|
81
|
+
expect(await completeWith(bare)).toBe(JSON.stringify(bare));
|
|
82
|
+
});
|
|
83
|
+
it("error map is NOT treated as a bare answer (stays empty)", async () => {
|
|
84
|
+
expect(await completeWith({ error: "rate limited by vendor" })).toBe("");
|
|
85
|
+
});
|
|
86
|
+
it("empty MCP content → throws but still captures the raw payload (#1250)", async () => {
|
|
87
|
+
// A delegated provider reply with an EMPTY content array now surfaces as
|
|
88
|
+
// null. The type guard throws, but _lastRawResponse must be set FIRST so the
|
|
89
|
+
// run() diagnostic isn't left stale — a regression vs the old JSON.parse("")
|
|
90
|
+
// path that recorded the payload before throwing.
|
|
91
|
+
const emptyEnvelope = { jsonrpc: "2.0", id: 1, result: { content: [] } };
|
|
92
|
+
globalThis.fetch = vi.fn(async () => ({
|
|
93
|
+
ok: true,
|
|
94
|
+
status: 200,
|
|
95
|
+
statusText: "OK",
|
|
96
|
+
headers: {
|
|
97
|
+
get: (name) => name.toLowerCase() === "content-type" ? "application/json" : null,
|
|
98
|
+
},
|
|
99
|
+
text: async () => JSON.stringify(emptyEnvelope),
|
|
100
|
+
json: async () => emptyEnvelope,
|
|
101
|
+
}));
|
|
102
|
+
const provider = new MeshDelegatedProvider(ENDPOINT, FN_BUFFERED, false);
|
|
103
|
+
const messages = [{ role: "user", content: "hi" }];
|
|
104
|
+
await expect(provider.complete("anthropic/claude-sonnet-4-5", messages)).rejects.toThrow("Invalid response from mesh provider");
|
|
105
|
+
expect(provider.lastRawResponse).toBe("null");
|
|
106
|
+
});
|
|
107
|
+
it("bare answer with a null error field is still recovered", async () => {
|
|
108
|
+
const payload = { data: "ok", error: null, verdict: "PASS" };
|
|
109
|
+
expect(await completeWith(payload)).toBe(JSON.stringify(payload));
|
|
110
|
+
});
|
|
111
|
+
it("tool_calls-only map is NOT treated as a bare answer (stays empty)", async () => {
|
|
112
|
+
const payload = {
|
|
113
|
+
tool_calls: [
|
|
114
|
+
{ id: "call_1", type: "function", function: { name: "f", arguments: "{}" } },
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
expect(await completeWith(payload)).toBe("");
|
|
118
|
+
});
|
|
119
|
+
it("_mesh_usage-only map is NOT treated as a bare answer (stays empty)", async () => {
|
|
120
|
+
expect(await completeWith({ _mesh_usage: { prompt_tokens: 1, completion_tokens: 2 } })).toBe("");
|
|
121
|
+
});
|
|
122
|
+
it("empty-string content in a real envelope stays empty", async () => {
|
|
123
|
+
expect(await completeWith({ role: "assistant", content: "" })).toBe("");
|
|
124
|
+
});
|
|
125
|
+
it("null content in a real envelope stays empty (legal, e.g. tool calls)", async () => {
|
|
126
|
+
expect(await completeWith({ role: "assistant", content: null })).toBe("");
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe("MeshLlmAgent.run() — empty-content diagnostic", () => {
|
|
130
|
+
let originalFetch;
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
originalFetch = globalThis.fetch;
|
|
133
|
+
});
|
|
134
|
+
afterEach(() => {
|
|
135
|
+
globalThis.fetch = originalFetch;
|
|
136
|
+
vi.restoreAllMocks();
|
|
137
|
+
});
|
|
138
|
+
const schema = z.object({ answer: z.string() });
|
|
139
|
+
function makeAgent() {
|
|
140
|
+
return new MeshLlmAgent({
|
|
141
|
+
functionId: "test.recovery",
|
|
142
|
+
provider: { capability: "llm" },
|
|
143
|
+
maxIterations: 5,
|
|
144
|
+
returnSchema: schema,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function stub(payload) {
|
|
148
|
+
globalThis.fetch = vi.fn(async () => mcpJsonResponse(payload));
|
|
149
|
+
}
|
|
150
|
+
it("empty content under a response schema → ResponseParseError with raw payload", async () => {
|
|
151
|
+
stub({ role: "assistant", content: "" });
|
|
152
|
+
const agent = makeAgent();
|
|
153
|
+
await expect(agent.run("hi", {
|
|
154
|
+
tools: [],
|
|
155
|
+
meshProvider: { endpoint: ENDPOINT, functionName: FN_BUFFERED },
|
|
156
|
+
})).rejects.toMatchObject({
|
|
157
|
+
name: "ResponseParseError",
|
|
158
|
+
});
|
|
159
|
+
stub({ role: "assistant", content: "" });
|
|
160
|
+
const agent2 = makeAgent();
|
|
161
|
+
const err = await agent2
|
|
162
|
+
.run("hi", {
|
|
163
|
+
tools: [],
|
|
164
|
+
meshProvider: { endpoint: ENDPOINT, functionName: FN_BUFFERED },
|
|
165
|
+
})
|
|
166
|
+
.catch((e) => e);
|
|
167
|
+
expect(err).toBeInstanceOf(ResponseParseError);
|
|
168
|
+
expect(err.message).toContain("empty content");
|
|
169
|
+
expect(err.message).toContain("Raw response payload:");
|
|
170
|
+
});
|
|
171
|
+
it("object content under a response schema → parses instead of failing", async () => {
|
|
172
|
+
stub({ role: "assistant", content: { answer: "structured" } });
|
|
173
|
+
const agent = makeAgent();
|
|
174
|
+
const result = await agent.run("hi", {
|
|
175
|
+
tools: [],
|
|
176
|
+
meshProvider: { endpoint: ENDPOINT, functionName: FN_BUFFERED },
|
|
177
|
+
});
|
|
178
|
+
expect(result).toEqual({ answer: "structured" });
|
|
179
|
+
});
|
|
180
|
+
it("genuine bare answer under a response schema → recovered (not an error)", async () => {
|
|
181
|
+
stub({ answer: "bare" });
|
|
182
|
+
const agent = makeAgent();
|
|
183
|
+
const result = await agent.run("hi", {
|
|
184
|
+
tools: [],
|
|
185
|
+
meshProvider: { endpoint: ENDPOINT, functionName: FN_BUFFERED },
|
|
186
|
+
});
|
|
187
|
+
expect(result).toEqual({ answer: "bare" });
|
|
188
|
+
});
|
|
189
|
+
it("error map → diagnostic surfaces the error payload, not a schema failure", async () => {
|
|
190
|
+
stub({ error: "rate limited by vendor" });
|
|
191
|
+
const agent = makeAgent();
|
|
192
|
+
const err = await agent
|
|
193
|
+
.run("hi", {
|
|
194
|
+
tools: [],
|
|
195
|
+
meshProvider: { endpoint: ENDPOINT, functionName: FN_BUFFERED },
|
|
196
|
+
})
|
|
197
|
+
.catch((e) => e);
|
|
198
|
+
expect(err).toBeInstanceOf(ResponseParseError);
|
|
199
|
+
expect(err.message).toContain("empty content");
|
|
200
|
+
expect(err.message).toContain("rate limited by vendor");
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
//# sourceMappingURL=llm-agent-content-recovery.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-agent-content-recovery.test.js","sourceRoot":"","sources":["../../src/__tests__/llm-agent-content-recovery.test.ts"],"names":[],"mappings":"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"}
|