@mcpmesh/sdk 1.4.1 → 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.
Files changed (242) hide show
  1. package/dist/__tests__/a2a/a2a-bearer.spec.d.ts +2 -0
  2. package/dist/__tests__/a2a/a2a-bearer.spec.d.ts.map +1 -0
  3. package/dist/__tests__/a2a/a2a-bearer.spec.js +58 -0
  4. package/dist/__tests__/a2a/a2a-bearer.spec.js.map +1 -0
  5. package/dist/__tests__/a2a/a2a-client.spec.d.ts +2 -0
  6. package/dist/__tests__/a2a/a2a-client.spec.d.ts.map +1 -0
  7. package/dist/__tests__/a2a/a2a-client.spec.js +334 -0
  8. package/dist/__tests__/a2a/a2a-client.spec.js.map +1 -0
  9. package/dist/__tests__/a2a/a2a-job.spec.d.ts +2 -0
  10. package/dist/__tests__/a2a/a2a-job.spec.d.ts.map +1 -0
  11. package/dist/__tests__/a2a/a2a-job.spec.js +255 -0
  12. package/dist/__tests__/a2a/a2a-job.spec.js.map +1 -0
  13. package/dist/__tests__/a2a/a2a-stream.spec.d.ts +2 -0
  14. package/dist/__tests__/a2a/a2a-stream.spec.d.ts.map +1 -0
  15. package/dist/__tests__/a2a/a2a-stream.spec.js +278 -0
  16. package/dist/__tests__/a2a/a2a-stream.spec.js.map +1 -0
  17. package/dist/__tests__/a2a/agent-a2a-config.spec.d.ts +2 -0
  18. package/dist/__tests__/a2a/agent-a2a-config.spec.d.ts.map +1 -0
  19. package/dist/__tests__/a2a/agent-a2a-config.spec.js +262 -0
  20. package/dist/__tests__/a2a/agent-a2a-config.spec.js.map +1 -0
  21. package/dist/__tests__/a2a/producer/auth-filter.spec.d.ts +2 -0
  22. package/dist/__tests__/a2a/producer/auth-filter.spec.d.ts.map +1 -0
  23. package/dist/__tests__/a2a/producer/auth-filter.spec.js +127 -0
  24. package/dist/__tests__/a2a/producer/auth-filter.spec.js.map +1 -0
  25. package/dist/__tests__/a2a/producer/card-builder.spec.d.ts +2 -0
  26. package/dist/__tests__/a2a/producer/card-builder.spec.d.ts.map +1 -0
  27. package/dist/__tests__/a2a/producer/card-builder.spec.js +113 -0
  28. package/dist/__tests__/a2a/producer/card-builder.spec.js.map +1 -0
  29. package/dist/__tests__/a2a/producer/dispatcher.spec.d.ts +2 -0
  30. package/dist/__tests__/a2a/producer/dispatcher.spec.d.ts.map +1 -0
  31. package/dist/__tests__/a2a/producer/dispatcher.spec.js +850 -0
  32. package/dist/__tests__/a2a/producer/dispatcher.spec.js.map +1 -0
  33. package/dist/__tests__/a2a/producer/mount-surface-push.spec.d.ts +2 -0
  34. package/dist/__tests__/a2a/producer/mount-surface-push.spec.d.ts.map +1 -0
  35. package/dist/__tests__/a2a/producer/mount-surface-push.spec.js +164 -0
  36. package/dist/__tests__/a2a/producer/mount-surface-push.spec.js.map +1 -0
  37. package/dist/__tests__/a2a/producer/mount.spec.d.ts +2 -0
  38. package/dist/__tests__/a2a/producer/mount.spec.d.ts.map +1 -0
  39. package/dist/__tests__/a2a/producer/mount.spec.js +433 -0
  40. package/dist/__tests__/a2a/producer/mount.spec.js.map +1 -0
  41. package/dist/__tests__/a2a/producer/public-url-cache.spec.d.ts +2 -0
  42. package/dist/__tests__/a2a/producer/public-url-cache.spec.d.ts.map +1 -0
  43. package/dist/__tests__/a2a/producer/public-url-cache.spec.js +116 -0
  44. package/dist/__tests__/a2a/producer/public-url-cache.spec.js.map +1 -0
  45. package/dist/__tests__/a2a/producer/sse-emitter.spec.d.ts +2 -0
  46. package/dist/__tests__/a2a/producer/sse-emitter.spec.d.ts.map +1 -0
  47. package/dist/__tests__/a2a/producer/sse-emitter.spec.js +754 -0
  48. package/dist/__tests__/a2a/producer/sse-emitter.spec.js.map +1 -0
  49. package/dist/__tests__/a2a/producer/state-translator.spec.d.ts +2 -0
  50. package/dist/__tests__/a2a/producer/state-translator.spec.d.ts.map +1 -0
  51. package/dist/__tests__/a2a/producer/state-translator.spec.js +124 -0
  52. package/dist/__tests__/a2a/producer/state-translator.spec.js.map +1 -0
  53. package/dist/__tests__/a2a/producer/task-store.spec.d.ts +2 -0
  54. package/dist/__tests__/a2a/producer/task-store.spec.d.ts.map +1 -0
  55. package/dist/__tests__/a2a/producer/task-store.spec.js +180 -0
  56. package/dist/__tests__/a2a/producer/task-store.spec.js.map +1 -0
  57. package/dist/__tests__/agent-add-tool.spec.d.ts +2 -0
  58. package/dist/__tests__/agent-add-tool.spec.d.ts.map +1 -0
  59. package/dist/__tests__/agent-add-tool.spec.js +483 -0
  60. package/dist/__tests__/agent-add-tool.spec.js.map +1 -0
  61. package/dist/__tests__/api-runtime-race.spec.d.ts +2 -0
  62. package/dist/__tests__/api-runtime-race.spec.d.ts.map +1 -0
  63. package/dist/__tests__/api-runtime-race.spec.js +193 -0
  64. package/dist/__tests__/api-runtime-race.spec.js.map +1 -0
  65. package/dist/__tests__/claim-dispatcher.spec.d.ts +2 -0
  66. package/dist/__tests__/claim-dispatcher.spec.d.ts.map +1 -0
  67. package/dist/__tests__/claim-dispatcher.spec.js +408 -0
  68. package/dist/__tests__/claim-dispatcher.spec.js.map +1 -0
  69. package/dist/__tests__/inbound-job-dispatch.spec.d.ts +2 -0
  70. package/dist/__tests__/inbound-job-dispatch.spec.d.ts.map +1 -0
  71. package/dist/__tests__/inbound-job-dispatch.spec.js +185 -0
  72. package/dist/__tests__/inbound-job-dispatch.spec.js.map +1 -0
  73. package/dist/__tests__/job-controller-progress.spec.d.ts +2 -0
  74. package/dist/__tests__/job-controller-progress.spec.d.ts.map +1 -0
  75. package/dist/__tests__/job-controller-progress.spec.js +85 -0
  76. package/dist/__tests__/job-controller-progress.spec.js.map +1 -0
  77. package/dist/__tests__/jobs-cancel-route.spec.d.ts +2 -0
  78. package/dist/__tests__/jobs-cancel-route.spec.d.ts.map +1 -0
  79. package/dist/__tests__/jobs-cancel-route.spec.js +88 -0
  80. package/dist/__tests__/jobs-cancel-route.spec.js.map +1 -0
  81. package/dist/__tests__/llm-agent-stream.test.d.ts +14 -0
  82. package/dist/__tests__/llm-agent-stream.test.d.ts.map +1 -0
  83. package/dist/__tests__/llm-agent-stream.test.js +341 -0
  84. package/dist/__tests__/llm-agent-stream.test.js.map +1 -0
  85. package/dist/__tests__/mesh-job-submitter.spec.d.ts +2 -0
  86. package/dist/__tests__/mesh-job-submitter.spec.d.ts.map +1 -0
  87. package/dist/__tests__/mesh-job-submitter.spec.js +110 -0
  88. package/dist/__tests__/mesh-job-submitter.spec.js.map +1 -0
  89. package/dist/__tests__/proxy-stream.test.d.ts +9 -0
  90. package/dist/__tests__/proxy-stream.test.d.ts.map +1 -0
  91. package/dist/__tests__/proxy-stream.test.js +347 -0
  92. package/dist/__tests__/proxy-stream.test.js.map +1 -0
  93. package/dist/__tests__/resolver-meshjob.spec.d.ts +26 -0
  94. package/dist/__tests__/resolver-meshjob.spec.d.ts.map +1 -0
  95. package/dist/__tests__/resolver-meshjob.spec.js +201 -0
  96. package/dist/__tests__/resolver-meshjob.spec.js.map +1 -0
  97. package/dist/__tests__/schema-verdict-policy.test.d.ts +6 -0
  98. package/dist/__tests__/schema-verdict-policy.test.d.ts.map +1 -0
  99. package/dist/__tests__/schema-verdict-policy.test.js +126 -0
  100. package/dist/__tests__/schema-verdict-policy.test.js.map +1 -0
  101. package/dist/__tests__/sse-stream.test.d.ts +12 -0
  102. package/dist/__tests__/sse-stream.test.d.ts.map +1 -0
  103. package/dist/__tests__/sse-stream.test.js +170 -0
  104. package/dist/__tests__/sse-stream.test.js.map +1 -0
  105. package/dist/a2a/a2a-bearer.d.ts +27 -0
  106. package/dist/a2a/a2a-bearer.d.ts.map +1 -0
  107. package/dist/a2a/a2a-bearer.js +63 -0
  108. package/dist/a2a/a2a-bearer.js.map +1 -0
  109. package/dist/a2a/a2a-client.d.ts +114 -0
  110. package/dist/a2a/a2a-client.d.ts.map +1 -0
  111. package/dist/a2a/a2a-client.js +405 -0
  112. package/dist/a2a/a2a-client.js.map +1 -0
  113. package/dist/a2a/a2a-event.d.ts +25 -0
  114. package/dist/a2a/a2a-event.d.ts.map +1 -0
  115. package/dist/a2a/a2a-event.js +9 -0
  116. package/dist/a2a/a2a-event.js.map +1 -0
  117. package/dist/a2a/a2a-job.d.ts +58 -0
  118. package/dist/a2a/a2a-job.d.ts.map +1 -0
  119. package/dist/a2a/a2a-job.js +264 -0
  120. package/dist/a2a/a2a-job.js.map +1 -0
  121. package/dist/a2a/a2a-stream.d.ts +39 -0
  122. package/dist/a2a/a2a-stream.d.ts.map +1 -0
  123. package/dist/a2a/a2a-stream.js +290 -0
  124. package/dist/a2a/a2a-stream.js.map +1 -0
  125. package/dist/a2a/errors.d.ts +29 -0
  126. package/dist/a2a/errors.d.ts.map +1 -0
  127. package/dist/a2a/errors.js +48 -0
  128. package/dist/a2a/errors.js.map +1 -0
  129. package/dist/a2a/index.d.ts +12 -0
  130. package/dist/a2a/index.d.ts.map +1 -0
  131. package/dist/a2a/index.js +11 -0
  132. package/dist/a2a/index.js.map +1 -0
  133. package/dist/a2a/producer/auth-filter.d.ts +34 -0
  134. package/dist/a2a/producer/auth-filter.d.ts.map +1 -0
  135. package/dist/a2a/producer/auth-filter.js +39 -0
  136. package/dist/a2a/producer/auth-filter.js.map +1 -0
  137. package/dist/a2a/producer/card-builder.d.ts +59 -0
  138. package/dist/a2a/producer/card-builder.d.ts.map +1 -0
  139. package/dist/a2a/producer/card-builder.js +59 -0
  140. package/dist/a2a/producer/card-builder.js.map +1 -0
  141. package/dist/a2a/producer/dispatcher.d.ts +276 -0
  142. package/dist/a2a/producer/dispatcher.d.ts.map +1 -0
  143. package/dist/a2a/producer/dispatcher.js +896 -0
  144. package/dist/a2a/producer/dispatcher.js.map +1 -0
  145. package/dist/a2a/producer/index.d.ts +26 -0
  146. package/dist/a2a/producer/index.d.ts.map +1 -0
  147. package/dist/a2a/producer/index.js +23 -0
  148. package/dist/a2a/producer/index.js.map +1 -0
  149. package/dist/a2a/producer/mount.d.ts +75 -0
  150. package/dist/a2a/producer/mount.d.ts.map +1 -0
  151. package/dist/a2a/producer/mount.js +422 -0
  152. package/dist/a2a/producer/mount.js.map +1 -0
  153. package/dist/a2a/producer/public-url-cache.d.ts +73 -0
  154. package/dist/a2a/producer/public-url-cache.d.ts.map +1 -0
  155. package/dist/a2a/producer/public-url-cache.js +0 -0
  156. package/dist/a2a/producer/public-url-cache.js.map +1 -0
  157. package/dist/a2a/producer/registry.d.ts +138 -0
  158. package/dist/a2a/producer/registry.d.ts.map +1 -0
  159. package/dist/a2a/producer/registry.js +117 -0
  160. package/dist/a2a/producer/registry.js.map +1 -0
  161. package/dist/a2a/producer/sse-emitter.d.ts +85 -0
  162. package/dist/a2a/producer/sse-emitter.d.ts.map +1 -0
  163. package/dist/a2a/producer/sse-emitter.js +405 -0
  164. package/dist/a2a/producer/sse-emitter.js.map +1 -0
  165. package/dist/a2a/producer/state-translator.d.ts +63 -0
  166. package/dist/a2a/producer/state-translator.d.ts.map +1 -0
  167. package/dist/a2a/producer/state-translator.js +108 -0
  168. package/dist/a2a/producer/state-translator.js.map +1 -0
  169. package/dist/a2a/producer/task-store.d.ts +128 -0
  170. package/dist/a2a/producer/task-store.d.ts.map +1 -0
  171. package/dist/a2a/producer/task-store.js +128 -0
  172. package/dist/a2a/producer/task-store.js.map +1 -0
  173. package/dist/agent.d.ts +72 -0
  174. package/dist/agent.d.ts.map +1 -1
  175. package/dist/agent.js +618 -13
  176. package/dist/agent.js.map +1 -1
  177. package/dist/api-runtime.d.ts +25 -0
  178. package/dist/api-runtime.d.ts.map +1 -1
  179. package/dist/api-runtime.js +75 -2
  180. package/dist/api-runtime.js.map +1 -1
  181. package/dist/claim-dispatcher.d.ts +126 -0
  182. package/dist/claim-dispatcher.d.ts.map +1 -0
  183. package/dist/claim-dispatcher.js +478 -0
  184. package/dist/claim-dispatcher.js.map +1 -0
  185. package/dist/express.d.ts.map +1 -1
  186. package/dist/express.js +33 -6
  187. package/dist/express.js.map +1 -1
  188. package/dist/inbound-job-dispatch.d.ts +105 -0
  189. package/dist/inbound-job-dispatch.d.ts.map +1 -0
  190. package/dist/inbound-job-dispatch.js +335 -0
  191. package/dist/inbound-job-dispatch.js.map +1 -0
  192. package/dist/index.d.ts +37 -4
  193. package/dist/index.d.ts.map +1 -1
  194. package/dist/index.js +29 -3
  195. package/dist/index.js.map +1 -1
  196. package/dist/job-context.d.ts +107 -0
  197. package/dist/job-context.d.ts.map +1 -0
  198. package/dist/job-context.js +95 -0
  199. package/dist/job-context.js.map +1 -0
  200. package/dist/jobs-cancel-route.d.ts +36 -0
  201. package/dist/jobs-cancel-route.d.ts.map +1 -0
  202. package/dist/jobs-cancel-route.js +60 -0
  203. package/dist/jobs-cancel-route.js.map +1 -0
  204. package/dist/jobs-helper-tools.d.ts +48 -0
  205. package/dist/jobs-helper-tools.d.ts.map +1 -0
  206. package/dist/jobs-helper-tools.js +133 -0
  207. package/dist/jobs-helper-tools.js.map +1 -0
  208. package/dist/llm-agent.d.ts +62 -53
  209. package/dist/llm-agent.d.ts.map +1 -1
  210. package/dist/llm-agent.js +211 -292
  211. package/dist/llm-agent.js.map +1 -1
  212. package/dist/llm-provider.d.ts +4 -4
  213. package/dist/llm.d.ts +4 -1
  214. package/dist/llm.d.ts.map +1 -1
  215. package/dist/llm.js +7 -17
  216. package/dist/llm.js.map +1 -1
  217. package/dist/mesh-job-submitter.d.ts +83 -0
  218. package/dist/mesh-job-submitter.d.ts.map +1 -0
  219. package/dist/mesh-job-submitter.js +143 -0
  220. package/dist/mesh-job-submitter.js.map +1 -0
  221. package/dist/proxy.d.ts +30 -0
  222. package/dist/proxy.d.ts.map +1 -1
  223. package/dist/proxy.js +351 -1
  224. package/dist/proxy.js.map +1 -1
  225. package/dist/resolver-meshjob.d.ts +170 -0
  226. package/dist/resolver-meshjob.d.ts.map +1 -0
  227. package/dist/resolver-meshjob.js +159 -0
  228. package/dist/resolver-meshjob.js.map +1 -0
  229. package/dist/route.d.ts +4 -0
  230. package/dist/route.d.ts.map +1 -1
  231. package/dist/route.js.map +1 -1
  232. package/dist/schema-normalize.d.ts +62 -0
  233. package/dist/schema-normalize.d.ts.map +1 -0
  234. package/dist/schema-normalize.js +128 -0
  235. package/dist/schema-normalize.js.map +1 -0
  236. package/dist/sse-stream.d.ts +44 -0
  237. package/dist/sse-stream.d.ts.map +1 -0
  238. package/dist/sse-stream.js +173 -0
  239. package/dist/sse-stream.js.map +1 -0
  240. package/dist/types.d.ts +351 -9
  241. package/dist/types.d.ts.map +1 -1
  242. package/package.json +4 -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=job-controller-progress.spec.d.ts.map
@@ -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":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=jobs-cancel-route.spec.d.ts.map
@@ -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"}