@mcpmesh/sdk 2.6.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/__tests__/inbound-job-dispatch.spec.js +21 -8
  2. package/dist/__tests__/inbound-job-dispatch.spec.js.map +1 -1
  3. package/dist/__tests__/llm-agent-content-recovery.test.d.ts +17 -0
  4. package/dist/__tests__/llm-agent-content-recovery.test.d.ts.map +1 -0
  5. package/dist/__tests__/llm-agent-content-recovery.test.js +203 -0
  6. package/dist/__tests__/llm-agent-content-recovery.test.js.map +1 -0
  7. package/dist/__tests__/llm-provider-multistep.test.js +30 -0
  8. package/dist/__tests__/llm-provider-multistep.test.js.map +1 -1
  9. package/dist/__tests__/llm-provider-sampling-gating.test.d.ts +20 -0
  10. package/dist/__tests__/llm-provider-sampling-gating.test.d.ts.map +1 -0
  11. package/dist/__tests__/llm-provider-sampling-gating.test.js +192 -0
  12. package/dist/__tests__/llm-provider-sampling-gating.test.js.map +1 -0
  13. package/dist/__tests__/llm-token-telemetry.test.d.ts +17 -0
  14. package/dist/__tests__/llm-token-telemetry.test.d.ts.map +1 -0
  15. package/dist/__tests__/llm-token-telemetry.test.js +199 -0
  16. package/dist/__tests__/llm-token-telemetry.test.js.map +1 -0
  17. package/dist/__tests__/proxy-empty-content.test.d.ts +16 -0
  18. package/dist/__tests__/proxy-empty-content.test.d.ts.map +1 -0
  19. package/dist/__tests__/proxy-empty-content.test.js +124 -0
  20. package/dist/__tests__/proxy-empty-content.test.js.map +1 -0
  21. package/dist/__tests__/proxy-resource-link.test.js +4 -2
  22. package/dist/__tests__/proxy-resource-link.test.js.map +1 -1
  23. package/dist/__tests__/proxy-tool-error.test.js +3 -2
  24. package/dist/__tests__/proxy-tool-error.test.js.map +1 -1
  25. package/dist/__tests__/resolver-meshjob.spec.js +2 -2
  26. package/dist/__tests__/resolver-meshjob.spec.js.map +1 -1
  27. package/dist/__tests__/route-required.spec.d.ts +9 -0
  28. package/dist/__tests__/route-required.spec.d.ts.map +1 -0
  29. package/dist/__tests__/route-required.spec.js +154 -0
  30. package/dist/__tests__/route-required.spec.js.map +1 -0
  31. package/dist/__tests__/unwired-slot-warn.spec.d.ts +2 -0
  32. package/dist/__tests__/unwired-slot-warn.spec.d.ts.map +1 -0
  33. package/dist/__tests__/unwired-slot-warn.spec.js +167 -0
  34. package/dist/__tests__/unwired-slot-warn.spec.js.map +1 -0
  35. package/dist/agent.d.ts +19 -0
  36. package/dist/agent.d.ts.map +1 -1
  37. package/dist/agent.js +61 -19
  38. package/dist/agent.js.map +1 -1
  39. package/dist/api-runtime.d.ts.map +1 -1
  40. package/dist/api-runtime.js +3 -0
  41. package/dist/api-runtime.js.map +1 -1
  42. package/dist/claim-dispatcher.d.ts.map +1 -1
  43. package/dist/claim-dispatcher.js +12 -2
  44. package/dist/claim-dispatcher.js.map +1 -1
  45. package/dist/express.d.ts.map +1 -1
  46. package/dist/express.js +3 -0
  47. package/dist/express.js.map +1 -1
  48. package/dist/inbound-job-dispatch.d.ts +9 -6
  49. package/dist/inbound-job-dispatch.d.ts.map +1 -1
  50. package/dist/inbound-job-dispatch.js +35 -10
  51. package/dist/inbound-job-dispatch.js.map +1 -1
  52. package/dist/job-context.d.ts +9 -0
  53. package/dist/job-context.d.ts.map +1 -1
  54. package/dist/job-context.js.map +1 -1
  55. package/dist/jobs.d.ts +8 -4
  56. package/dist/jobs.d.ts.map +1 -1
  57. package/dist/jobs.js +8 -4
  58. package/dist/jobs.js.map +1 -1
  59. package/dist/llm-agent.d.ts +3 -0
  60. package/dist/llm-agent.d.ts.map +1 -1
  61. package/dist/llm-agent.js +72 -7
  62. package/dist/llm-agent.js.map +1 -1
  63. package/dist/llm-provider.d.ts +31 -0
  64. package/dist/llm-provider.d.ts.map +1 -1
  65. package/dist/llm-provider.js +98 -15
  66. package/dist/llm-provider.js.map +1 -1
  67. package/dist/llm.d.ts.map +1 -1
  68. package/dist/llm.js +34 -2
  69. package/dist/llm.js.map +1 -1
  70. package/dist/proxy.d.ts +13 -2
  71. package/dist/proxy.d.ts.map +1 -1
  72. package/dist/proxy.js +83 -11
  73. package/dist/proxy.js.map +1 -1
  74. package/dist/route.d.ts +3 -0
  75. package/dist/route.d.ts.map +1 -1
  76. package/dist/route.js +42 -0
  77. package/dist/route.js.map +1 -1
  78. package/dist/types.d.ts +25 -0
  79. package/dist/types.d.ts.map +1 -1
  80. package/package.json +3 -3
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Issue #1249: per-dependency `required` flag + route perimeter 503.
3
+ *
4
+ * Uses the REAL `normalizeDependency` (proxy.js) and the REAL route
5
+ * middleware — no proxy mock — so the declaration → normalized-dep → wire
6
+ * mapping and the perimeter check are genuinely exercised.
7
+ */
8
+ import { describe, it, expect, vi, beforeEach, afterAll } from "vitest";
9
+ import { normalizeDependency } from "../proxy.js";
10
+ import { route, RouteRegistry } from "../route.js";
11
+ import { resetSettleStateForTests } from "../settle.js";
12
+ // Disable the settling-window grace (#1193) so the perimeter is judged
13
+ // immediately (MCP_MESH_SETTLE_TIMEOUT=0 tuning case). The perimeter runs
14
+ // AFTER the settle wait, so with the grace on a still-settling dep would get
15
+ // its full window before being called unavailable — covered elsewhere.
16
+ const savedSettleTimeout = process.env.MCP_MESH_SETTLE_TIMEOUT;
17
+ beforeEach(() => {
18
+ process.env.MCP_MESH_SETTLE_TIMEOUT = "0";
19
+ resetSettleStateForTests();
20
+ RouteRegistry.reset();
21
+ });
22
+ afterAll(() => {
23
+ if (savedSettleTimeout === undefined) {
24
+ delete process.env.MCP_MESH_SETTLE_TIMEOUT;
25
+ }
26
+ else {
27
+ process.env.MCP_MESH_SETTLE_TIMEOUT = savedSettleTimeout;
28
+ }
29
+ resetSettleStateForTests();
30
+ });
31
+ function mockRes() {
32
+ return {
33
+ status: vi.fn().mockReturnThis(),
34
+ json: vi.fn().mockReturnThis(),
35
+ };
36
+ }
37
+ describe("normalizeDependency required flag (#1249)", () => {
38
+ it("carries required:true through to the normalized dep (napi wire seam)", () => {
39
+ const norm = normalizeDependency({ capability: "weather-api", required: true });
40
+ expect(norm.capability).toBe("weather-api");
41
+ expect(norm.required).toBe(true);
42
+ // The three JsDependencySpec builders map `dep.required ? true : undefined`,
43
+ // so a true here becomes required:true on the napi DependencySpec.
44
+ expect(norm.required ? true : undefined).toBe(true);
45
+ });
46
+ it("omits required when declared false (byte-identical soft-fail default)", () => {
47
+ const norm = normalizeDependency({ capability: "weather-api", required: false });
48
+ expect(norm.required).toBeUndefined();
49
+ expect(norm.required ? true : undefined).toBeUndefined();
50
+ });
51
+ it("omits required when absent (default optional)", () => {
52
+ const norm = normalizeDependency({ capability: "weather-api" });
53
+ expect(norm.required).toBeUndefined();
54
+ });
55
+ it("string-form deps are always optional", () => {
56
+ const norm = normalizeDependency("weather-api");
57
+ expect(norm.required).toBeUndefined();
58
+ });
59
+ });
60
+ describe("RouteRegistry stores required in route metadata (#1249)", () => {
61
+ it("persists required:true on the declared route dep", () => {
62
+ const registry = RouteRegistry.getInstance();
63
+ const routeId = registry.registerRoute("GET", "/x", [
64
+ { capability: "weather-api", required: true },
65
+ { capability: "logger" },
66
+ ]);
67
+ const meta = registry.getRoute(routeId);
68
+ expect(meta?.dependencies[0].required).toBe(true);
69
+ expect(meta?.dependencies[1].required).toBeUndefined();
70
+ });
71
+ });
72
+ describe("route perimeter 503 (#1249)", () => {
73
+ it("returns 503 before user code when a required dep is unavailable", async () => {
74
+ const handler = vi.fn();
75
+ const middleware = route([{ capability: "calculator", required: true }], handler);
76
+ const req = { method: "POST", path: "/compute", headers: {} };
77
+ const res = mockRes();
78
+ const next = vi.fn();
79
+ await middleware(req, res, next);
80
+ expect(res.status).toHaveBeenCalledWith(503);
81
+ expect(res.json).toHaveBeenCalledWith({
82
+ error: "dependency_unavailable",
83
+ capability: "calculator",
84
+ });
85
+ // User code must NOT run.
86
+ expect(handler).not.toHaveBeenCalled();
87
+ expect(next).not.toHaveBeenCalled();
88
+ });
89
+ it("runs the handler (no 503) when the required dep IS available", async () => {
90
+ const registry = RouteRegistry.getInstance();
91
+ const handler = vi.fn();
92
+ const middleware = route([{ capability: "calculator", required: true }], handler);
93
+ const mockCalc = (async () => "result");
94
+ registry.setDependency(middleware._meshRouteId, 0, mockCalc);
95
+ const req = { method: "POST", path: "/compute", headers: {} };
96
+ const res = mockRes();
97
+ const next = vi.fn();
98
+ await middleware(req, res, next);
99
+ expect(res.status).not.toHaveBeenCalled();
100
+ expect(handler).toHaveBeenCalledWith(req, res, expect.objectContaining({ calculator: mockCalc }));
101
+ });
102
+ it("does NOT 503 for an unavailable OPTIONAL dep (soft-fail preserved)", async () => {
103
+ const handler = vi.fn();
104
+ const middleware = route([{ capability: "calculator" }], handler);
105
+ const req = { method: "POST", path: "/compute", headers: {} };
106
+ const res = mockRes();
107
+ const next = vi.fn();
108
+ await middleware(req, res, next);
109
+ expect(res.status).not.toHaveBeenCalled();
110
+ // Handler runs with a null proxy — the existing degraded behavior.
111
+ expect(handler).toHaveBeenCalledWith(req, res, expect.objectContaining({ calculator: null }));
112
+ });
113
+ it("does NOT 503 when a duplicate capability is live in the winning slot", async () => {
114
+ // A capability declared twice collapses to one capability-keyed `deps`
115
+ // slot (last resolution wins). The perimeter must judge the same slot the
116
+ // handler sees — not a dead sibling index — so a live proxy means run.
117
+ const registry = RouteRegistry.getInstance();
118
+ const handler = vi.fn();
119
+ const middleware = route([
120
+ { capability: "calculator", required: true }, // required, its index unresolved
121
+ { capability: "calculator" }, // optional, this index is live → wins deps[cap]
122
+ ], handler);
123
+ const mockCalc = (async () => "result");
124
+ registry.setDependency(middleware._meshRouteId, 1, mockCalc);
125
+ const req = { method: "POST", path: "/compute", headers: {} };
126
+ const res = mockRes();
127
+ const next = vi.fn();
128
+ await middleware(req, res, next);
129
+ expect(res.status).not.toHaveBeenCalled();
130
+ expect(handler).toHaveBeenCalledWith(req, res, expect.objectContaining({ calculator: mockCalc }));
131
+ });
132
+ it("503s on the first unavailable required dep among a mix", async () => {
133
+ const registry = RouteRegistry.getInstance();
134
+ const handler = vi.fn();
135
+ const middleware = route([
136
+ { capability: "logger" }, // optional, unresolved
137
+ { capability: "calculator", required: true }, // required, resolved
138
+ { capability: "weather-api", required: true }, // required, unresolved
139
+ ], handler);
140
+ const mockCalc = (async () => "result");
141
+ registry.setDependency(middleware._meshRouteId, 1, mockCalc);
142
+ const req = { method: "POST", path: "/compute", headers: {} };
143
+ const res = mockRes();
144
+ const next = vi.fn();
145
+ await middleware(req, res, next);
146
+ expect(res.status).toHaveBeenCalledWith(503);
147
+ expect(res.json).toHaveBeenCalledWith({
148
+ error: "dependency_unavailable",
149
+ capability: "weather-api",
150
+ });
151
+ expect(handler).not.toHaveBeenCalled();
152
+ });
153
+ });
154
+ //# sourceMappingURL=route-required.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-required.spec.js","sourceRoot":"","sources":["../../src/__tests__/route-required.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=unwired-slot-warn.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unwired-slot-warn.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/unwired-slot-warn.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Unwired typed-dependency-slot diagnostics (issue #1231).
3
+ *
4
+ * A declared typed dependency slot can remain null after the settle window
5
+ * closes — no proxy was ever injected — leaving the parameter silently null.
6
+ * The deps array is rebuilt PER CALL, so the diagnostic must warn ONCE per
7
+ * tool+slot (a per-call warning would spam) and only AFTER the settle latch
8
+ * has flipped (during settling the proxy may still land). The same warn-once
9
+ * dedupe spans BOTH the inbound HTTP path and the claim-dispatch path.
10
+ */
11
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
12
+ import { z } from "zod";
13
+ import { MeshAgent, __resetUnwiredSlotWarnedForTests } from "../agent.js";
14
+ import { RouteRegistry } from "../route.js";
15
+ import { resetSettleStateForTests } from "../settle.js";
16
+ function makeFastMCPStub() {
17
+ return {
18
+ addTool: vi.fn(),
19
+ start: vi.fn(),
20
+ getApp: vi.fn(),
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ };
23
+ }
24
+ let autoStartSpy = null;
25
+ let warnSpy = null;
26
+ const savedEnv = {};
27
+ beforeEach(() => {
28
+ autoStartSpy = vi
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ .spyOn(MeshAgent.prototype, "_autoStart")
31
+ .mockImplementation(async () => {
32
+ /* no-op */
33
+ });
34
+ warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {
35
+ /* swallow */
36
+ });
37
+ savedEnv.MCP_MESH_SETTLE_TIMEOUT = process.env.MCP_MESH_SETTLE_TIMEOUT;
38
+ savedEnv.MCP_MESH_TOOL_ISOLATION = process.env.MCP_MESH_TOOL_ISOLATION;
39
+ process.env.MCP_MESH_TOOL_ISOLATION = "false";
40
+ // Disable the settle grace so the agent is settled immediately — a null
41
+ // slot here is genuinely unwired, exactly the case the warning targets.
42
+ process.env.MCP_MESH_SETTLE_TIMEOUT = "0";
43
+ resetSettleStateForTests();
44
+ RouteRegistry.reset();
45
+ __resetUnwiredSlotWarnedForTests();
46
+ });
47
+ afterEach(() => {
48
+ if (autoStartSpy) {
49
+ autoStartSpy.mockRestore();
50
+ autoStartSpy = null;
51
+ }
52
+ if (warnSpy) {
53
+ warnSpy.mockRestore();
54
+ warnSpy = null;
55
+ }
56
+ for (const [key, value] of Object.entries(savedEnv)) {
57
+ if (value === undefined)
58
+ delete process.env[key];
59
+ else
60
+ process.env[key] = value;
61
+ }
62
+ resetSettleStateForTests();
63
+ RouteRegistry.reset();
64
+ __resetUnwiredSlotWarnedForTests();
65
+ });
66
+ function unwiredWarnings() {
67
+ return (warnSpy?.mock.calls ?? [])
68
+ .map((call) => String(call[0]))
69
+ .filter((msg) => msg.includes("no proxy was injected"));
70
+ }
71
+ describe("unwired typed-slot warning (#1231)", () => {
72
+ it("warns ONCE per tool+slot across multiple calls, not per-call", async () => {
73
+ const fastmcp = makeFastMCPStub();
74
+ const agent = new MeshAgent(fastmcp, {
75
+ name: "unwired-agent",
76
+ httpPort: 0,
77
+ });
78
+ agent.addTool({
79
+ name: "consumer_tool",
80
+ parameters: z.object({}),
81
+ dependencies: [{ capability: "missing_cap" }],
82
+ // Defensive user idiom — the dep slot is never resolved, so it stays
83
+ // null on every call.
84
+ execute: async (_args, dep) => dep ? "wired" : "unwired",
85
+ });
86
+ const execute = fastmcp.addTool.mock.calls[0][0].execute;
87
+ for (let i = 0; i < 3; i++) {
88
+ expect(await execute({})).toBe("unwired");
89
+ }
90
+ const warnings = unwiredWarnings();
91
+ expect(warnings).toHaveLength(1);
92
+ expect(warnings[0]).toContain("missing_cap");
93
+ expect(warnings[0]).toContain("consumer_tool");
94
+ expect(warnings[0]).toContain("slot 0");
95
+ });
96
+ it("does not warn when the slot actually wires", async () => {
97
+ const fastmcp = makeFastMCPStub();
98
+ const agent = new MeshAgent(fastmcp, {
99
+ name: "wired-agent",
100
+ httpPort: 0,
101
+ });
102
+ agent.addTool({
103
+ name: "wired_tool",
104
+ parameters: z.object({}),
105
+ dependencies: [{ capability: "present_cap" }],
106
+ execute: async (_args, dep) => dep ? "wired" : "unwired",
107
+ });
108
+ const execute = fastmcp.addTool.mock.calls[0][0].execute;
109
+ // Resolve the dependency so a real proxy lands in the slot.
110
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
+ agent.handleDependencyAvailable("present_cap", "http://localhost:19999", "remote_fn", "provider-agent", "wired_tool", 0);
112
+ expect(await execute({})).toBe("wired");
113
+ expect(unwiredWarnings()).toHaveLength(0);
114
+ });
115
+ it("warns ONCE for an unwired slot on the claim-dispatch path", async () => {
116
+ const fastmcp = makeFastMCPStub();
117
+ const agent = new MeshAgent(fastmcp, {
118
+ name: "claim-agent",
119
+ httpPort: 0,
120
+ });
121
+ agent.addTool({
122
+ name: "task_tool",
123
+ task: true,
124
+ parameters: z.object({}),
125
+ dependencies: [{ capability: "missing_cap" }],
126
+ execute: async (_args, dep) => dep ? "wired" : "unwired",
127
+ });
128
+ // The claim-dispatch path is exercised via the registered ClaimHandler
129
+ // (one per task: true tool), not via FastMCP's execute().
130
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
131
+ const { handler } = agent._taskHandlers.get("task_tool");
132
+ // meshJobParamIndex is unset, so the dummy controller is never spliced
133
+ // into callArgs — the user execute() only sees (payload, dep).
134
+ for (let i = 0; i < 3; i++) {
135
+ expect(await handler({}, {})).toBe("unwired");
136
+ }
137
+ const warnings = unwiredWarnings();
138
+ expect(warnings).toHaveLength(1);
139
+ expect(warnings[0]).toContain("missing_cap");
140
+ expect(warnings[0]).toContain("task_tool");
141
+ expect(warnings[0]).toContain("slot 0");
142
+ });
143
+ it("dedupes the unwired warning ACROSS the inbound and claim paths", async () => {
144
+ const fastmcp = makeFastMCPStub();
145
+ const agent = new MeshAgent(fastmcp, {
146
+ name: "both-paths-agent",
147
+ httpPort: 0,
148
+ });
149
+ agent.addTool({
150
+ name: "task_tool",
151
+ task: true,
152
+ parameters: z.object({}),
153
+ dependencies: [{ capability: "missing_cap" }],
154
+ execute: async (_args, dep) => dep ? "wired" : "unwired",
155
+ });
156
+ const execute = fastmcp.addTool.mock.calls[0][0].execute;
157
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
158
+ const { handler } = agent._taskHandlers.get("task_tool");
159
+ // Exercise the same tool+slot via BOTH paths — the shared
160
+ // _unwiredSlotWarned Set must dedupe to a single warning total.
161
+ expect(await execute({})).toBe("unwired");
162
+ expect(await handler({}, {})).toBe("unwired");
163
+ expect(await execute({})).toBe("unwired");
164
+ expect(unwiredWarnings()).toHaveLength(1);
165
+ });
166
+ });
167
+ //# sourceMappingURL=unwired-slot-warn.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unwired-slot-warn.spec.js","sourceRoot":"","sources":["../../src/__tests__/unwired-slot-warn.spec.ts"],"names":[],"mappings":"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"}
package/dist/agent.d.ts CHANGED
@@ -38,6 +38,8 @@ export declare const IN_WORKER_SYMBOL: unique symbol;
38
38
  * module. Not part of the public API.
39
39
  */
40
40
  export declare function __getWorkerToolMap(): Map<string, (...args: unknown[]) => unknown>;
41
+ /** Test support: drop the warn-once dedupe so a fresh suite warns again. */
42
+ export declare function __resetUnwiredSlotWarnedForTests(): void;
41
43
  /**
42
44
  * MeshAgent wraps a FastMCP server with MCP Mesh capabilities.
43
45
  *
@@ -184,6 +186,23 @@ export declare class MeshAgent {
184
186
  * tool-A's bearer onto tool-B's outbound traffic.
185
187
  */
186
188
  private _bearerCacheKey;
189
+ /**
190
+ * Build the positional dependency-slot array for one tool call, shared by
191
+ * BOTH the inbound HTTP wrapper (wrappedExecute) and the claim-dispatch
192
+ * path (the ClaimHandler for task:true tools). Centralising this keeps the
193
+ * two slot-assembly sites identical:
194
+ * - the MeshJob slot (meshJobDepIndex) → a freshly-bound MeshJobSubmitter
195
+ * - every other slot → resolvedDeps.get(`${toolName}:dep_${index}`) ?? null
196
+ *
197
+ * #1231: a typed dep slot can resolve at the registry yet never receive an
198
+ * injected proxy, leaving the parameter null. Once the settle latch has
199
+ * flipped (during settling the proxy may still land), warn ONCE per
200
+ * tool+slot — the array is rebuilt per call, so a per-call warning would
201
+ * spam. The warn-once dedupe keys on `${toolName}:dep_${index}` via the
202
+ * module-level `_unwiredSlotWarned` Set, so a tool exercised via BOTH the
203
+ * inbound and claim paths warns at most once total.
204
+ */
205
+ private _buildDepSlots;
187
206
  private _getOrBuildA2AClient;
188
207
  /**
189
208
  * Convert Zod schema to JSON Schema.
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
package/dist/agent.js CHANGED
@@ -16,7 +16,7 @@ import { enrichSchemaWithMediaTypes } from "./media-param.js";
16
16
  import { createProxy, normalizeDependency, runWithTraceContext, runWithPropagatedHeaders, PROXY_DISPATCH_META } from "./proxy.js";
17
17
  import { readJobHeaders, runWithJobContext, makeJobController, spliceJobController, } from "./inbound-job-dispatch.js";
18
18
  import { MeshJobSubmitter } from "./mesh-job-submitter.js";
19
- import { getSettleState } from "./settle.js";
19
+ import { getSettleState, } from "./settle.js";
20
20
  import { ClaimDispatcher, stopDispatchers, } from "./claim-dispatcher.js";
21
21
  import { registerJobHelperTools } from "./jobs-helper-tools.js";
22
22
  import { registerCancelRoute } from "./jobs-cancel-route.js";
@@ -84,6 +84,16 @@ let autoStartScheduled = false;
84
84
  // pre-consumption danger zone, while sequential constructions across
85
85
  // async boundaries (e.g. one agent per test in a harness) stay allowed.
86
86
  let constructionGuardName = null;
87
+ // #1231: warn-once dedupe for typed dependency slots that resolved at the
88
+ // registry ("N/N deps resolved") but did not actually wire — the proxy was
89
+ // never injected and the slot stays null. The deps array is rebuilt PER
90
+ // CALL, so a per-call warn would spam; this set keys on `${toolName}:${dep}`
91
+ // so each unwired slot warns exactly once per process.
92
+ const _unwiredSlotWarned = new Set();
93
+ /** Test support: drop the warn-once dedupe so a fresh suite warns again. */
94
+ export function __resetUnwiredSlotWarnedForTests() {
95
+ _unwiredSlotWarned.clear();
96
+ }
87
97
  // Schedule auto-start after module loading completes
88
98
  function scheduleAutoStart() {
89
99
  if (autoStartScheduled)
@@ -495,15 +505,7 @@ export class MeshAgent {
495
505
  // MeshJobSubmitter targeting that dep's capability. We bind the
496
506
  // submitter to the live registryUrl/agentId so it can submit
497
507
  // jobs without needing access to the agent instance.
498
- const depsArray = normalizedDeps.map((dep, depIndex) => {
499
- if (depIndex === meshJobDepIndex) {
500
- // Build the submitter lazily per call so we always pick
501
- // up the current registryUrl (test harnesses sometimes
502
- // mutate it between calls).
503
- return new MeshJobSubmitter(dep.capability, this.agentId, this.config.registryUrl);
504
- }
505
- return this.resolvedDeps.get(`${toolName}:dep_${depIndex}`) ?? null;
506
- });
508
+ const depsArray = this._buildDepSlots(toolName, normalizedDeps, meshJobDepIndex, settleState);
507
509
  const injectedCount = depsArray.filter((d) => d !== null).length;
508
510
  // Extract trace context from arguments (injected by upstream proxy)
509
511
  // This is the fallback mechanism since fastmcp doesn't expose HTTP headers
@@ -619,11 +621,11 @@ export class MeshAgent {
619
621
  result = await runWithTraceContext(traceContext, async () => {
620
622
  return await runWithPropagatedHeaders(propagatedHeaders, async () => {
621
623
  if (isTaskTool) {
622
- const [jobId, deadlineSecs] = readJobHeaders(propagatedHeaders);
624
+ const [jobId, deadlineSecs, claimEpoch] = readJobHeaders(propagatedHeaders);
623
625
  let controller = null;
624
626
  if (jobId && this.config.registryUrl && this.agentId) {
625
627
  try {
626
- controller = makeJobController(jobId, this.agentId, this.config.registryUrl);
628
+ controller = makeJobController(jobId, this.agentId, this.config.registryUrl, claimEpoch);
627
629
  }
628
630
  catch (err) {
629
631
  // Don't silently fall back to a regular tool call —
@@ -653,7 +655,7 @@ export class MeshAgent {
653
655
  if (a2aClient !== null) {
654
656
  callArgs.push(a2aClient);
655
657
  }
656
- return await runWithJobContext(jobId, deadlineSecs, controller, () => Promise.resolve(execute(...callArgs)), retryOn);
658
+ return await runWithJobContext(jobId, deadlineSecs, controller, () => Promise.resolve(execute(...callArgs)), retryOn, claimEpoch);
657
659
  }
658
660
  if (a2aClient !== null) {
659
661
  return await execute(cleanArgs, ...depsArray, a2aClient);
@@ -753,12 +755,7 @@ export class MeshAgent {
753
755
  await settleState.awaitPending(pendingSettle);
754
756
  }
755
757
  }
756
- const liveDeps = normalizedDeps.map((dep, depIndex) => {
757
- if (depIndex === meshJobDepIndex) {
758
- return new MeshJobSubmitter(dep.capability, this.agentId, this.config.registryUrl);
759
- }
760
- return this.resolvedDeps.get(`${toolName}:dep_${depIndex}`) ?? null;
761
- });
758
+ const liveDeps = this._buildDepSlots(toolName, normalizedDeps, meshJobDepIndex, settleState);
762
759
  const callArgs = spliceJobController(payload, liveDeps, controller, meshJobParamIndex);
763
760
  // Issue #917: A2A consumer tools dispatched via the claim
764
761
  // path get the same trailing A2AClient argument as the
@@ -905,6 +902,48 @@ export class MeshAgent {
905
902
  }
906
903
  return id;
907
904
  }
905
+ /**
906
+ * Build the positional dependency-slot array for one tool call, shared by
907
+ * BOTH the inbound HTTP wrapper (wrappedExecute) and the claim-dispatch
908
+ * path (the ClaimHandler for task:true tools). Centralising this keeps the
909
+ * two slot-assembly sites identical:
910
+ * - the MeshJob slot (meshJobDepIndex) → a freshly-bound MeshJobSubmitter
911
+ * - every other slot → resolvedDeps.get(`${toolName}:dep_${index}`) ?? null
912
+ *
913
+ * #1231: a typed dep slot can resolve at the registry yet never receive an
914
+ * injected proxy, leaving the parameter null. Once the settle latch has
915
+ * flipped (during settling the proxy may still land), warn ONCE per
916
+ * tool+slot — the array is rebuilt per call, so a per-call warning would
917
+ * spam. The warn-once dedupe keys on `${toolName}:dep_${index}` via the
918
+ * module-level `_unwiredSlotWarned` Set, so a tool exercised via BOTH the
919
+ * inbound and claim paths warns at most once total.
920
+ */
921
+ _buildDepSlots(toolName, normalizedDeps, meshJobDepIndex, settleState) {
922
+ return normalizedDeps.map((dep, depIndex) => {
923
+ if (depIndex === meshJobDepIndex) {
924
+ // Build the submitter lazily per call so we always pick up the
925
+ // current registryUrl (test harnesses sometimes mutate it between
926
+ // calls).
927
+ return new MeshJobSubmitter(dep.capability, this.agentId, this.config.registryUrl);
928
+ }
929
+ const slotKey = `${toolName}:dep_${depIndex}`;
930
+ const proxy = this.resolvedDeps.get(slotKey) ?? null;
931
+ // #1231: a declared typed dep slot that is still null AFTER settling
932
+ // never received an injected proxy. Warn ONCE per tool+slot. Note this
933
+ // is a post-settle null — there is no per-slot "resolved" signal here,
934
+ // so the message must not claim registry resolution.
935
+ if (proxy === null && settleState.isSettled()) {
936
+ if (!_unwiredSlotWarned.has(slotKey)) {
937
+ _unwiredSlotWarned.add(slotKey);
938
+ console.warn(`[mesh-tool] dependency '${dep.capability}' on '${toolName}' ` +
939
+ `is still null after settling — no proxy was injected into ` +
940
+ `positional slot ${depIndex}. Fix: ensure the provider for ` +
941
+ `'${dep.capability}' is registered and reachable.`);
942
+ }
943
+ }
944
+ return proxy;
945
+ });
946
+ }
908
947
  _getOrBuildA2AClient(config) {
909
948
  const key = [
910
949
  config.url,
@@ -1300,6 +1339,9 @@ export class MeshAgent {
1300
1339
  expectedSchemaCanonical: expectedCanonical,
1301
1340
  expectedSchemaHash: expectedHash,
1302
1341
  matchMode: dep.matchMode,
1342
+ // Issue #1249: only carry `required` when opted in (absent →
1343
+ // false) so existing heartbeats stay byte-identical.
1344
+ required: dep.required ? true : undefined,
1303
1345
  };
1304
1346
  }),
1305
1347
  inputSchema: meta.inputSchema,