@polpo-ai/core 0.15.144 → 0.16.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 (178) hide show
  1. package/README.md +30 -0
  2. package/dist/__tests__/custom-tool-runner.test.d.ts +2 -0
  3. package/dist/__tests__/custom-tool-runner.test.d.ts.map +1 -0
  4. package/dist/__tests__/custom-tool-runner.test.js +121 -0
  5. package/dist/__tests__/custom-tool-runner.test.js.map +1 -0
  6. package/dist/__tests__/mcp-runner-client.test.d.ts +2 -0
  7. package/dist/__tests__/mcp-runner-client.test.d.ts.map +1 -0
  8. package/dist/__tests__/mcp-runner-client.test.js +144 -0
  9. package/dist/__tests__/mcp-runner-client.test.js.map +1 -0
  10. package/dist/__tests__/mission-execution.test.d.ts +2 -0
  11. package/dist/__tests__/mission-execution.test.d.ts.map +1 -0
  12. package/dist/__tests__/mission-execution.test.js +68 -0
  13. package/dist/__tests__/mission-execution.test.js.map +1 -0
  14. package/dist/__tests__/run-state-monitor.test.d.ts +2 -0
  15. package/dist/__tests__/run-state-monitor.test.d.ts.map +1 -0
  16. package/dist/__tests__/run-state-monitor.test.js +59 -0
  17. package/dist/__tests__/run-state-monitor.test.js.map +1 -0
  18. package/dist/__tests__/runtime-metadata.test.d.ts +2 -0
  19. package/dist/__tests__/runtime-metadata.test.d.ts.map +1 -0
  20. package/dist/__tests__/runtime-metadata.test.js +17 -0
  21. package/dist/__tests__/runtime-metadata.test.js.map +1 -0
  22. package/dist/__tests__/task-execution-router.test.js +1 -1
  23. package/dist/__tests__/task-execution-router.test.js.map +1 -1
  24. package/dist/__tests__/task-runner-agent-lifetime.test.d.ts +2 -0
  25. package/dist/__tests__/task-runner-agent-lifetime.test.d.ts.map +1 -0
  26. package/dist/__tests__/task-runner-agent-lifetime.test.js +136 -0
  27. package/dist/__tests__/task-runner-agent-lifetime.test.js.map +1 -0
  28. package/dist/adapter.d.ts +18 -0
  29. package/dist/adapter.d.ts.map +1 -1
  30. package/dist/agent-manager.d.ts +3 -0
  31. package/dist/agent-manager.d.ts.map +1 -1
  32. package/dist/agent-manager.js +28 -2
  33. package/dist/agent-manager.js.map +1 -1
  34. package/dist/agent-store.d.ts +70 -1
  35. package/dist/agent-store.d.ts.map +1 -1
  36. package/dist/agent-store.js +103 -1
  37. package/dist/agent-store.js.map +1 -1
  38. package/dist/approval-manager.d.ts +24 -4
  39. package/dist/approval-manager.d.ts.map +1 -1
  40. package/dist/approval-manager.js +160 -112
  41. package/dist/approval-manager.js.map +1 -1
  42. package/dist/approval-manager.test.d.ts +2 -0
  43. package/dist/approval-manager.test.d.ts.map +1 -0
  44. package/dist/approval-manager.test.js +116 -0
  45. package/dist/approval-manager.test.js.map +1 -0
  46. package/dist/assessment-orchestrator.d.ts +12 -1
  47. package/dist/assessment-orchestrator.d.ts.map +1 -1
  48. package/dist/assessment-orchestrator.js +181 -83
  49. package/dist/assessment-orchestrator.js.map +1 -1
  50. package/dist/chat-interactions.d.ts +23 -3
  51. package/dist/chat-interactions.d.ts.map +1 -1
  52. package/dist/chat-interactions.js +50 -2
  53. package/dist/chat-interactions.js.map +1 -1
  54. package/dist/chat-interactions.test.js +39 -2
  55. package/dist/chat-interactions.test.js.map +1 -1
  56. package/dist/custom-tool-runner.d.ts +56 -0
  57. package/dist/custom-tool-runner.d.ts.map +1 -0
  58. package/dist/custom-tool-runner.js +197 -0
  59. package/dist/custom-tool-runner.js.map +1 -0
  60. package/dist/escalation-manager.d.ts +2 -0
  61. package/dist/escalation-manager.d.ts.map +1 -1
  62. package/dist/escalation-manager.js +79 -25
  63. package/dist/escalation-manager.js.map +1 -1
  64. package/dist/events.d.ts +4 -0
  65. package/dist/events.d.ts.map +1 -1
  66. package/dist/hooks.d.ts.map +1 -1
  67. package/dist/hooks.js +6 -0
  68. package/dist/hooks.js.map +1 -1
  69. package/dist/index.d.ts +19 -3
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +13 -1
  72. package/dist/index.js.map +1 -1
  73. package/dist/mcp-capability.d.ts +40 -0
  74. package/dist/mcp-capability.d.ts.map +1 -0
  75. package/dist/mcp-capability.js +2 -0
  76. package/dist/mcp-capability.js.map +1 -0
  77. package/dist/mcp-runner.d.ts +45 -0
  78. package/dist/mcp-runner.d.ts.map +1 -0
  79. package/dist/mcp-runner.js +238 -0
  80. package/dist/mcp-runner.js.map +1 -0
  81. package/dist/mission-cancellation.d.ts +14 -0
  82. package/dist/mission-cancellation.d.ts.map +1 -0
  83. package/dist/mission-cancellation.js +43 -0
  84. package/dist/mission-cancellation.js.map +1 -0
  85. package/dist/mission-execution.d.ts +103 -0
  86. package/dist/mission-execution.d.ts.map +1 -0
  87. package/dist/mission-execution.js +196 -0
  88. package/dist/mission-execution.js.map +1 -0
  89. package/dist/mission-executor.d.ts +18 -16
  90. package/dist/mission-executor.d.ts.map +1 -1
  91. package/dist/mission-executor.js +339 -203
  92. package/dist/mission-executor.js.map +1 -1
  93. package/dist/mission-gating.d.ts +11 -15
  94. package/dist/mission-gating.d.ts.map +1 -1
  95. package/dist/mission-gating.js +25 -33
  96. package/dist/mission-gating.js.map +1 -1
  97. package/dist/mission-inspection.d.ts +29 -0
  98. package/dist/mission-inspection.d.ts.map +1 -0
  99. package/dist/mission-inspection.js +99 -0
  100. package/dist/mission-inspection.js.map +1 -0
  101. package/dist/mission-store.d.ts +7 -0
  102. package/dist/mission-store.d.ts.map +1 -1
  103. package/dist/mission-store.js +8 -0
  104. package/dist/mission-store.js.map +1 -1
  105. package/dist/orchestrator-context.d.ts +3 -0
  106. package/dist/orchestrator-context.d.ts.map +1 -1
  107. package/dist/orchestrator-engine.d.ts +39 -39
  108. package/dist/orchestrator-engine.d.ts.map +1 -1
  109. package/dist/orchestrator-engine.js +124 -24
  110. package/dist/orchestrator-engine.js.map +1 -1
  111. package/dist/run-state-monitor.d.ts +14 -0
  112. package/dist/run-state-monitor.d.ts.map +1 -0
  113. package/dist/run-state-monitor.js +63 -0
  114. package/dist/run-state-monitor.js.map +1 -0
  115. package/dist/run-store.d.ts +15 -0
  116. package/dist/run-store.d.ts.map +1 -1
  117. package/dist/runner-capability.d.ts +43 -0
  118. package/dist/runner-capability.d.ts.map +1 -0
  119. package/dist/runner-capability.js +55 -0
  120. package/dist/runner-capability.js.map +1 -0
  121. package/dist/runtime-metadata.d.ts +5 -0
  122. package/dist/runtime-metadata.d.ts.map +1 -0
  123. package/dist/runtime-metadata.js +21 -0
  124. package/dist/runtime-metadata.js.map +1 -0
  125. package/dist/scheduler.d.ts +2 -1
  126. package/dist/scheduler.d.ts.map +1 -1
  127. package/dist/scheduler.js +16 -6
  128. package/dist/scheduler.js.map +1 -1
  129. package/dist/schemas.d.ts +8 -6
  130. package/dist/schemas.d.ts.map +1 -1
  131. package/dist/schemas.js +10 -0
  132. package/dist/schemas.js.map +1 -1
  133. package/dist/spawner.d.ts +12 -0
  134. package/dist/spawner.d.ts.map +1 -1
  135. package/dist/steering.d.ts +4 -1
  136. package/dist/steering.d.ts.map +1 -1
  137. package/dist/steering.js.map +1 -1
  138. package/dist/task-approval-coordinator.d.ts +29 -0
  139. package/dist/task-approval-coordinator.d.ts.map +1 -0
  140. package/dist/task-approval-coordinator.js +216 -0
  141. package/dist/task-approval-coordinator.js.map +1 -0
  142. package/dist/task-dispatch.d.ts +40 -0
  143. package/dist/task-dispatch.d.ts.map +1 -0
  144. package/dist/task-dispatch.js +136 -0
  145. package/dist/task-dispatch.js.map +1 -0
  146. package/dist/task-execution.d.ts +146 -0
  147. package/dist/task-execution.d.ts.map +1 -0
  148. package/dist/task-execution.js +357 -0
  149. package/dist/task-execution.js.map +1 -0
  150. package/dist/task-manager.d.ts +31 -22
  151. package/dist/task-manager.d.ts.map +1 -1
  152. package/dist/task-manager.js +74 -7
  153. package/dist/task-manager.js.map +1 -1
  154. package/dist/task-runner.d.ts +30 -3
  155. package/dist/task-runner.d.ts.map +1 -1
  156. package/dist/task-runner.js +429 -90
  157. package/dist/task-runner.js.map +1 -1
  158. package/dist/task-store.d.ts +9 -3
  159. package/dist/task-store.d.ts.map +1 -1
  160. package/dist/tool-invocation.d.ts +4 -0
  161. package/dist/tool-invocation.d.ts.map +1 -1
  162. package/dist/tool-invocation.js +8 -1
  163. package/dist/tool-invocation.js.map +1 -1
  164. package/dist/tool-invocation.test.d.ts +2 -0
  165. package/dist/tool-invocation.test.d.ts.map +1 -0
  166. package/dist/tool-invocation.test.js +40 -0
  167. package/dist/tool-invocation.test.js.map +1 -0
  168. package/dist/types/agent.d.ts +4 -0
  169. package/dist/types/agent.d.ts.map +1 -1
  170. package/dist/types/agent.js.map +1 -1
  171. package/dist/types/config.d.ts +3 -0
  172. package/dist/types/config.d.ts.map +1 -1
  173. package/dist/types/config.js.map +1 -1
  174. package/dist/types/mission.d.ts +23 -1
  175. package/dist/types/mission.d.ts.map +1 -1
  176. package/dist/types/task.d.ts +10 -0
  177. package/dist/types/task.d.ts.map +1 -1
  178. package/package.json +17 -1
package/README.md CHANGED
@@ -8,6 +8,36 @@ Pure business logic, types, schemas, and store interfaces for the [Polpo](https:
8
8
  npm install @polpo-ai/core
9
9
  ```
10
10
 
11
+ ## Durable Task execution
12
+
13
+ First-party file and SQL stores expose `TaskExecutionStore` from
14
+ `@polpo-ai/core/task-execution`. TaskRunner claims a persisted pending Task and
15
+ creates its Run atomically before asynchronous preparation. Configure TaskStore
16
+ and RunStore against the same project/database using the matching store factory.
17
+ A claim uses the persisted input and principal; competing dispatchers cannot both
18
+ start the same pending generation.
19
+
20
+ Result collection passes a `TaskExecutionReference` to the assessment callback;
21
+ forward that third argument when composing your own engine. Assessment, retry,
22
+ fix, clarification and escalation writes retain that reference through awaits.
23
+ Cancellation or requeue invalidates it. Obsolete results are acknowledged without
24
+ attaching their session, outcomes or completion to the next attempt. Requeue
25
+ publishes its complete input and retry counter in one transaction.
26
+
27
+ TaskManager cancellation commits the captured Task/Run state before notifying
28
+ `Spawner.cancelRun(runId)`. Node and in-process spawners notify only their local
29
+ owner for that exact Run; they do not infer ownership from a persisted PID.
30
+ Hosts must also observe durable cancellation while executing. Cross-host polling,
31
+ Cloud cold cancellation, approval resolution and Mission abort still need their
32
+ own integration; this contract alone does not prove a remote process has stopped.
33
+
34
+ Task creation, Mission admission, scheduling and spawning await asynchronous
35
+ before-hooks. A human spawn gate invalidates the claimed attempt and retains
36
+ `awaiting_approval`; a generic veto requeues without consuming a retry. Custom
37
+ stores without execution persistence retain legacy behavior only for unversioned
38
+ Tasks. Stop old writers during upgrade or rollback, and never force a status
39
+ change as a fallback for an execution conflict.
40
+
11
41
  ## Agentic loops
12
42
 
13
43
  Use `defineProjectLoop` for code-first loop definitions that compile to the same JSON-compatible contract used by the API, CLI, dashboard, and runtime. Project files can live in `.polpo/loops/*.ts`; `polpo deploy` sends the source to the server, which compiles it statically and persists canonical JSON.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=custom-tool-runner.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-tool-runner.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/custom-tool-runner.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,121 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { createToolInvocationContext } from "../tool-invocation.js";
3
+ import { createRemoteCustomTools, CustomToolRunnerRequestSchema, MAX_CUSTOM_TOOL_RUNNER_REQUEST_BYTES, MAX_CUSTOM_TOOL_RUNNER_RESPONSE_BYTES } from "../custom-tool-runner.js";
4
+ const lease = () => ({ version: 1, url: "https://host.test/internal/tools", token: "fixture_private_bearer",
5
+ sandboxId: "sandbox", agentName: "support", agentIdentity: { name: "support", incarnation: "original" },
6
+ runId: "run", expiresAt: new Date(Date.now() + 60_000).toISOString() });
7
+ const invocation = createToolInvocationContext({ requestId: "request", runId: "run", surface: "task",
8
+ agent: { name: "support", incarnation: "original" }, user: "customer" });
9
+ const inventory = [{ name: "read_mail", label: "Read mail", description: "Read", parameters: { type: "object", properties: {} } }];
10
+ afterEach(() => vi.useRealTimers());
11
+ describe("remote custom-tool host port", () => {
12
+ it("transmits only inventory/call intent, preserving private run authority outside model schemas", async () => {
13
+ const requests = [];
14
+ const fetcher = vi.fn(async (_url, options) => {
15
+ const request = JSON.parse(String(options?.body));
16
+ requests.push(request);
17
+ expect(new Headers(options?.headers).get("authorization")).toBe("Bearer fixture_private_bearer");
18
+ expect(options?.redirect).toBe("error");
19
+ return Response.json({ ok: true, data: request.operation === "inventory" ? inventory : { content: [{ type: "text", text: "mail" }], details: null } });
20
+ });
21
+ const tools = await createRemoteCustomTools(lease(), { fetch: fetcher })({ agentName: "support", allowedTools: ["read_mail"], invocation });
22
+ expect(JSON.stringify(tools)).not.toContain("fixture_private_bearer");
23
+ expect(await tools[0].execute("call", { user: "forged" })).toMatchObject({ content: [{ text: "mail" }] });
24
+ expect(requests).toEqual([{ operation: "inventory" }, { operation: "call", toolName: "read_mail", toolCallId: "call", arguments: { user: "forged" } }]);
25
+ });
26
+ it("retains exact custom-tool allowlists without expanding built-in wildcards", async () => {
27
+ const fetcher = vi.fn(async () => Response.json({ ok: true, data: inventory }));
28
+ const resolve = createRemoteCustomTools(lease(), { fetch: fetcher });
29
+ expect(await resolve({ agentName: "support", allowedTools: ["read_*"], invocation })).toEqual([]);
30
+ });
31
+ it.each(["run", "agent", "incarnation", "expired"])("denies mismatched %s before sending a request", async (mismatch) => {
32
+ const value = lease();
33
+ if (mismatch === "run")
34
+ value.runId = "different";
35
+ if (mismatch === "agent") {
36
+ value.agentName = "different";
37
+ value.agentIdentity.name = "different";
38
+ }
39
+ if (mismatch === "incarnation")
40
+ value.agentIdentity.incarnation = "replacement";
41
+ if (mismatch === "expired")
42
+ value.expiresAt = "2000-01-01T00:00:00.000Z";
43
+ const fetcher = vi.fn();
44
+ await expect(createRemoteCustomTools(value, { fetch: fetcher })({ agentName: "support", allowedTools: ["*"], invocation })).rejects.toThrow();
45
+ expect(fetcher).not.toHaveBeenCalled();
46
+ });
47
+ it("does not reflect transport exceptions, remote error messages or retry a failed call", async () => {
48
+ const fetcher = vi.fn().mockResolvedValueOnce(Response.json({ ok: true, data: inventory }))
49
+ .mockRejectedValueOnce(new Error("fixture_private_bearer in SDK request"));
50
+ const [tool] = await createRemoteCustomTools(lease(), { fetch: fetcher })({ agentName: "support", allowedTools: ["*"], invocation });
51
+ await expect(tool.execute("call", {})).rejects.toThrow(/^Custom tool broker request failed$/);
52
+ expect(fetcher).toHaveBeenCalledTimes(2);
53
+ });
54
+ it("honors invocation cancellation before a later call", async () => {
55
+ const controller = new AbortController();
56
+ const fetcher = vi.fn(async () => Response.json({ ok: true, data: inventory }));
57
+ const [tool] = await createRemoteCustomTools(lease(), { fetch: fetcher })({ agentName: "support", allowedTools: ["*"], invocation, signal: controller.signal });
58
+ controller.abort();
59
+ await expect(tool.execute("call", {})).rejects.toThrow();
60
+ expect(fetcher).toHaveBeenCalledOnce();
61
+ });
62
+ it.each(["http://remote.test", "https://secret@host.test", "https://host.test?secret=value", "https://host.test#secret", "not-a-url"])("rejects unsafe broker URL %s", url => {
63
+ expect(() => createRemoteCustomTools({ ...lease(), url })).toThrow(/^Custom tool broker request failed$/);
64
+ });
65
+ it.each(["duplicate", "private-metadata", "remote-error", "oversized", "bad-json"])("rejects %s responses without reflecting their content", async (scenario) => {
66
+ const fetcher = vi.fn(async () => {
67
+ if (scenario === "remote-error")
68
+ return Response.json({ ok: false, error: { message: "private_secret" } }, { status: 403 });
69
+ if (scenario === "bad-json")
70
+ return new Response("private_secret");
71
+ if (scenario === "oversized")
72
+ return new Response("private_secret", { headers: { "content-length": String(MAX_CUSTOM_TOOL_RUNNER_RESPONSE_BYTES + 1) } });
73
+ return Response.json({ ok: true, data: scenario === "duplicate" ? [...inventory, ...inventory] : [{ ...inventory[0], token: "private_secret" }] });
74
+ });
75
+ await expect(createRemoteCustomTools(lease(), { fetch: fetcher })({ agentName: "support", allowedTools: ["*"], invocation }))
76
+ .rejects.toThrow(/^Custom tool broker request failed$/);
77
+ expect(fetcher).toHaveBeenCalledOnce();
78
+ });
79
+ it("denies oversized arguments before delivery and malformed tool results after delivery", async () => {
80
+ const fetcher = vi.fn().mockResolvedValueOnce(Response.json({ ok: true, data: inventory }))
81
+ .mockResolvedValueOnce(Response.json({ ok: true, data: { token: "private_secret" } }));
82
+ const [tool] = await createRemoteCustomTools(lease(), { fetch: fetcher })({ agentName: "support", allowedTools: ["*"], invocation });
83
+ await expect(tool.execute("call", { value: "x".repeat(MAX_CUSTOM_TOOL_RUNNER_REQUEST_BYTES) })).rejects.toThrow();
84
+ expect(fetcher).toHaveBeenCalledOnce();
85
+ await expect(tool.execute("call-2", {})).rejects.toThrow(/^Custom tool broker request failed$/);
86
+ });
87
+ it("enforces the byte limit even when content-length is absent", async () => {
88
+ const fetcher = vi.fn(async () => new Response(new ReadableStream({ start(controller) {
89
+ controller.enqueue(new Uint8Array(MAX_CUSTOM_TOOL_RUNNER_RESPONSE_BYTES + 1));
90
+ controller.close();
91
+ } })));
92
+ await expect(createRemoteCustomTools(lease(), { fetch: fetcher })({ agentName: "support", invocation }))
93
+ .rejects.toThrow(/^Custom tool broker request failed$/);
94
+ });
95
+ it("bounds a stuck inventory fetch even if the transport ignores cancellation", async () => {
96
+ vi.useFakeTimers();
97
+ const fetcher = vi.fn(async () => new Promise(() => { }));
98
+ const work = createRemoteCustomTools(lease(), { fetch: fetcher })({ agentName: "support", invocation });
99
+ const check = expect(work).rejects.toThrow(/^Custom tool capability is no longer available$/);
100
+ await vi.advanceTimersByTimeAsync(30_001);
101
+ await check;
102
+ expect(fetcher).toHaveBeenCalledOnce();
103
+ });
104
+ it("allows a long-running custom tool past MCP's timeout, then aborts at lease expiry", async () => {
105
+ vi.useFakeTimers();
106
+ let signal;
107
+ const fetcher = vi.fn().mockResolvedValueOnce(Response.json({ ok: true, data: inventory }))
108
+ .mockImplementationOnce(async (_url, options) => { signal = options.signal; return new Promise(() => { }); });
109
+ const [tool] = await createRemoteCustomTools(lease(), { fetch: fetcher })({ agentName: "support", allowedTools: ["*"], invocation });
110
+ const check = expect(tool.execute("call", {})).rejects.toThrow(/^Custom tool capability is no longer available$/);
111
+ await vi.advanceTimersByTimeAsync(30_001);
112
+ expect(signal?.aborted).toBe(false);
113
+ await vi.advanceTimersByTimeAsync(30_000);
114
+ await check;
115
+ expect(signal?.aborted).toBe(true);
116
+ });
117
+ it.each(["provider", "slot", "mode", "connectionId", "invocation", "sandboxId"])("rejects a runner-supplied %s authority override", key => {
118
+ expect(CustomToolRunnerRequestSchema.safeParse({ operation: "call", toolName: "read_mail", toolCallId: "call", arguments: {}, [key]: "forged" }).success).toBe(false);
119
+ });
120
+ });
121
+ //# sourceMappingURL=custom-tool-runner.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-tool-runner.test.js","sourceRoot":"","sources":["../../src/__tests__/custom-tool-runner.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mcp-runner-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-runner-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/mcp-runner-client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,144 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { createRemoteMcpRuntimeCapabilities, McpRunnerRequestSchema } from "../mcp-runner.js";
3
+ import { createToolInvocationContext } from "../tool-invocation.js";
4
+ const invocation = createToolInvocationContext({ requestId: "request", runId: "run", user: "forged-user", surface: "task", agent: { name: "support", incarnation: "original" } });
5
+ const lease = () => ({ version: 2, url: "https://polpo.example/mcp", token: "fixture-bearer",
6
+ sandboxId: "sandbox", agentName: "support", agentIdentity: { name: "support", incarnation: "original" }, runId: "run", expiresAt: new Date(Date.now() + 60_000).toISOString() });
7
+ const inventory = [{ serverName: "docs", tools: [{ name: "read", inputSchema: { type: "object" } }] }, { serverName: "revoked", tools: [] }];
8
+ const json = (data) => Response.json({ ok: true, data });
9
+ const request = { serverName: "docs", toolName: "read", toolCallId: "call", invocation };
10
+ const load = (factory) => factory({ agentName: "support", invocation });
11
+ afterEach(() => vi.useRealTimers());
12
+ describe("MCP subprocess protocol", () => {
13
+ it("exposes only a validated rate limit to the agent and never retries", async () => {
14
+ const fetcher = vi.fn().mockResolvedValueOnce(json(inventory)).mockResolvedValueOnce(Response.json({
15
+ ok: false, error: { code: "rate_limited", message: "private upstream text", retryAfterSeconds: 7, secret: "private" },
16
+ }, { status: 429 }));
17
+ const providers = await load(createRemoteMcpRuntimeCapabilities(lease(), { fetch: fetcher }));
18
+ const capability = await providers.docs.resolver.resolve(request);
19
+ const error = await capability.call({}).catch(error => error);
20
+ expect(error).toMatchObject({ code: "rate_limited", retryAfterSeconds: 7, message: "Connection request limit exceeded; retry after 7 seconds" });
21
+ expect(JSON.stringify(error)).not.toContain("private");
22
+ expect(fetcher).toHaveBeenCalledTimes(2);
23
+ });
24
+ it.each([0, -1, 1.5, 86401, "7", null])("redacts malformed rate-limit retry %j", async (retryAfterSeconds) => {
25
+ const fetcher = vi.fn().mockResolvedValue(Response.json({
26
+ ok: false, error: { code: "rate_limited", message: "private", retryAfterSeconds },
27
+ }, { status: 429 }));
28
+ await expect(load(createRemoteMcpRuntimeCapabilities(lease(), { fetch: fetcher }))).rejects.toThrow(/^MCP broker request failed$/);
29
+ });
30
+ it.each(["missing", "malformed", "extra-field", "wrong-name", "old-protocol"])("rejects %s lease identity before any request", scenario => {
31
+ const input = lease();
32
+ if (scenario === "missing")
33
+ delete input.agentIdentity;
34
+ if (scenario === "malformed")
35
+ input.agentIdentity.incarnation = "invalid identity";
36
+ if (scenario === "extra-field")
37
+ input.agentIdentity.version = 2;
38
+ if (scenario === "wrong-name")
39
+ input.agentIdentity.name = "other";
40
+ if (scenario === "old-protocol")
41
+ input.version = 1;
42
+ const fetcher = vi.fn();
43
+ expect(() => createRemoteMcpRuntimeCapabilities(input, { fetch: fetcher })).toThrow(/^MCP broker request failed$/);
44
+ expect(fetcher).not.toHaveBeenCalled();
45
+ });
46
+ it.each([undefined, { name: "support", incarnation: "replacement" }])("denies missing/substituted identity before inventory (%j)", async (agent) => {
47
+ const fetcher = vi.fn().mockResolvedValue(json(inventory));
48
+ const factory = createRemoteMcpRuntimeCapabilities(lease(), { fetch: fetcher });
49
+ await expect(factory({ agentName: "support", invocation: { ...invocation, agent } })).rejects.toThrow();
50
+ expect(fetcher).not.toHaveBeenCalled();
51
+ });
52
+ it("denies a replaced identity at tool acquisition after valid inventory", async () => {
53
+ const fetcher = vi.fn().mockResolvedValue(json(inventory));
54
+ const providers = await load(createRemoteMcpRuntimeCapabilities(lease(), { fetch: fetcher }));
55
+ await expect(providers.docs.resolver.resolve({ ...request, invocation: { ...invocation, agent: { name: "support", incarnation: "replacement" } } })).rejects.toThrow();
56
+ expect(fetcher).toHaveBeenCalledOnce();
57
+ });
58
+ it("rejects caller-controlled identity, destinations and authorization fields", () => {
59
+ for (const field of ["user", "invocation", "agentName", "url", "connectionId", "headers", "allowedTools"]) {
60
+ expect(McpRunnerRequestSchema.safeParse({ operation: "inventory", [field]: "forged" }).success).toBe(false);
61
+ expect(McpRunnerRequestSchema.safeParse({ operation: "call", capability: "mcp__docs__read", toolCallId: "call", arguments: {}, [field]: "forged" }).success).toBe(false);
62
+ }
63
+ expect(McpRunnerRequestSchema.safeParse({ operation: "call", capability: "mcp__docs__read", toolCallId: "call", arguments: [] }).success).toBe(false);
64
+ });
65
+ it("only sends an opaque lease and logical operation; each capability is single use", async () => {
66
+ const fetcher = vi.fn().mockResolvedValueOnce(json(inventory)).mockResolvedValueOnce(json({ content: [{ type: "text", text: "result" }] }));
67
+ const input = lease();
68
+ const factory = createRemoteMcpRuntimeCapabilities(input, { fetch: fetcher });
69
+ input.token = "mutated";
70
+ input.agentIdentity.incarnation = "mutated-after-construction";
71
+ const providers = await load(factory);
72
+ expect(providers.revoked.tools).toEqual([]);
73
+ const capability = await providers.docs.resolver.resolve(request);
74
+ expect(fetcher).toHaveBeenCalledTimes(1);
75
+ expect(await capability.call({ user: "tool-data" })).toEqual({ content: [{ type: "text", text: "result" }] });
76
+ await expect(capability.call({})).rejects.toThrow("no longer available");
77
+ const init = fetcher.mock.calls[1][1];
78
+ expect(JSON.parse(init.body)).toEqual({ operation: "call", capability: "mcp__docs__read", toolCallId: "call", arguments: { user: "tool-data" } });
79
+ expect(new Headers(init.headers).get("authorization")).toBe("Bearer fixture-bearer");
80
+ expect(new Headers(init.headers).get("x-polpo-sandbox-id")).toBe("sandbox");
81
+ expect(init.redirect).toBe("error");
82
+ await capability.dispose();
83
+ });
84
+ it("rejects mismatched agent/run, unknown tool, expired lease and unsafe endpoint before network", async () => {
85
+ const fetcher = vi.fn().mockResolvedValue(json(inventory));
86
+ const factory = createRemoteMcpRuntimeCapabilities(lease(), { fetch: fetcher });
87
+ await expect(factory({ agentName: "other", invocation })).rejects.toThrow();
88
+ await expect(factory({ agentName: "support", invocation: { ...invocation, runId: "other" } })).rejects.toThrow();
89
+ expect(fetcher).not.toHaveBeenCalled();
90
+ const providers = await load(factory);
91
+ await expect(providers.docs.resolver.resolve({ ...request, toolName: "write" })).rejects.toThrow();
92
+ await expect(providers.docs.resolver.resolve({ ...request, serverName: "other" })).rejects.toThrow();
93
+ for (const url of ["http://polpo.example/mcp", "https://u:p@polpo.example/mcp", "https://polpo.example/mcp?secret=x", "https://polpo.example/mcp#x", "file:///tmp/mcp"]) {
94
+ expect(() => createRemoteMcpRuntimeCapabilities({ ...lease(), url })).toThrow();
95
+ }
96
+ const expired = createRemoteMcpRuntimeCapabilities({ ...lease(), expiresAt: "2000-01-01T00:00:00.000Z" }, { fetch: fetcher });
97
+ await expect(load(expired)).rejects.toThrow();
98
+ expect(fetcher).toHaveBeenCalledTimes(1);
99
+ });
100
+ it("redacts remote errors and never retries a possibly completed mutation", async () => {
101
+ const fetcher = vi.fn().mockResolvedValueOnce(json(inventory))
102
+ .mockRejectedValueOnce(new Error("provider secret fixture-bearer https://secret.example"));
103
+ const providers = await load(createRemoteMcpRuntimeCapabilities(lease(), { fetch: fetcher }));
104
+ const capability = await providers.docs.resolver.resolve(request);
105
+ await expect(capability.call({})).rejects.toThrow(/^MCP broker request failed$/);
106
+ await expect(capability.call({})).rejects.toThrow();
107
+ expect(fetcher).toHaveBeenCalledTimes(2);
108
+ });
109
+ it("disposal cancels an in-flight call even when the fetch adapter ignores abort", async () => {
110
+ const fetcher = vi.fn().mockResolvedValueOnce(json(inventory))
111
+ .mockImplementationOnce(() => new Promise(() => { }));
112
+ const providers = await load(createRemoteMcpRuntimeCapabilities(lease(), { fetch: fetcher }));
113
+ const capability = await providers.docs.resolver.resolve(request);
114
+ const pending = capability.call({});
115
+ const check = expect(pending).rejects.toThrow();
116
+ capability.dispose();
117
+ await check;
118
+ expect(fetcher.mock.calls[1]?.[1]?.signal?.aborted ?? true).toBe(true);
119
+ });
120
+ it("bounds inventory stalls and response body reads", async () => {
121
+ vi.useFakeTimers();
122
+ const fetcher = vi.fn().mockResolvedValue(new Response(new ReadableStream({ start() { } })));
123
+ const pending = load(createRemoteMcpRuntimeCapabilities(lease(), { fetch: fetcher }));
124
+ const check = expect(pending).rejects.toThrow();
125
+ await vi.advanceTimersByTimeAsync(30_001);
126
+ await check;
127
+ expect(fetcher).toHaveBeenCalledOnce();
128
+ });
129
+ it("rejects oversized, malformed and ambiguous inventories without exposing response data", async () => {
130
+ const responses = [
131
+ json([...inventory, inventory[0]]),
132
+ json([{ serverName: "docs", tools: [{ name: "read", inputSchema: {}, token: "secret" }] }]),
133
+ json([{ serverName: "a__b", tools: [{ name: "c", inputSchema: {} }] }, { serverName: "a", tools: [{ name: "b__c", inputSchema: {} }] }]),
134
+ new Response("provider secret", { status: 403 }),
135
+ new Response("x".repeat(2 * 1024 * 1024 + 1)),
136
+ ];
137
+ for (const response of responses) {
138
+ const fetcher = vi.fn().mockResolvedValue(response);
139
+ await expect(load(createRemoteMcpRuntimeCapabilities(lease(), { fetch: fetcher }))).rejects.toThrow(/^MCP /);
140
+ expect(fetcher).toHaveBeenCalledOnce();
141
+ }
142
+ });
143
+ });
144
+ //# sourceMappingURL=mcp-runner-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-runner-client.test.js","sourceRoot":"","sources":["../../src/__tests__/mcp-runner-client.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,kCAAkC,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC9F,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,UAAU,GAAG,2BAA2B,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;AAClL,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAU,EAAE,GAAG,EAAE,2BAA2B,EAAE,KAAK,EAAE,gBAAgB;IACnG,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACnL,MAAM,SAAS,GAAG,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;AAC7I,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAClE,MAAM,OAAO,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AACzF,MAAM,IAAI,GAAG,CAAC,OAA8D,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;AAE/H,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,CAAC;AAEpC,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAgB,CAAC,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC/G,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE;SACtH,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,kCAAkC,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9F,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QAC9D,MAAM,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC,EAAE,OAAO,EAAE,0DAA0D,EAAE,CAAC,CAAC;QACjJ,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACvD,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,uCAAuC,EAAE,KAAK,EAAC,iBAAiB,EAAC,EAAE;QACzG,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAgB,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC;YACpE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE;SAClF,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,MAAM,MAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;IACrI,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC,CAAC,8CAA8C,EAAE,QAAQ,CAAC,EAAE;QACxI,MAAM,KAAK,GAAQ,KAAK,EAAE,CAAC;QAC3B,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC,aAAa,CAAC;QACvD,IAAI,QAAQ,KAAK,WAAW;YAAE,KAAK,CAAC,aAAa,CAAC,WAAW,GAAG,kBAAkB,CAAC;QACnF,IAAI,QAAQ,KAAK,aAAa;YAAE,KAAK,CAAC,aAAa,CAAC,OAAO,GAAG,CAAC,CAAC;QAChE,IAAI,QAAQ,KAAK,YAAY;YAAE,KAAK,CAAC,aAAa,CAAC,IAAI,GAAG,OAAO,CAAC;QAClE,IAAI,QAAQ,KAAK,cAAc;YAAE,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAgB,CAAC;QACtC,MAAM,CAAC,GAAG,EAAE,CAAC,kCAAkC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;QACnH,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,2DAA2D,EAAE,KAAK,EAAC,KAAK,EAAC,EAAE;QAC/I,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAgB,CAAC,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,kCAAkC,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAChF,MAAM,MAAM,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACxG,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAgB,CAAC,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QACzE,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,kCAAkC,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9F,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACvK,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,cAAc,CAAC,EAAE,CAAC;YAC1G,MAAM,CAAC,sBAAsB,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC5G,MAAM,CAAC,sBAAsB,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3K,CAAC;QACD,MAAM,CAAC,sBAAsB,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,KAAK,IAAI,EAAE;QAC/F,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAgB,CAAC,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1J,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,kCAAkC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9E,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;QACxB,KAAK,CAAC,aAAa,CAAC,WAAW,GAAG,4BAA4B,CAAC;QAC/D,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClE,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9G,MAAM,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;QACzE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;QAC5J,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACrF,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5E,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8FAA8F,EAAE,KAAK,IAAI,EAAE;QAC5G,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAgB,CAAC,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,kCAAkC,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAChF,MAAM,MAAM,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC5E,MAAM,MAAM,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACjH,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACnG,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACrG,KAAK,MAAM,GAAG,IAAI,CAAC,0BAA0B,EAAE,+BAA+B,EAAE,oCAAoC,EAAE,6BAA6B,EAAE,iBAAiB,CAAC,EAAE,CAAC;YACxK,MAAM,CAAC,GAAG,EAAE,CAAC,kCAAkC,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAClF,CAAC;QACD,MAAM,OAAO,GAAG,kCAAkC,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,SAAS,EAAE,0BAA0B,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9H,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9C,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;QACrF,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAgB,CAAC,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;aACzE,qBAAqB,CAAC,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC,CAAC;QAC7F,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,kCAAkC,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9F,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClE,MAAM,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;QACjF,MAAM,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACpD,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAgB,CAAC,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;aACzE,sBAAsB,CAAC,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,kCAAkC,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9F,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAChD,UAAU,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,KAAK,CAAC;QACZ,MAAM,CAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAsB,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAgB,CAAC,iBAAiB,CAAC,IAAI,QAAQ,CAAC,IAAI,cAAc,CAAC,EAAE,KAAK,KAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1G,MAAM,OAAO,GAAG,IAAI,CAAC,kCAAkC,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QACtF,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAChD,MAAM,EAAE,CAAC,wBAAwB,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,KAAK,CAAC;QACZ,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uFAAuF,EAAE,KAAK,IAAI,EAAE;QACrG,MAAM,SAAS,GAAG;YAChB,IAAI,CAAC,CAAC,GAAG,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YAClC,IAAI,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;YAC3F,IAAI,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;YACxI,IAAI,QAAQ,CAAC,iBAAiB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;YAChD,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;SAC9C,CAAC;QACF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAgB,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YAClE,MAAM,MAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAC7G,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,EAAE,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mission-execution.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-execution.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/mission-execution.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { admitMissionExecution, normalizeMissionExecutionCommit, validateMissionExecutionKey } from "../mission-execution.js";
3
+ function plan() {
4
+ const now = "2026-10-11T06:00:00.000Z";
5
+ return {
6
+ expected: { id: "mission", name: "Example", data: "{}", status: "draft", user: "customer-a", createdAt: now, updatedAt: now },
7
+ receipt: { executionId: "execution", missionId: "mission", idempotencyKey: "occurrence", group: "Example", sequence: 1,
8
+ user: "customer-a", createdAt: now, tasks: [{ id: "task", title: "Work", description: "Do work", assignTo: "support",
9
+ missionId: "mission", group: "Example", user: "customer-a", status: "pending", dependsOn: [], expectations: [], metrics: [],
10
+ retries: 0, maxRetries: 2, createdAt: now, updatedAt: now }] },
11
+ };
12
+ }
13
+ describe("Mission execution admission contract", () => {
14
+ it("rejects changed or missing application metadata in task snapshots", () => {
15
+ const input = plan();
16
+ input.expected.metadata = { connectionNamespace: "app-a" };
17
+ input.receipt.metadata = { connectionNamespace: "app-a" };
18
+ for (const task of input.receipt.tasks)
19
+ task.metadata = { connectionNamespace: "app-a" };
20
+ expect(normalizeMissionExecutionCommit(input)).toEqual(input);
21
+ input.receipt.tasks[0].metadata = { connectionNamespace: "app-b" };
22
+ expect(() => normalizeMissionExecutionCommit(input)).toThrowError(expect.objectContaining({ code: "mission_execution_invalid" }));
23
+ delete input.receipt.tasks[0].metadata;
24
+ expect(() => normalizeMissionExecutionCommit(input)).toThrowError(expect.objectContaining({ code: "mission_execution_invalid" }));
25
+ });
26
+ it.each([undefined, null, [], {}, { expected: {} }, { receipt: {} }])("rejects a malformed plan: %j", input => {
27
+ expect(() => normalizeMissionExecutionCommit(input)).toThrowError(expect.objectContaining({ code: "mission_execution_invalid" }));
28
+ });
29
+ it.each(["", " ", " padded", "padded ", "line\nbreak", "x".repeat(201)])("rejects an invalid occurrence key: %j", key => {
30
+ expect(() => validateMissionExecutionKey("mission", key)).toThrowError(expect.objectContaining({ code: "mission_execution_invalid" }));
31
+ });
32
+ it.each(["principal", "forward dependency", "sparse dependencies", "duplicate id", "duplicate title", "status", "counter", "oversize"])("rejects %s without admission", kind => {
33
+ const input = plan();
34
+ if (kind === "principal")
35
+ input.receipt.tasks[0].user = "other-customer";
36
+ if (kind === "forward dependency")
37
+ input.receipt.tasks[0].dependsOn = ["not-yet-created"];
38
+ if (kind === "sparse dependencies")
39
+ input.receipt.tasks[0].dependsOn = new Array(1);
40
+ if (kind === "duplicate id")
41
+ input.receipt.tasks.push({ ...input.receipt.tasks[0], title: "Other" });
42
+ if (kind === "duplicate title")
43
+ input.receipt.tasks.push({ ...input.receipt.tasks[0], id: "other" });
44
+ if (kind === "status")
45
+ input.receipt.tasks[0].status = "in_progress";
46
+ if (kind === "counter")
47
+ input.receipt.sequence = 2;
48
+ if (kind === "oversize")
49
+ input.expected.data = "x".repeat(4 * 1024 * 1024);
50
+ expect(() => normalizeMissionExecutionCommit(input)).toThrowError(expect.objectContaining({ code: "mission_execution_invalid" }));
51
+ });
52
+ it("takes an independent JSON snapshot and compares optional defaults consistently", () => {
53
+ const input = plan(), captured = normalizeMissionExecutionCommit(input);
54
+ input.expected.user = "other-customer";
55
+ input.receipt.tasks[0].dependsOn.push("injected");
56
+ expect(captured.expected.user).toBe("customer-a");
57
+ expect(captured.receipt.tasks[0].dependsOn).toEqual([]);
58
+ expect(admitMissionExecution(captured, { ...captured.expected, executionCount: 0 }, [])).toMatchObject({ status: "active", executionCount: 1 });
59
+ expect(() => admitMissionExecution(captured, input.expected, [])).toThrowError(expect.objectContaining({ code: "mission_execution_conflict" }));
60
+ });
61
+ it.each([null, {}, { volatileAgents: [null] }, { volatileAgents: [{ name: "helper" }, { name: "helper" }] },
62
+ { volatileAgents: [], taskTitles: [] }, { volatileAgents: [], taskTitles: [{ authored: "Work", accepted: "Unknown" }] },
63
+ ])("rejects runtime recovery metadata that cannot describe the accepted task plan: %j", runtime => {
64
+ const input = plan();
65
+ expect(() => normalizeMissionExecutionCommit({ ...input, runtime })).toThrowError(expect.objectContaining({ code: "mission_execution_invalid" }));
66
+ });
67
+ });
68
+ //# sourceMappingURL=mission-execution.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-execution.test.js","sourceRoot":"","sources":["../../src/__tests__/mission-execution.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=run-state-monitor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-state-monitor.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/run-state-monitor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { watchRunState } from "../run-state-monitor.js";
3
+ afterEach(() => vi.useRealTimers());
4
+ const record = (status) => ({ id: "run-a", status });
5
+ describe("durable Run state observation", () => {
6
+ it.each(["killed", "failed", "completed"])("stops the exact Run on persisted %s", async (status) => {
7
+ vi.useFakeTimers();
8
+ const getRun = vi.fn().mockResolvedValueOnce(record("running")).mockResolvedValue(record(status));
9
+ const observer = watchRunState({ runId: "run-a", runStore: { getRun }, pollIntervalMs: 10 });
10
+ await vi.advanceTimersByTimeAsync(10);
11
+ expect(observer.signal.aborted).toBe(false);
12
+ await vi.advanceTimersByTimeAsync(10);
13
+ expect(observer.signal.reason).toBe("run_terminal");
14
+ await vi.advanceTimersByTimeAsync(1000);
15
+ expect(getRun.mock.calls).toEqual([["run-a"], ["run-a"]]);
16
+ expect(vi.getTimerCount()).toBe(0);
17
+ observer.dispose();
18
+ });
19
+ it.each(["missing", "rejected", "hung"])("fails closed on %s persistence without leaking storage errors", async (mode) => {
20
+ vi.useFakeTimers();
21
+ const getRun = vi.fn(() => mode === "hung" ? new Promise(() => { })
22
+ : mode === "rejected" ? Promise.reject(new Error("private driver information")) : Promise.resolve(undefined));
23
+ const observer = watchRunState({ runId: "run-a", runStore: { getRun }, pollIntervalMs: 10, readTimeoutMs: 25 });
24
+ await vi.advanceTimersByTimeAsync(35);
25
+ expect(observer.signal.reason).toBe(mode === "missing" ? "run_missing" : "run_unavailable");
26
+ await vi.advanceTimersByTimeAsync(1000);
27
+ expect(getRun).toHaveBeenCalledTimes(1);
28
+ expect(vi.getTimerCount()).toBe(0);
29
+ observer.dispose();
30
+ });
31
+ it("does not overlap slow reads or apply their late result after disposal", async () => {
32
+ vi.useFakeTimers();
33
+ let resolve;
34
+ const getRun = vi.fn(() => new Promise(done => { resolve = done; }));
35
+ const observer = watchRunState({ runId: "run-a", runStore: { getRun }, pollIntervalMs: 10, readTimeoutMs: 100 });
36
+ await vi.advanceTimersByTimeAsync(50);
37
+ expect(getRun).toHaveBeenCalledTimes(1);
38
+ observer.dispose();
39
+ resolve(record("killed"));
40
+ await vi.advanceTimersByTimeAsync(1000);
41
+ expect(observer.signal.aborted).toBe(false);
42
+ expect(getRun).toHaveBeenCalledTimes(1);
43
+ expect(vi.getTimerCount()).toBe(0);
44
+ });
45
+ it("does not reactivate after a timed-out read eventually returns running", async () => {
46
+ vi.useFakeTimers();
47
+ let resolve;
48
+ const getRun = vi.fn(() => new Promise(done => { resolve = done; }));
49
+ const observer = watchRunState({ runId: "run-a", runStore: { getRun }, pollIntervalMs: 10, readTimeoutMs: 20 });
50
+ await vi.advanceTimersByTimeAsync(30);
51
+ resolve(record("running"));
52
+ await vi.advanceTimersByTimeAsync(100);
53
+ expect(observer.signal.reason).toBe("run_unavailable");
54
+ expect(getRun).toHaveBeenCalledTimes(1);
55
+ expect(vi.getTimerCount()).toBe(0);
56
+ observer.dispose();
57
+ });
58
+ });
59
+ //# sourceMappingURL=run-state-monitor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-state-monitor.test.js","sourceRoot":"","sources":["../../src/__tests__/run-state-monitor.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runtime-metadata.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-metadata.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/runtime-metadata.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { normalizeRuntimeMetadata, sameRuntimeMetadata } from "../runtime-metadata.js";
3
+ describe("caller metadata snapshots", () => {
4
+ it("compares independent JSON/JSONB key orderings without changing special keys", () => {
5
+ const source = JSON.parse('{"connectionNamespace":"app-a","__proto__":"data","z":"last","A":"first"}');
6
+ const captured = normalizeRuntimeMetadata(source);
7
+ source.connectionNamespace = "mutated";
8
+ expect(captured.connectionNamespace).toBe("app-a");
9
+ expect(Object.isFrozen(captured)).toBe(true);
10
+ expect(Object.getPrototypeOf(captured)).toBe(Object.prototype);
11
+ expect(Object.hasOwn(captured, "__proto__")).toBe(true);
12
+ expect(sameRuntimeMetadata(captured, JSON.parse('{"A":"first","z":"last","__proto__":"data","connectionNamespace":"app-a"}'))).toBe(true);
13
+ expect(sameRuntimeMetadata(captured, source)).toBe(false);
14
+ expect(sameRuntimeMetadata(undefined, {})).toBe(false);
15
+ });
16
+ });
17
+ //# sourceMappingURL=runtime-metadata.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-metadata.test.js","sourceRoot":"","sources":["../../src/__tests__/runtime-metadata.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAEvF,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,6EAA6E,EAAE,GAAG,EAAE;QACrF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,2EAA2E,CAAC,CAAC;QACvG,MAAM,QAAQ,GAAG,wBAAwB,CAAC,MAAM,CAAE,CAAC;QACnD,MAAM,CAAC,mBAAmB,GAAG,SAAS,CAAC;QACvC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,2EAA2E,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1I,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1D,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -104,7 +104,7 @@ function harness(overrides = {}) {
104
104
  }),
105
105
  },
106
106
  hooks: {
107
- runBeforeSync: vi.fn(() => ({ cancelled: false })),
107
+ runBefore: vi.fn(async () => ({ cancelled: false })),
108
108
  },
109
109
  spawner: {
110
110
  spawn: vi.fn(async (config) => {