@mcpmesh/sdk 1.3.4 → 2.0.0-beta.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__/a2a/a2a-bearer.spec.d.ts +2 -0
- package/dist/__tests__/a2a/a2a-bearer.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/a2a-bearer.spec.js +58 -0
- package/dist/__tests__/a2a/a2a-bearer.spec.js.map +1 -0
- package/dist/__tests__/a2a/a2a-client.spec.d.ts +2 -0
- package/dist/__tests__/a2a/a2a-client.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/a2a-client.spec.js +334 -0
- package/dist/__tests__/a2a/a2a-client.spec.js.map +1 -0
- package/dist/__tests__/a2a/a2a-job.spec.d.ts +2 -0
- package/dist/__tests__/a2a/a2a-job.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/a2a-job.spec.js +255 -0
- package/dist/__tests__/a2a/a2a-job.spec.js.map +1 -0
- package/dist/__tests__/a2a/a2a-stream.spec.d.ts +2 -0
- package/dist/__tests__/a2a/a2a-stream.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/a2a-stream.spec.js +278 -0
- package/dist/__tests__/a2a/a2a-stream.spec.js.map +1 -0
- package/dist/__tests__/a2a/agent-a2a-config.spec.d.ts +2 -0
- package/dist/__tests__/a2a/agent-a2a-config.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/agent-a2a-config.spec.js +262 -0
- package/dist/__tests__/a2a/agent-a2a-config.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/auth-filter.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/auth-filter.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/auth-filter.spec.js +127 -0
- package/dist/__tests__/a2a/producer/auth-filter.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/card-builder.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/card-builder.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/card-builder.spec.js +113 -0
- package/dist/__tests__/a2a/producer/card-builder.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/dispatcher.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/dispatcher.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/dispatcher.spec.js +850 -0
- package/dist/__tests__/a2a/producer/dispatcher.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/mount-surface-push.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/mount-surface-push.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/mount-surface-push.spec.js +164 -0
- package/dist/__tests__/a2a/producer/mount-surface-push.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/mount.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/mount.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/mount.spec.js +433 -0
- package/dist/__tests__/a2a/producer/mount.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/public-url-cache.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/public-url-cache.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/public-url-cache.spec.js +116 -0
- package/dist/__tests__/a2a/producer/public-url-cache.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/sse-emitter.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/sse-emitter.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/sse-emitter.spec.js +754 -0
- package/dist/__tests__/a2a/producer/sse-emitter.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/state-translator.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/state-translator.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/state-translator.spec.js +124 -0
- package/dist/__tests__/a2a/producer/state-translator.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/task-store.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/task-store.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/task-store.spec.js +180 -0
- package/dist/__tests__/a2a/producer/task-store.spec.js.map +1 -0
- package/dist/__tests__/agent-add-tool.spec.d.ts +2 -0
- package/dist/__tests__/agent-add-tool.spec.d.ts.map +1 -0
- package/dist/__tests__/agent-add-tool.spec.js +483 -0
- package/dist/__tests__/agent-add-tool.spec.js.map +1 -0
- package/dist/__tests__/api-runtime-race.spec.d.ts +2 -0
- package/dist/__tests__/api-runtime-race.spec.d.ts.map +1 -0
- package/dist/__tests__/api-runtime-race.spec.js +193 -0
- package/dist/__tests__/api-runtime-race.spec.js.map +1 -0
- package/dist/__tests__/claim-dispatcher.spec.d.ts +2 -0
- package/dist/__tests__/claim-dispatcher.spec.d.ts.map +1 -0
- package/dist/__tests__/claim-dispatcher.spec.js +408 -0
- package/dist/__tests__/claim-dispatcher.spec.js.map +1 -0
- package/dist/__tests__/inbound-job-dispatch.spec.d.ts +2 -0
- package/dist/__tests__/inbound-job-dispatch.spec.d.ts.map +1 -0
- package/dist/__tests__/inbound-job-dispatch.spec.js +185 -0
- package/dist/__tests__/inbound-job-dispatch.spec.js.map +1 -0
- package/dist/__tests__/job-controller-progress.spec.d.ts +2 -0
- package/dist/__tests__/job-controller-progress.spec.d.ts.map +1 -0
- package/dist/__tests__/job-controller-progress.spec.js +85 -0
- package/dist/__tests__/job-controller-progress.spec.js.map +1 -0
- package/dist/__tests__/jobs-cancel-route.spec.d.ts +2 -0
- package/dist/__tests__/jobs-cancel-route.spec.d.ts.map +1 -0
- package/dist/__tests__/jobs-cancel-route.spec.js +88 -0
- package/dist/__tests__/jobs-cancel-route.spec.js.map +1 -0
- package/dist/__tests__/llm-agent-stream.test.d.ts +14 -0
- package/dist/__tests__/llm-agent-stream.test.d.ts.map +1 -0
- package/dist/__tests__/llm-agent-stream.test.js +341 -0
- package/dist/__tests__/llm-agent-stream.test.js.map +1 -0
- package/dist/__tests__/llm-provider.test.js +22 -1
- package/dist/__tests__/llm-provider.test.js.map +1 -1
- package/dist/__tests__/media-resolver.test.js +40 -0
- package/dist/__tests__/media-resolver.test.js.map +1 -1
- package/dist/__tests__/mesh-job-submitter.spec.d.ts +2 -0
- package/dist/__tests__/mesh-job-submitter.spec.d.ts.map +1 -0
- package/dist/__tests__/mesh-job-submitter.spec.js +110 -0
- package/dist/__tests__/mesh-job-submitter.spec.js.map +1 -0
- package/dist/__tests__/proxy-stream.test.d.ts +9 -0
- package/dist/__tests__/proxy-stream.test.d.ts.map +1 -0
- package/dist/__tests__/proxy-stream.test.js +347 -0
- package/dist/__tests__/proxy-stream.test.js.map +1 -0
- package/dist/__tests__/resolver-meshjob.spec.d.ts +26 -0
- package/dist/__tests__/resolver-meshjob.spec.d.ts.map +1 -0
- package/dist/__tests__/resolver-meshjob.spec.js +201 -0
- package/dist/__tests__/resolver-meshjob.spec.js.map +1 -0
- package/dist/__tests__/schema-verdict-policy.test.d.ts +6 -0
- package/dist/__tests__/schema-verdict-policy.test.d.ts.map +1 -0
- package/dist/__tests__/schema-verdict-policy.test.js +126 -0
- package/dist/__tests__/schema-verdict-policy.test.js.map +1 -0
- package/dist/__tests__/sse-stream.test.d.ts +12 -0
- package/dist/__tests__/sse-stream.test.d.ts.map +1 -0
- package/dist/__tests__/sse-stream.test.js +170 -0
- package/dist/__tests__/sse-stream.test.js.map +1 -0
- package/dist/a2a/a2a-bearer.d.ts +27 -0
- package/dist/a2a/a2a-bearer.d.ts.map +1 -0
- package/dist/a2a/a2a-bearer.js +63 -0
- package/dist/a2a/a2a-bearer.js.map +1 -0
- package/dist/a2a/a2a-client.d.ts +114 -0
- package/dist/a2a/a2a-client.d.ts.map +1 -0
- package/dist/a2a/a2a-client.js +405 -0
- package/dist/a2a/a2a-client.js.map +1 -0
- package/dist/a2a/a2a-event.d.ts +25 -0
- package/dist/a2a/a2a-event.d.ts.map +1 -0
- package/dist/a2a/a2a-event.js +9 -0
- package/dist/a2a/a2a-event.js.map +1 -0
- package/dist/a2a/a2a-job.d.ts +58 -0
- package/dist/a2a/a2a-job.d.ts.map +1 -0
- package/dist/a2a/a2a-job.js +264 -0
- package/dist/a2a/a2a-job.js.map +1 -0
- package/dist/a2a/a2a-stream.d.ts +39 -0
- package/dist/a2a/a2a-stream.d.ts.map +1 -0
- package/dist/a2a/a2a-stream.js +290 -0
- package/dist/a2a/a2a-stream.js.map +1 -0
- package/dist/a2a/errors.d.ts +29 -0
- package/dist/a2a/errors.d.ts.map +1 -0
- package/dist/a2a/errors.js +48 -0
- package/dist/a2a/errors.js.map +1 -0
- package/dist/a2a/index.d.ts +12 -0
- package/dist/a2a/index.d.ts.map +1 -0
- package/dist/a2a/index.js +11 -0
- package/dist/a2a/index.js.map +1 -0
- package/dist/a2a/producer/auth-filter.d.ts +34 -0
- package/dist/a2a/producer/auth-filter.d.ts.map +1 -0
- package/dist/a2a/producer/auth-filter.js +39 -0
- package/dist/a2a/producer/auth-filter.js.map +1 -0
- package/dist/a2a/producer/card-builder.d.ts +59 -0
- package/dist/a2a/producer/card-builder.d.ts.map +1 -0
- package/dist/a2a/producer/card-builder.js +59 -0
- package/dist/a2a/producer/card-builder.js.map +1 -0
- package/dist/a2a/producer/dispatcher.d.ts +276 -0
- package/dist/a2a/producer/dispatcher.d.ts.map +1 -0
- package/dist/a2a/producer/dispatcher.js +896 -0
- package/dist/a2a/producer/dispatcher.js.map +1 -0
- package/dist/a2a/producer/index.d.ts +26 -0
- package/dist/a2a/producer/index.d.ts.map +1 -0
- package/dist/a2a/producer/index.js +23 -0
- package/dist/a2a/producer/index.js.map +1 -0
- package/dist/a2a/producer/mount.d.ts +75 -0
- package/dist/a2a/producer/mount.d.ts.map +1 -0
- package/dist/a2a/producer/mount.js +422 -0
- package/dist/a2a/producer/mount.js.map +1 -0
- package/dist/a2a/producer/public-url-cache.d.ts +73 -0
- package/dist/a2a/producer/public-url-cache.d.ts.map +1 -0
- package/dist/a2a/producer/public-url-cache.js +0 -0
- package/dist/a2a/producer/public-url-cache.js.map +1 -0
- package/dist/a2a/producer/registry.d.ts +138 -0
- package/dist/a2a/producer/registry.d.ts.map +1 -0
- package/dist/a2a/producer/registry.js +117 -0
- package/dist/a2a/producer/registry.js.map +1 -0
- package/dist/a2a/producer/sse-emitter.d.ts +85 -0
- package/dist/a2a/producer/sse-emitter.d.ts.map +1 -0
- package/dist/a2a/producer/sse-emitter.js +405 -0
- package/dist/a2a/producer/sse-emitter.js.map +1 -0
- package/dist/a2a/producer/state-translator.d.ts +63 -0
- package/dist/a2a/producer/state-translator.d.ts.map +1 -0
- package/dist/a2a/producer/state-translator.js +108 -0
- package/dist/a2a/producer/state-translator.js.map +1 -0
- package/dist/a2a/producer/task-store.d.ts +128 -0
- package/dist/a2a/producer/task-store.d.ts.map +1 -0
- package/dist/a2a/producer/task-store.js +128 -0
- package/dist/a2a/producer/task-store.js.map +1 -0
- package/dist/agent.d.ts +99 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +754 -19
- package/dist/agent.js.map +1 -1
- package/dist/api-runtime.d.ts +25 -0
- package/dist/api-runtime.d.ts.map +1 -1
- package/dist/api-runtime.js +75 -2
- package/dist/api-runtime.js.map +1 -1
- package/dist/claim-dispatcher.d.ts +126 -0
- package/dist/claim-dispatcher.d.ts.map +1 -0
- package/dist/claim-dispatcher.js +478 -0
- package/dist/claim-dispatcher.js.map +1 -0
- package/dist/express.d.ts.map +1 -1
- package/dist/express.js +33 -6
- package/dist/express.js.map +1 -1
- package/dist/inbound-job-dispatch.d.ts +105 -0
- package/dist/inbound-job-dispatch.d.ts.map +1 -0
- package/dist/inbound-job-dispatch.js +335 -0
- package/dist/inbound-job-dispatch.js.map +1 -0
- package/dist/index.d.ts +40 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -3
- package/dist/index.js.map +1 -1
- package/dist/job-context.d.ts +107 -0
- package/dist/job-context.d.ts.map +1 -0
- package/dist/job-context.js +95 -0
- package/dist/job-context.js.map +1 -0
- package/dist/jobs-cancel-route.d.ts +36 -0
- package/dist/jobs-cancel-route.d.ts.map +1 -0
- package/dist/jobs-cancel-route.js +60 -0
- package/dist/jobs-cancel-route.js.map +1 -0
- package/dist/jobs-helper-tools.d.ts +48 -0
- package/dist/jobs-helper-tools.d.ts.map +1 -0
- package/dist/jobs-helper-tools.js +133 -0
- package/dist/jobs-helper-tools.js.map +1 -0
- package/dist/llm-agent.d.ts +62 -53
- package/dist/llm-agent.d.ts.map +1 -1
- package/dist/llm-agent.js +211 -292
- package/dist/llm-agent.js.map +1 -1
- package/dist/llm-provider.d.ts +11 -4
- package/dist/llm-provider.d.ts.map +1 -1
- package/dist/llm-provider.js +57 -4
- package/dist/llm-provider.js.map +1 -1
- package/dist/llm.d.ts +4 -1
- package/dist/llm.d.ts.map +1 -1
- package/dist/llm.js +7 -17
- package/dist/llm.js.map +1 -1
- package/dist/media/resolver.d.ts.map +1 -1
- package/dist/media/resolver.js +3 -2
- package/dist/media/resolver.js.map +1 -1
- package/dist/mesh-job-submitter.d.ts +83 -0
- package/dist/mesh-job-submitter.d.ts.map +1 -0
- package/dist/mesh-job-submitter.js +143 -0
- package/dist/mesh-job-submitter.js.map +1 -0
- package/dist/provider-handlers/gemini-handler.js +5 -0
- package/dist/provider-handlers/gemini-handler.js.map +1 -1
- package/dist/proxy.d.ts +40 -0
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +375 -2
- package/dist/proxy.js.map +1 -1
- package/dist/resolver-meshjob.d.ts +170 -0
- package/dist/resolver-meshjob.d.ts.map +1 -0
- package/dist/resolver-meshjob.js +159 -0
- package/dist/resolver-meshjob.js.map +1 -0
- package/dist/route.d.ts +4 -0
- package/dist/route.d.ts.map +1 -1
- package/dist/route.js.map +1 -1
- package/dist/schema-normalize.d.ts +62 -0
- package/dist/schema-normalize.d.ts.map +1 -0
- package/dist/schema-normalize.js +128 -0
- package/dist/schema-normalize.js.map +1 -0
- package/dist/sse-stream.d.ts +44 -0
- package/dist/sse-stream.d.ts.map +1 -0
- package/dist/sse-stream.js +173 -0
- package/dist/sse-stream.js.map +1 -0
- package/dist/tool-worker-entry.d.ts +21 -0
- package/dist/tool-worker-entry.d.ts.map +1 -0
- package/dist/tool-worker-entry.js +162 -0
- package/dist/tool-worker-entry.js.map +1 -0
- package/dist/tool-worker-pool.d.ts +49 -0
- package/dist/tool-worker-pool.d.ts.map +1 -0
- package/dist/tool-worker-pool.js +272 -0
- package/dist/tool-worker-pool.js.map +1 -0
- package/dist/types.d.ts +351 -9
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the inbound MeshJob dispatch wrapper (Phase 1).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the behavioural envelope of Python's
|
|
5
|
+
* `tests/test_job_dispatch.py`. We don't bind to the napi-rs
|
|
6
|
+
* `JobController` here — those are exercised by the cross-FFI test
|
|
7
|
+
* suite. Instead we verify:
|
|
8
|
+
*
|
|
9
|
+
* - `readJobHeaders` returns the right shape on the standard
|
|
10
|
+
* headers + on absent / malformed inputs;
|
|
11
|
+
* - `runWithJobContext` runs the thunk inside `CURRENT_JOB.run`
|
|
12
|
+
* when a controller is present, and bypasses the wrap when
|
|
13
|
+
* either jobId or controller is null;
|
|
14
|
+
* - the auto-complete path fires only when the controller hadn't
|
|
15
|
+
* already gone terminal, and propagates user exceptions verbatim.
|
|
16
|
+
*/
|
|
17
|
+
import { describe, it, expect, vi } from "vitest";
|
|
18
|
+
import { readJobHeaders, runWithJobContext, } from "../inbound-job-dispatch.js";
|
|
19
|
+
import { currentJob } from "../job-context.js";
|
|
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]);
|
|
25
|
+
});
|
|
26
|
+
it("extracts job id only when X-Mesh-Timeout is absent", () => {
|
|
27
|
+
expect(readJobHeaders({ "x-mesh-job-id": "job-123" })).toEqual([
|
|
28
|
+
"job-123",
|
|
29
|
+
null,
|
|
30
|
+
]);
|
|
31
|
+
});
|
|
32
|
+
it("extracts both headers and parses timeout as float", () => {
|
|
33
|
+
expect(readJobHeaders({
|
|
34
|
+
"x-mesh-job-id": "job-456",
|
|
35
|
+
"x-mesh-timeout": "12.5",
|
|
36
|
+
})).toEqual(["job-456", 12.5]);
|
|
37
|
+
});
|
|
38
|
+
it("ignores malformed timeout (non-numeric / non-positive)", () => {
|
|
39
|
+
expect(readJobHeaders({
|
|
40
|
+
"x-mesh-job-id": "j1",
|
|
41
|
+
"x-mesh-timeout": "abc",
|
|
42
|
+
})).toEqual(["j1", null]);
|
|
43
|
+
expect(readJobHeaders({
|
|
44
|
+
"x-mesh-job-id": "j2",
|
|
45
|
+
"x-mesh-timeout": "0",
|
|
46
|
+
})).toEqual(["j2", null]);
|
|
47
|
+
expect(readJobHeaders({
|
|
48
|
+
"x-mesh-job-id": "j3",
|
|
49
|
+
"x-mesh-timeout": "-1.5",
|
|
50
|
+
})).toEqual(["j3", null]);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("runWithJobContext", () => {
|
|
54
|
+
it("runs the thunk directly when jobId or controller is null", async () => {
|
|
55
|
+
const result = await runWithJobContext(null, null, null, async () => {
|
|
56
|
+
// No active job inside the thunk.
|
|
57
|
+
expect(currentJob()).toBeNull();
|
|
58
|
+
return "ran";
|
|
59
|
+
});
|
|
60
|
+
expect(result).toBe("ran");
|
|
61
|
+
});
|
|
62
|
+
it("sets CURRENT_JOB inside the scope when controller is provided", async () => {
|
|
63
|
+
// Build a stub that mimics the JobController surface readWithJobContext
|
|
64
|
+
// touches: isTerminal() and complete(). The napi binding's actual
|
|
65
|
+
// behaviour is exercised in the Rust test suite.
|
|
66
|
+
const stub = {
|
|
67
|
+
isTerminal: vi.fn().mockResolvedValue(true), // already terminal → no auto-complete
|
|
68
|
+
complete: vi.fn().mockResolvedValue(undefined),
|
|
69
|
+
fail: vi.fn().mockResolvedValue(undefined),
|
|
70
|
+
};
|
|
71
|
+
const result = await runWithJobContext("job-abc", 30,
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
73
|
+
stub, async () => {
|
|
74
|
+
const cur = currentJob();
|
|
75
|
+
expect(cur?.jobId).toBe("job-abc");
|
|
76
|
+
expect(cur?.deadlineSecsRemaining).toBe(30);
|
|
77
|
+
return 42;
|
|
78
|
+
});
|
|
79
|
+
expect(result).toBe(42);
|
|
80
|
+
// Already-terminal probe → no auto-complete.
|
|
81
|
+
expect(stub.complete).not.toHaveBeenCalled();
|
|
82
|
+
// Outside the scope CURRENT_JOB is gone again.
|
|
83
|
+
expect(currentJob()).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
it("auto-completes when the controller is not yet terminal", async () => {
|
|
86
|
+
const stub = {
|
|
87
|
+
isTerminal: vi.fn().mockResolvedValue(false),
|
|
88
|
+
complete: vi.fn().mockResolvedValue(undefined),
|
|
89
|
+
fail: vi.fn().mockResolvedValue(undefined),
|
|
90
|
+
};
|
|
91
|
+
const userValue = { ok: true, n: 42 };
|
|
92
|
+
const result = await runWithJobContext("job-auto", null,
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
|
+
stub, async () => userValue);
|
|
95
|
+
// The user's value is captured in a closure and returned directly
|
|
96
|
+
// (no napi serde round-trip), so the same reference is preserved.
|
|
97
|
+
expect(result).toBe(userValue);
|
|
98
|
+
expect(stub.complete).toHaveBeenCalledOnce();
|
|
99
|
+
expect(stub.complete).toHaveBeenCalledWith(userValue);
|
|
100
|
+
});
|
|
101
|
+
it("returns non-JSON-safe values to the caller and wraps for auto-complete", async () => {
|
|
102
|
+
// Map / Set / class instance / Symbol — any of these would have
|
|
103
|
+
// tripped the napi serde-json round-trip with a cryptic Rust error
|
|
104
|
+
// before the fix. Now they pass through to the caller untouched
|
|
105
|
+
// (Rust never sees them) and the auto-complete wraps them as
|
|
106
|
+
// `{ value: String(...) }` for the registry.
|
|
107
|
+
const cases = [
|
|
108
|
+
{ name: "Map", build: () => new Map([["k", 1]]) },
|
|
109
|
+
{ name: "Set", build: () => new Set([1, 2, 3]) },
|
|
110
|
+
{
|
|
111
|
+
name: "class instance",
|
|
112
|
+
build: () => {
|
|
113
|
+
class Foo {
|
|
114
|
+
x = 1;
|
|
115
|
+
describe() {
|
|
116
|
+
return "foo";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return new Foo();
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{ name: "BigInt", build: () => BigInt(123) },
|
|
123
|
+
{ name: "undefined", build: () => undefined },
|
|
124
|
+
];
|
|
125
|
+
for (const c of cases) {
|
|
126
|
+
const stub = {
|
|
127
|
+
isTerminal: vi.fn().mockResolvedValue(false),
|
|
128
|
+
complete: vi.fn().mockResolvedValue(undefined),
|
|
129
|
+
fail: vi.fn().mockResolvedValue(undefined),
|
|
130
|
+
};
|
|
131
|
+
const value = c.build();
|
|
132
|
+
const result = await runWithJobContext(`job-${c.name}`, null,
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
134
|
+
stub, async () => value);
|
|
135
|
+
// The user value is preserved verbatim — it never crosses napi.
|
|
136
|
+
expect(result, `case=${c.name} return value`).toBe(value);
|
|
137
|
+
// And the registry sees a JSON-safe wrap.
|
|
138
|
+
expect(stub.complete, `case=${c.name} complete called`).toHaveBeenCalledOnce();
|
|
139
|
+
const completedWith = stub.complete.mock.calls[0][0];
|
|
140
|
+
expect(completedWith, `case=${c.name} complete payload is wrapped`).toMatchObject({ value: expect.any(String) });
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
it("auto-completes nested JSON-safe arrays/objects verbatim", async () => {
|
|
144
|
+
const stub = {
|
|
145
|
+
isTerminal: vi.fn().mockResolvedValue(false),
|
|
146
|
+
complete: vi.fn().mockResolvedValue(undefined),
|
|
147
|
+
fail: vi.fn().mockResolvedValue(undefined),
|
|
148
|
+
};
|
|
149
|
+
const userValue = { items: [1, 2, { nested: "ok" }], count: 3 };
|
|
150
|
+
await runWithJobContext("job-deep", null,
|
|
151
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
152
|
+
stub, async () => userValue);
|
|
153
|
+
// Nested-but-JSON-safe → forwarded verbatim, no { value: ... } wrap.
|
|
154
|
+
expect(stub.complete).toHaveBeenCalledWith(userValue);
|
|
155
|
+
});
|
|
156
|
+
it("calls fail() on user exception when not yet terminal", async () => {
|
|
157
|
+
const stub = {
|
|
158
|
+
isTerminal: vi.fn().mockResolvedValue(false),
|
|
159
|
+
complete: vi.fn().mockResolvedValue(undefined),
|
|
160
|
+
fail: vi.fn().mockResolvedValue(undefined),
|
|
161
|
+
};
|
|
162
|
+
await expect(runWithJobContext("job-fail", null,
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
164
|
+
stub, async () => {
|
|
165
|
+
throw new Error("boom");
|
|
166
|
+
})).rejects.toThrow("boom");
|
|
167
|
+
expect(stub.fail).toHaveBeenCalledOnce();
|
|
168
|
+
expect(stub.fail).toHaveBeenCalledWith("boom");
|
|
169
|
+
expect(stub.complete).not.toHaveBeenCalled();
|
|
170
|
+
});
|
|
171
|
+
it("does not call fail() when the controller is already terminal", async () => {
|
|
172
|
+
const stub = {
|
|
173
|
+
isTerminal: vi.fn().mockResolvedValue(true),
|
|
174
|
+
complete: vi.fn().mockResolvedValue(undefined),
|
|
175
|
+
fail: vi.fn().mockResolvedValue(undefined),
|
|
176
|
+
};
|
|
177
|
+
await expect(runWithJobContext("job-already", null,
|
|
178
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
179
|
+
stub, async () => {
|
|
180
|
+
throw new Error("ignored-fail");
|
|
181
|
+
})).rejects.toThrow("ignored-fail");
|
|
182
|
+
expect(stub.fail).not.toHaveBeenCalled();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
//# sourceMappingURL=inbound-job-dispatch.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"job-controller-progress.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/job-controller-progress.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for the napi `JobController` constructor's batching tick.
|
|
3
|
+
*
|
|
4
|
+
* The `#[napi(constructor)]` is invoked synchronously from JS context with
|
|
5
|
+
* NO ambient Tokio runtime — `Handle::try_current()` returns Err there,
|
|
6
|
+
* so an earlier implementation silently skipped `spawn_batching_tick` and
|
|
7
|
+
* mid-flight `updateProgress` calls accumulated in the coalescing queue
|
|
8
|
+
* forever (only `complete()`/`fail()` flushed because those are async napi
|
|
9
|
+
* methods that run inside napi-rs's Tokio runtime).
|
|
10
|
+
*
|
|
11
|
+
* The fix uses `napi::bindgen_prelude::within_runtime_if_available` so
|
|
12
|
+
* the tick spawns inside napi-rs's shared runtime regardless of whether
|
|
13
|
+
* the caller is sync or async — mirroring Python's pattern of entering
|
|
14
|
+
* `pyo3_async_runtimes::tokio::get_runtime()`.
|
|
15
|
+
*
|
|
16
|
+
* This test spins up a tiny HTTP server playing the role of the registry,
|
|
17
|
+
* constructs a real `JobController` against it, calls `updateProgress`,
|
|
18
|
+
* and asserts that the batching tick eventually POSTs to `/jobs/batch`
|
|
19
|
+
* with the queued delta — proving the tick actually runs.
|
|
20
|
+
*/
|
|
21
|
+
import { describe, it, expect } from "vitest";
|
|
22
|
+
import { JobController } from "@mcpmesh/core";
|
|
23
|
+
import http from "node:http";
|
|
24
|
+
function readBody(req) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const chunks = [];
|
|
27
|
+
req.on("data", (c) => chunks.push(c));
|
|
28
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
29
|
+
req.on("error", reject);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
describe("JobController batching tick", () => {
|
|
33
|
+
it("flushes updateProgress via the batching tick on a fake registry", async () => {
|
|
34
|
+
const recorded = [];
|
|
35
|
+
// Tiny registry mock: accept POST /jobs/batch, record body, return 200.
|
|
36
|
+
const server = http.createServer((req, res) => {
|
|
37
|
+
if (req.method === "POST" && req.url === "/jobs/batch") {
|
|
38
|
+
readBody(req)
|
|
39
|
+
.then((body) => {
|
|
40
|
+
recorded.push(JSON.parse(body));
|
|
41
|
+
res.statusCode = 200;
|
|
42
|
+
res.setHeader("content-type", "application/json");
|
|
43
|
+
res.end(JSON.stringify({ accepted: 1, rejected: [] }));
|
|
44
|
+
})
|
|
45
|
+
.catch(() => {
|
|
46
|
+
res.statusCode = 500;
|
|
47
|
+
res.end();
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
res.statusCode = 404;
|
|
52
|
+
res.end();
|
|
53
|
+
});
|
|
54
|
+
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
55
|
+
const port = server.address().port;
|
|
56
|
+
const registryUrl = `http://127.0.0.1:${port}`;
|
|
57
|
+
try {
|
|
58
|
+
// Construct a real napi JobController — this is the exact code path
|
|
59
|
+
// that was broken: the constructor must spawn the batching tick on
|
|
60
|
+
// napi-rs's runtime, not on `Handle::try_current()` (which returns
|
|
61
|
+
// Err from a synchronous JS context).
|
|
62
|
+
const ctrl = new JobController("job-batch-test", "inst-1", registryUrl);
|
|
63
|
+
await ctrl.updateProgress(0.5, "halfway");
|
|
64
|
+
// Default batching interval is 2s. Wait long enough for at least one
|
|
65
|
+
// tick to fire. If the tick wasn't spawned, recorded stays empty.
|
|
66
|
+
// Use 5s of slack so this isn't flaky on slow CI runners — 500ms
|
|
67
|
+
// of headroom over a 2s tick was too tight, the cost of ~2.5s
|
|
68
|
+
// extra wall time is acceptable for a single test.
|
|
69
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
70
|
+
expect(recorded.length, "batching tick must POST at least one batch with the queued progress delta").toBeGreaterThanOrEqual(1);
|
|
71
|
+
const seen = recorded[0];
|
|
72
|
+
expect(seen.instance_id).toBe("inst-1");
|
|
73
|
+
expect(seen.deltas.length).toBe(1);
|
|
74
|
+
expect(seen.deltas[0].id).toBe("job-batch-test");
|
|
75
|
+
expect(seen.deltas[0].progress).toBeCloseTo(0.5, 5);
|
|
76
|
+
expect(seen.deltas[0].progress_message).toBe("halfway");
|
|
77
|
+
// Mid-flight delta — no terminal status.
|
|
78
|
+
expect(seen.deltas[0].status ?? null).toBeNull();
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
82
|
+
}
|
|
83
|
+
}, 15_000);
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=job-controller-progress.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-controller-progress.spec.js","sourceRoot":"","sources":["../../src/__tests__/job-controller-progress.spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,IAAI,MAAM,WAAW,CAAC;AAa7B,SAAS,QAAQ,CAAC,GAAyB;IACzC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACrE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,QAAQ,GAAoB,EAAE,CAAC;QAErC,wEAAwE;QACxE,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5C,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,aAAa,EAAE,CAAC;gBACvD,QAAQ,CAAC,GAAG,CAAC;qBACV,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;oBACb,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAC,CAAC;oBACjD,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;oBACrB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;oBAClD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;gBACzD,CAAC,CAAC;qBACD,KAAK,CAAC,GAAG,EAAE;oBACV,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;oBACrB,GAAG,CAAC,GAAG,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;gBACL,OAAO;YACT,CAAC;YACD,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QAC7E,MAAM,IAAI,GAAI,MAAM,CAAC,OAAO,EAAkB,CAAC,IAAI,CAAC;QACpD,MAAM,WAAW,GAAG,oBAAoB,IAAI,EAAE,CAAC;QAE/C,IAAI,CAAC;YACH,oEAAoE;YACpE,mEAAmE;YACnE,mEAAmE;YACnE,sCAAsC;YACtC,MAAM,IAAI,GAAG,IAAI,aAAa,CAAC,gBAAgB,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;YAExE,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAE1C,qEAAqE;YACrE,kEAAkE;YAClE,iEAAiE;YACjE,8DAA8D;YAC9D,mDAAmD;YACnD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;YAE9C,MAAM,CACJ,QAAQ,CAAC,MAAM,EACf,2EAA2E,CAC5E,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;YAE5B,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACpD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxD,yCAAyC;YACzC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;QACnD,CAAC;gBAAS,CAAC;YACT,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC,EAAE,MAAM,CAAC,CAAC;AACb,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs-cancel-route.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/jobs-cancel-route.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cancel-route registration visibility tests (W5 — PR review finding).
|
|
3
|
+
*
|
|
4
|
+
* Cancel-mid-flight is a primary control-plane signal for long-running
|
|
5
|
+
* jobs. Before the fix, registration failures landed at `console.warn`
|
|
6
|
+
* — operators who only watched `error`-level logs would never see the
|
|
7
|
+
* regression and the agent would silently degrade to lease-expiry
|
|
8
|
+
* cancellation.
|
|
9
|
+
*
|
|
10
|
+
* This file verifies the escalation:
|
|
11
|
+
* - failures land at `console.error`;
|
|
12
|
+
* - the message includes the underlying reason so the failure mode
|
|
13
|
+
* is debuggable from logs alone.
|
|
14
|
+
*/
|
|
15
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
16
|
+
import { registerCancelRoute } from "../jobs-cancel-route.js";
|
|
17
|
+
describe("registerCancelRoute — visibility", () => {
|
|
18
|
+
let errorSpy;
|
|
19
|
+
let warnSpy;
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
errorSpy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
22
|
+
warnSpy = vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
23
|
+
});
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
errorSpy.mockRestore();
|
|
26
|
+
warnSpy.mockRestore();
|
|
27
|
+
});
|
|
28
|
+
it("logs at console.error (not warn) when getApp() throws, including the reason", () => {
|
|
29
|
+
const stubServer = {
|
|
30
|
+
getApp: () => {
|
|
31
|
+
throw new Error("FastMCP server not started");
|
|
32
|
+
},
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
};
|
|
35
|
+
const ok = registerCancelRoute(stubServer);
|
|
36
|
+
expect(ok).toBe(false);
|
|
37
|
+
// Escalated to error, NOT warn.
|
|
38
|
+
expect(errorSpy).toHaveBeenCalledOnce();
|
|
39
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
40
|
+
// Reason is included so operators can debug from logs alone.
|
|
41
|
+
const msg = errorSpy.mock.calls[0][0];
|
|
42
|
+
expect(msg).toContain("cancel route NOT registered");
|
|
43
|
+
expect(msg).toContain("FastMCP server not started");
|
|
44
|
+
});
|
|
45
|
+
it("logs at console.error when getApp() returns null", () => {
|
|
46
|
+
const stubServer = {
|
|
47
|
+
getApp: () => null,
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
+
};
|
|
50
|
+
const ok = registerCancelRoute(stubServer);
|
|
51
|
+
expect(ok).toBe(false);
|
|
52
|
+
expect(errorSpy).toHaveBeenCalledOnce();
|
|
53
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
54
|
+
const msg = errorSpy.mock.calls[0][0];
|
|
55
|
+
expect(msg).toContain("cancel route NOT registered");
|
|
56
|
+
expect(msg).toContain("returned null");
|
|
57
|
+
});
|
|
58
|
+
it("logs at console.error when app.post() raises, including the reason", () => {
|
|
59
|
+
const stubServer = {
|
|
60
|
+
getApp: () => ({
|
|
61
|
+
post: () => {
|
|
62
|
+
throw new Error("hono internal: route conflict");
|
|
63
|
+
},
|
|
64
|
+
}),
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
};
|
|
67
|
+
const ok = registerCancelRoute(stubServer);
|
|
68
|
+
expect(ok).toBe(false);
|
|
69
|
+
expect(errorSpy).toHaveBeenCalledOnce();
|
|
70
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
71
|
+
const msg = errorSpy.mock.calls[0][0];
|
|
72
|
+
expect(msg).toContain("cancel route NOT registered");
|
|
73
|
+
expect(msg).toContain("hono internal: route conflict");
|
|
74
|
+
});
|
|
75
|
+
it("returns true and does not log anything when registration succeeds", () => {
|
|
76
|
+
const postSpy = vi.fn();
|
|
77
|
+
const stubServer = {
|
|
78
|
+
getApp: () => ({ post: postSpy }),
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
+
};
|
|
81
|
+
const ok = registerCancelRoute(stubServer);
|
|
82
|
+
expect(ok).toBe(true);
|
|
83
|
+
expect(postSpy).toHaveBeenCalledWith("/jobs/:job_id/cancel", expect.any(Function));
|
|
84
|
+
expect(errorSpy).not.toHaveBeenCalled();
|
|
85
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=jobs-cancel-route.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs-cancel-route.spec.js","sourceRoot":"","sources":["../../src/__tests__/jobs-cancel-route.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for MeshLlmAgent.stream() — Stage 3 of issue #854.
|
|
3
|
+
*
|
|
4
|
+
* Covers:
|
|
5
|
+
* - Mesh-delegate path: chunks come through from a mocked process_chat_stream
|
|
6
|
+
* - The {request: <MeshLlmRequest>} body shape is preserved
|
|
7
|
+
* - Calling stream() without a meshProvider throws a clear error
|
|
8
|
+
* - The createCallable's .stream() exposes the same iterable
|
|
9
|
+
* - Tag-based discrimination: provider tags (including ai.mcpmesh.stream) are
|
|
10
|
+
* forwarded verbatim to buildLlmAgentSpecs() so the registry resolver can
|
|
11
|
+
* pick the streaming variant.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=llm-agent-stream.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-agent-stream.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/llm-agent-stream.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|