@adhd/agent-mcp 0.0.9 → 1.0.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 (105) hide show
  1. package/drizzle/0004_brief_shocker.sql +4 -0
  2. package/drizzle/0005_clear_lenny_balinger.sql +25 -0
  3. package/drizzle/meta/0004_snapshot.json +551 -0
  4. package/drizzle/meta/0005_snapshot.json +545 -0
  5. package/drizzle/meta/_journal.json +14 -0
  6. package/package.json +1 -1
  7. package/src/__tests__/integration/harness.d.ts +195 -0
  8. package/src/__tests__/integration/harness.d.ts.map +1 -0
  9. package/src/__tests__/integration/harness.js +401 -0
  10. package/src/__tests__/integration/harness.js.map +1 -0
  11. package/src/__tests__/integration/scripted-provider.d.ts +44 -0
  12. package/src/__tests__/integration/scripted-provider.d.ts.map +1 -0
  13. package/src/__tests__/integration/scripted-provider.js +82 -0
  14. package/src/__tests__/integration/scripted-provider.js.map +1 -0
  15. package/src/clients/http-client.d.ts +1 -1
  16. package/src/clients/http-client.d.ts.map +1 -1
  17. package/src/clients/http-client.js +4 -2
  18. package/src/clients/http-client.js.map +1 -1
  19. package/src/clients/in-process.d.ts +1 -1
  20. package/src/clients/in-process.d.ts.map +1 -1
  21. package/src/clients/in-process.js +6 -1
  22. package/src/clients/in-process.js.map +1 -1
  23. package/src/clients/registry.d.ts +17 -0
  24. package/src/clients/registry.d.ts.map +1 -1
  25. package/src/clients/registry.js +45 -6
  26. package/src/clients/registry.js.map +1 -1
  27. package/src/clients/stdio-client.d.ts +1 -1
  28. package/src/clients/stdio-client.d.ts.map +1 -1
  29. package/src/clients/stdio-client.js +9 -3
  30. package/src/clients/stdio-client.js.map +1 -1
  31. package/src/clients/tool-naming.d.ts +20 -0
  32. package/src/clients/tool-naming.d.ts.map +1 -0
  33. package/src/clients/tool-naming.js +22 -0
  34. package/src/clients/tool-naming.js.map +1 -0
  35. package/src/clients/types.d.ts +5 -1
  36. package/src/clients/types.d.ts.map +1 -1
  37. package/src/db/client.d.ts +1 -0
  38. package/src/db/client.d.ts.map +1 -1
  39. package/src/db/client.js +1 -1
  40. package/src/db/client.js.map +1 -1
  41. package/src/db/migrate-runner.d.ts +38 -0
  42. package/src/db/migrate-runner.d.ts.map +1 -0
  43. package/src/db/migrate-runner.js +41 -0
  44. package/src/db/migrate-runner.js.map +1 -0
  45. package/src/db/migrate.d.ts +3 -0
  46. package/src/db/migrate.d.ts.map +1 -1
  47. package/src/db/migrate.js +6 -5
  48. package/src/db/migrate.js.map +1 -1
  49. package/src/db/schema.d.ts +96 -3
  50. package/src/db/schema.d.ts.map +1 -1
  51. package/src/db/schema.js +16 -6
  52. package/src/db/schema.js.map +1 -1
  53. package/src/engine/dag-engine.d.ts +62 -0
  54. package/src/engine/dag-engine.d.ts.map +1 -0
  55. package/src/engine/dag-engine.js +158 -0
  56. package/src/engine/dag-engine.js.map +1 -0
  57. package/src/engine/orchestrator.d.ts +14 -0
  58. package/src/engine/orchestrator.d.ts.map +1 -1
  59. package/src/engine/orchestrator.js +206 -58
  60. package/src/engine/orchestrator.js.map +1 -1
  61. package/src/engine/queue.d.ts +6 -0
  62. package/src/engine/queue.d.ts.map +1 -1
  63. package/src/engine/queue.js +8 -0
  64. package/src/engine/queue.js.map +1 -1
  65. package/src/index.js +75 -0
  66. package/src/index.js.map +1 -1
  67. package/src/providers/anthropic.d.ts.map +1 -1
  68. package/src/providers/anthropic.js +22 -3
  69. package/src/providers/anthropic.js.map +1 -1
  70. package/src/server.d.ts +18 -0
  71. package/src/server.d.ts.map +1 -1
  72. package/src/server.js +25 -2
  73. package/src/server.js.map +1 -1
  74. package/src/store/task-store.d.ts +7 -1
  75. package/src/store/task-store.d.ts.map +1 -1
  76. package/src/store/task-store.js +37 -5
  77. package/src/store/task-store.js.map +1 -1
  78. package/src/streaming/event-bus.d.ts +28 -0
  79. package/src/streaming/event-bus.d.ts.map +1 -0
  80. package/src/streaming/event-bus.js +12 -0
  81. package/src/streaming/event-bus.js.map +1 -0
  82. package/src/streaming/sse-server.d.ts +16 -0
  83. package/src/streaming/sse-server.d.ts.map +1 -0
  84. package/src/streaming/sse-server.js +118 -0
  85. package/src/streaming/sse-server.js.map +1 -0
  86. package/src/tools/task.d.ts +35 -0
  87. package/src/tools/task.d.ts.map +1 -1
  88. package/src/tools/task.js +200 -36
  89. package/src/tools/task.js.map +1 -1
  90. package/src/validation/agent.d.ts +3 -0
  91. package/src/validation/agent.d.ts.map +1 -1
  92. package/src/validation/agent.js +7 -0
  93. package/src/validation/agent.js.map +1 -1
  94. package/src/validation/errors.d.ts +1 -0
  95. package/src/validation/errors.d.ts.map +1 -1
  96. package/src/validation/errors.js +1 -0
  97. package/src/validation/errors.js.map +1 -1
  98. package/src/validation/execution.d.ts +2 -0
  99. package/src/validation/execution.d.ts.map +1 -1
  100. package/src/validation/execution.js +5 -0
  101. package/src/validation/execution.js.map +1 -1
  102. package/src/validation/task.d.ts +31 -1
  103. package/src/validation/task.d.ts.map +1 -1
  104. package/src/validation/task.js +24 -1
  105. package/src/validation/task.js.map +1 -1
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Integration test harness.
3
+ *
4
+ * Builds a fully-wired real system against a fresh temp sqlite database.
5
+ * Runs the same drizzle migrations used by the production server.
6
+ * Constructs real stores/queue/PolicyEngine/orchestrator/DagEngine with
7
+ * the same dispatchFn wiring as index.ts.
8
+ *
9
+ * The only scripted/stubbed components are:
10
+ * - LLMProvider (ScriptedProvider from scripted-provider.ts)
11
+ * - External MCP tool clients (in-process stubs at the registry boundary)
12
+ */
13
+ import http from "node:http";
14
+ import Database from "better-sqlite3";
15
+ import { drizzle } from "drizzle-orm/better-sqlite3";
16
+ import * as schema from "../../db/schema.js";
17
+ import { AgentStore } from "../../store/agent-store.js";
18
+ import { SessionStore } from "../../store/session-store.js";
19
+ import { TaskStore } from "../../store/task-store.js";
20
+ import { BackgroundQueue } from "../../engine/queue.js";
21
+ import { DagEngine } from "../../engine/dag-engine.js";
22
+ import { Orchestrator } from "../../engine/orchestrator.js";
23
+ import { HookRegistry } from "../../engine/hooks.js";
24
+ import { PolicyEngine } from "../../engine/policy.js";
25
+ import { taskTool, taskCancel, taskResume } from "../../tools/task.js";
26
+ import type { TaskDeps } from "../../tools/task.js";
27
+ import type { AgentCreateInput } from "../../validation/index.js";
28
+ import type { LLMProvider } from "../../providers/types.js";
29
+ export type TestDb = ReturnType<typeof drizzle<typeof schema>>;
30
+ export interface Harness {
31
+ db: TestDb;
32
+ rawSqlite: InstanceType<typeof Database>;
33
+ agentStore: AgentStore;
34
+ sessionStore: SessionStore;
35
+ taskStore: TaskStore;
36
+ queue: BackgroundQueue;
37
+ orchestrator: Orchestrator;
38
+ policy: PolicyEngine;
39
+ dagEngine: DagEngine;
40
+ taskDeps: TaskDeps;
41
+ hooks: HookRegistry;
42
+ /** Tear down: close DB, stop SSE server if started */
43
+ teardown: () => Promise<void>;
44
+ /** The file path of the temp DB (for restart tests) */
45
+ dbPath: string;
46
+ /** SSE server (if started) */
47
+ sseServer?: http.Server;
48
+ /** Ephemeral port the SSE server is bound to (if started) */
49
+ ssePort?: number;
50
+ }
51
+ export interface HarnessOptions {
52
+ /** Start an SSE server on an ephemeral port */
53
+ withSse?: boolean;
54
+ /** Override server-max-tool-loops */
55
+ serverMaxToolLoops?: number;
56
+ /** Use an explicit DB path (for restart tests) */
57
+ dbPath?: string;
58
+ /**
59
+ * Skip the automatic orphan-scan re-enqueue on build.
60
+ * Prefer `defaultProvider` over this flag: that keeps the real scan
61
+ * exercised while avoiding the external-provider race.
62
+ */
63
+ skipOrphanScan?: boolean;
64
+ /**
65
+ * Inject a scripted LLMProvider into the harness orchestrator so every
66
+ * task dispatched through taskDeps (including the startup orphan scan)
67
+ * uses it instead of the real provider built from agentDefinition.provider.
68
+ *
69
+ * This mirrors the pattern used for per-task provider injection but applies
70
+ * it at harness construction time — so the orphan scan that fires during
71
+ * buildHarness already has the right provider, with no race against a
72
+ * parallel bad-provider run.
73
+ */
74
+ defaultProvider?: LLMProvider;
75
+ }
76
+ /**
77
+ * Build a fully-wired harness. Call teardown() when done.
78
+ */
79
+ export declare function buildHarness(opts?: HarnessOptions): Promise<Harness>;
80
+ /**
81
+ * Rebuild a harness against the SAME DB file (for restart/persistence tests).
82
+ * Does NOT re-run migrations (they're already applied).
83
+ * DOES run the orphan scan.
84
+ */
85
+ export declare function rebuildHarness(dbPath: string, opts?: Omit<HarnessOptions, "dbPath">): Promise<Harness>;
86
+ /**
87
+ * Wait for the queue to drain (all enqueued tasks finished).
88
+ * Uses p-queue's onIdle() with a bounded deadline, which is more reliable
89
+ * than polling pending+size (which can momentarily read zero between batches).
90
+ */
91
+ export declare function drainQueue(queue: BackgroundQueue, timeoutMs?: number): Promise<void>;
92
+ /**
93
+ * Create a minimal test agent + open a session. Returns { agentName, sessionId }.
94
+ */
95
+ export declare function createSessionAndAgent(harness: Harness, provider: LLMProvider, agentOverrides?: Partial<AgentCreateInput>): Promise<{
96
+ agentName: string;
97
+ sessionId: string;
98
+ }>;
99
+ /**
100
+ * Run a task synchronously via the real taskTool, injecting the given provider
101
+ * into the orchestrator for this task only.
102
+ *
103
+ * We achieve provider injection by temporarily monkey-patching the registry
104
+ * to use the scripted provider — the cleanest approach without modifying
105
+ * production source. The provider is captured in a closure inside the harness
106
+ * task runner below.
107
+ */
108
+ export declare function runTaskViaToolWithProvider(harness: Harness, sessionId: string, prompt: string, provider: LLMProvider, extraInput?: Record<string, unknown>): Promise<{
109
+ task_id: string;
110
+ status: "pending" | "running" | "completed" | "failed" | "cancelled" | "waiting" | "awaiting_input";
111
+ result?: string | undefined;
112
+ stream_url?: string | undefined;
113
+ usage?: {
114
+ direct: {
115
+ inputTokens: number;
116
+ outputTokens: number;
117
+ modelCalls: number;
118
+ toolCallCount: number;
119
+ latencyMs: number;
120
+ stopReason?: string | undefined;
121
+ };
122
+ subtree: {
123
+ inputTokens: number;
124
+ outputTokens: number;
125
+ modelCalls: number;
126
+ toolCallCount: number;
127
+ latencyMs: number;
128
+ stopReason?: string | undefined;
129
+ };
130
+ taskCount: number;
131
+ } | undefined;
132
+ }>;
133
+ /**
134
+ * Background version — enqueues and returns immediately; use drainQueue to wait.
135
+ */
136
+ export declare function enqueueTaskWithProvider(harness: Harness, sessionId: string, prompt: string, provider: LLMProvider, extraInput?: Record<string, unknown>): Promise<{
137
+ task_id: string;
138
+ status: "pending" | "running" | "completed" | "failed" | "cancelled" | "waiting" | "awaiting_input";
139
+ result?: string | undefined;
140
+ stream_url?: string | undefined;
141
+ usage?: {
142
+ direct: {
143
+ inputTokens: number;
144
+ outputTokens: number;
145
+ modelCalls: number;
146
+ toolCallCount: number;
147
+ latencyMs: number;
148
+ stopReason?: string | undefined;
149
+ };
150
+ subtree: {
151
+ inputTokens: number;
152
+ outputTokens: number;
153
+ modelCalls: number;
154
+ toolCallCount: number;
155
+ latencyMs: number;
156
+ stopReason?: string | undefined;
157
+ };
158
+ taskCount: number;
159
+ } | undefined;
160
+ }>;
161
+ /**
162
+ * A binary latch. One side waits; the other releases.
163
+ * Used for proving concurrency without sleeps.
164
+ */
165
+ export declare class Latch {
166
+ private _resolve;
167
+ private _reject;
168
+ readonly promise: Promise<void>;
169
+ constructor();
170
+ release(): void;
171
+ fail(err: Error): void;
172
+ /** Await with a bounded deadline. Rejects if deadline exceeded. */
173
+ wait(timeoutMs?: number): Promise<void>;
174
+ }
175
+ export interface SseFrame {
176
+ type: string;
177
+ data: Record<string, unknown>;
178
+ }
179
+ /**
180
+ * Connect to the real SSE server and collect frames until "done" or timeout.
181
+ * Returns collected frames in order.
182
+ *
183
+ * @param port - Ephemeral port from harness.ssePort
184
+ * @param taskId - Task UUID
185
+ * @param timeoutMs - Bounded deadline (default 8s)
186
+ * @param onConnected - Optional callback fired when the HTTP 200 response
187
+ * headers are received (i.e. the SSE connection is established and the
188
+ * server has accepted the request). Use this to release a latch that
189
+ * unblocks a background task, guaranteeing the SSE subscription is active
190
+ * before any events are emitted.
191
+ */
192
+ export declare function collectSseFrames(port: number, taskId: string, timeoutMs?: number, onConnected?: () => void): Promise<SseFrame[]>;
193
+ export { taskTool, taskCancel, taskResume };
194
+ export type { TaskDeps };
195
+ //# sourceMappingURL=harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../../../../../../packages/ai/agent-mcp/src/__tests__/integration/harness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,401 @@
1
+ /**
2
+ * Integration test harness.
3
+ *
4
+ * Builds a fully-wired real system against a fresh temp sqlite database.
5
+ * Runs the same drizzle migrations used by the production server.
6
+ * Constructs real stores/queue/PolicyEngine/orchestrator/DagEngine with
7
+ * the same dispatchFn wiring as index.ts.
8
+ *
9
+ * The only scripted/stubbed components are:
10
+ * - LLMProvider (ScriptedProvider from scripted-provider.ts)
11
+ * - External MCP tool clients (in-process stubs at the registry boundary)
12
+ */
13
+ import os from "node:os";
14
+ import path from "node:path";
15
+ import fs from "node:fs";
16
+ import http from "node:http";
17
+ import crypto from "node:crypto";
18
+ import Database from "better-sqlite3";
19
+ import { drizzle } from "drizzle-orm/better-sqlite3";
20
+ import { fileURLToPath } from "node:url";
21
+ import * as schema from "../../db/schema.js";
22
+ import { AgentStore } from "../../store/agent-store.js";
23
+ import { SessionStore } from "../../store/session-store.js";
24
+ import { TaskStore } from "../../store/task-store.js";
25
+ import { BackgroundQueue } from "../../engine/queue.js";
26
+ import { DagEngine } from "../../engine/dag-engine.js";
27
+ import { Orchestrator } from "../../engine/orchestrator.js";
28
+ import { HookRegistry } from "../../engine/hooks.js";
29
+ import { PolicyEngine } from "../../engine/policy.js";
30
+ import { startSseServer } from "../../streaming/sse-server.js";
31
+ import { enqueueExistingTask } from "../../tools/task.js";
32
+ import { runMigrationsOn } from "../../db/migrate-runner.js";
33
+ import { taskTool, taskCancel, taskResume } from "../../tools/task.js";
34
+ import { tasksTable } from "../../db/schema.js";
35
+ import { eq } from "drizzle-orm";
36
+ import { generateId } from "../../utils/ids.js";
37
+ const __dirname = fileURLToPath(new URL(".", import.meta.url));
38
+ const MIGRATIONS_FOLDER = path.resolve(__dirname, "../../../drizzle");
39
+ /**
40
+ * Build a fully-wired harness. Call teardown() when done.
41
+ */
42
+ export async function buildHarness(opts = {}) {
43
+ const dbPath = opts.dbPath ??
44
+ path.join(os.tmpdir(), `agent-mcp-test-${crypto.randomUUID()}.db`);
45
+ const rawSqlite = new Database(dbPath);
46
+ rawSqlite.pragma("journal_mode = WAL");
47
+ rawSqlite.pragma("foreign_keys = ON");
48
+ const db = drizzle(rawSqlite, { schema });
49
+ // Run migrations — same folder + FK-safe runner as production
50
+ runMigrationsOn(rawSqlite, db, MIGRATIONS_FOLDER);
51
+ const hooks = new HookRegistry();
52
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
+ const dbAny = db;
54
+ const agentStore = new AgentStore(dbAny, hooks);
55
+ const sessionStore = new SessionStore(dbAny, hooks);
56
+ const taskStore = new TaskStore(dbAny);
57
+ const queue = new BackgroundQueue(5);
58
+ const orchestrator = new Orchestrator();
59
+ const policy = new PolicyEngine({
60
+ serverMaxDepth: 5,
61
+ serverMaxToolLoops: opts.serverMaxToolLoops ?? 50,
62
+ });
63
+ // Mirror dispatchFn wiring from index.ts
64
+ let taskDeps;
65
+ const dispatchFn = async (taskId) => {
66
+ if (!taskDeps)
67
+ throw new Error("dispatchFn called before harness initialized");
68
+ await enqueueExistingTask(taskId, taskDeps);
69
+ };
70
+ const dagEngine = new DagEngine(dbAny, queue, taskStore, dispatchFn);
71
+ // SSE server (optional)
72
+ let sseServer;
73
+ let ssePort;
74
+ if (opts.withSse) {
75
+ // Bind an ephemeral port (0) directly — startSseServer does the single
76
+ // listen() internally; we just wait for it and read the chosen port.
77
+ sseServer = startSseServer(taskStore, 0, "127.0.0.1");
78
+ await new Promise((resolve) => {
79
+ const readPort = () => {
80
+ const addr = sseServer.address();
81
+ ssePort = addr?.port;
82
+ resolve();
83
+ };
84
+ if (sseServer.listening)
85
+ readPort();
86
+ else
87
+ sseServer.once("listening", readPort);
88
+ });
89
+ }
90
+ // Build taskDeps — same shape as index.ts.
91
+ // When defaultProvider is supplied, wrap the orchestrator so every run
92
+ // that goes through taskDeps (including the startup orphan scan) uses it
93
+ // instead of the real provider built from agentDefinition.provider.
94
+ const effectiveOrchestrator = opts.defaultProvider
95
+ ? {
96
+ run: (input) => orchestrator.run({ ...input, provider: opts.defaultProvider }),
97
+ }
98
+ : orchestrator;
99
+ taskDeps = {
100
+ agentStore,
101
+ sessionStore,
102
+ taskStore,
103
+ orchestrator: effectiveOrchestrator,
104
+ queue,
105
+ policy,
106
+ hooks,
107
+ selfUrl: undefined,
108
+ inProcessDescriptors: [],
109
+ inProcessHandler: async () => {
110
+ throw new Error("in-process tools not configured in test harness");
111
+ },
112
+ db: dbAny,
113
+ dagEngine,
114
+ };
115
+ // Re-enqueue orphaned pending tasks (mirrors startup scan in index.ts).
116
+ // Skipped when opts.skipOrphanScan is true — callers that need custom
117
+ // (patched) deps can trigger enqueueExistingTask manually.
118
+ if (!opts.skipOrphanScan) {
119
+ const orphanedPending = dbAny
120
+ .select()
121
+ .from(tasksTable)
122
+ .where(eq(tasksTable.status, "pending"))
123
+ .all();
124
+ for (const row of orphanedPending) {
125
+ try {
126
+ await enqueueExistingTask(row.id, taskDeps);
127
+ }
128
+ catch {
129
+ // ignore failures in teardown
130
+ }
131
+ }
132
+ }
133
+ const teardown = async () => {
134
+ // 1. Close the SSE server first so no new HTTP responses write to the DB.
135
+ if (sseServer) {
136
+ await new Promise((resolve) => sseServer.close(() => resolve()));
137
+ }
138
+ // 2. Drain the queue fully via p-queue's onIdle() — more reliable than
139
+ // polling pending+size, which can momentarily read zero between tasks.
140
+ // Race against a 15s deadline to avoid hanging test suite.
141
+ await Promise.race([
142
+ queue.onIdle(),
143
+ new Promise((_, reject) => setTimeout(() => reject(new Error("teardown: queue.onIdle() timed out after 15s")), 15_000)),
144
+ ]).catch(() => {
145
+ // Log but don't rethrow — we still need to close the DB.
146
+ });
147
+ // 3. One event-loop tick so the last task's finally-block DB writes settle.
148
+ await new Promise((r) => setImmediate(r));
149
+ // 4. Close the connection cleanly BEFORE unlinking the file.
150
+ //
151
+ // The previous strategy (leak the handle, unlink the open file) caused
152
+ // the intermittent teardown SIGSEGV (exit 139): in WAL mode, at process
153
+ // exit better-sqlite3's destructor checkpoints the WAL — but the backing
154
+ // file had already been unlinked, so the checkpoint operates on a deleted
155
+ // file and crashes the native addon. Leaking also let connections
156
+ // accumulate across the suite, multiplying the destructor work at exit.
157
+ //
158
+ // Closing here, after onIdle()+setImmediate have settled all statements,
159
+ // is race-free: TRUNCATE-checkpoint the WAL into the still-present file,
160
+ // then close(). better-sqlite3 finalizes drizzle's cached statements on
161
+ // close. Only then do we unlink the (now fully released) files.
162
+ if (rawSqlite.open) {
163
+ try {
164
+ rawSqlite.pragma("wal_checkpoint(TRUNCATE)");
165
+ }
166
+ catch {
167
+ // checkpoint is best-effort
168
+ }
169
+ try {
170
+ rawSqlite.close();
171
+ }
172
+ catch {
173
+ // already closed (e.g. a test closed it explicitly)
174
+ }
175
+ }
176
+ for (const suffix of ["", "-wal", "-shm"]) {
177
+ try {
178
+ fs.unlinkSync(`${dbPath}${suffix}`);
179
+ }
180
+ catch {
181
+ // ignore — file may not exist (e.g. -wal after TRUNCATE close)
182
+ }
183
+ }
184
+ };
185
+ return {
186
+ db,
187
+ rawSqlite,
188
+ agentStore,
189
+ sessionStore,
190
+ taskStore,
191
+ queue,
192
+ // Expose the effective orchestrator (wrapped when defaultProvider is set)
193
+ // so per-test overrides in runTaskViaToolWithProvider compose correctly.
194
+ orchestrator: effectiveOrchestrator,
195
+ policy,
196
+ dagEngine,
197
+ taskDeps: taskDeps,
198
+ hooks,
199
+ teardown,
200
+ dbPath,
201
+ sseServer,
202
+ ssePort,
203
+ };
204
+ }
205
+ /**
206
+ * Rebuild a harness against the SAME DB file (for restart/persistence tests).
207
+ * Does NOT re-run migrations (they're already applied).
208
+ * DOES run the orphan scan.
209
+ */
210
+ export async function rebuildHarness(dbPath, opts = {}) {
211
+ return buildHarness({ ...opts, dbPath });
212
+ }
213
+ // ──────────────────────────────────────────────────────────────────────────────
214
+ // Helpers
215
+ // ──────────────────────────────────────────────────────────────────────────────
216
+ /**
217
+ * Wait for the queue to drain (all enqueued tasks finished).
218
+ * Uses p-queue's onIdle() with a bounded deadline, which is more reliable
219
+ * than polling pending+size (which can momentarily read zero between batches).
220
+ */
221
+ export async function drainQueue(queue, timeoutMs = 10_000) {
222
+ await Promise.race([
223
+ queue.onIdle(),
224
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`drainQueue timed out after ${timeoutMs}ms`)), timeoutMs)),
225
+ ]);
226
+ // One extra tick to let the last task's finally-block DB writes settle.
227
+ await new Promise((r) => setImmediate(r));
228
+ }
229
+ /**
230
+ * Create a minimal test agent + open a session. Returns { agentName, sessionId }.
231
+ */
232
+ export async function createSessionAndAgent(harness, provider, agentOverrides = {}) {
233
+ const agentName = `test-agent-${generateId()}`;
234
+ // Inject the provider into the factory by using a special "stub" type
235
+ // The provider is passed via the harness task runner helper
236
+ harness.agentStore.create({
237
+ name: agentName,
238
+ provider: { type: "openai", model: "test-model" },
239
+ systemPrompt: "You are a test assistant.",
240
+ mcpServers: {},
241
+ permissions: {},
242
+ ...agentOverrides,
243
+ });
244
+ const agentDef = harness.agentStore.read(agentName);
245
+ const session = harness.sessionStore.create({
246
+ agentName,
247
+ agentDefinition: agentDef,
248
+ });
249
+ return { agentName, sessionId: session.id };
250
+ }
251
+ /**
252
+ * Run a task synchronously via the real taskTool, injecting the given provider
253
+ * into the orchestrator for this task only.
254
+ *
255
+ * We achieve provider injection by temporarily monkey-patching the registry
256
+ * to use the scripted provider — the cleanest approach without modifying
257
+ * production source. The provider is captured in a closure inside the harness
258
+ * task runner below.
259
+ */
260
+ export async function runTaskViaToolWithProvider(harness, sessionId, prompt, provider, extraInput = {}) {
261
+ // Wrap the orchestrator to inject the scripted provider for this one run
262
+ const realOrchestrator = harness.orchestrator;
263
+ const originalRun = realOrchestrator.run.bind(realOrchestrator);
264
+ const patchedDeps = {
265
+ ...harness.taskDeps,
266
+ orchestrator: {
267
+ run: (input) => originalRun({ ...input, provider }),
268
+ },
269
+ };
270
+ return taskTool({ session_id: sessionId, prompt, background: false, ...extraInput }, patchedDeps);
271
+ }
272
+ /**
273
+ * Background version — enqueues and returns immediately; use drainQueue to wait.
274
+ */
275
+ export async function enqueueTaskWithProvider(harness, sessionId, prompt, provider, extraInput = {}) {
276
+ const realOrchestrator = harness.orchestrator;
277
+ const originalRun = realOrchestrator.run.bind(realOrchestrator);
278
+ const patchedDeps = {
279
+ ...harness.taskDeps,
280
+ orchestrator: {
281
+ run: (input) => originalRun({ ...input, provider }),
282
+ },
283
+ };
284
+ return taskTool({ session_id: sessionId, prompt, background: true, ...extraInput }, patchedDeps);
285
+ }
286
+ // ──────────────────────────────────────────────────────────────────────────────
287
+ // Latch utility
288
+ // ──────────────────────────────────────────────────────────────────────────────
289
+ /**
290
+ * A binary latch. One side waits; the other releases.
291
+ * Used for proving concurrency without sleeps.
292
+ */
293
+ export class Latch {
294
+ _resolve;
295
+ _reject;
296
+ promise;
297
+ constructor() {
298
+ this.promise = new Promise((res, rej) => {
299
+ this._resolve = res;
300
+ this._reject = rej;
301
+ });
302
+ }
303
+ release() {
304
+ this._resolve();
305
+ }
306
+ fail(err) {
307
+ this._reject(err);
308
+ }
309
+ /** Await with a bounded deadline. Rejects if deadline exceeded. */
310
+ async wait(timeoutMs = 5_000) {
311
+ return Promise.race([
312
+ this.promise,
313
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Latch.wait timed out after ${timeoutMs}ms`)), timeoutMs)),
314
+ ]);
315
+ }
316
+ }
317
+ /**
318
+ * Connect to the real SSE server and collect frames until "done" or timeout.
319
+ * Returns collected frames in order.
320
+ *
321
+ * @param port - Ephemeral port from harness.ssePort
322
+ * @param taskId - Task UUID
323
+ * @param timeoutMs - Bounded deadline (default 8s)
324
+ * @param onConnected - Optional callback fired when the HTTP 200 response
325
+ * headers are received (i.e. the SSE connection is established and the
326
+ * server has accepted the request). Use this to release a latch that
327
+ * unblocks a background task, guaranteeing the SSE subscription is active
328
+ * before any events are emitted.
329
+ */
330
+ export async function collectSseFrames(port, taskId, timeoutMs = 8_000, onConnected) {
331
+ return new Promise((resolve, reject) => {
332
+ const frames = [];
333
+ const timer = setTimeout(() => {
334
+ reject(new Error(`collectSseFrames timed out after ${timeoutMs}ms`));
335
+ }, timeoutMs);
336
+ const req = http.get(`http://127.0.0.1:${port}/tasks/${taskId}/stream`, { headers: { Accept: "text/event-stream" } }, (res) => {
337
+ // Fire the onConnected callback as soon as the HTTP response
338
+ // headers arrive — this confirms the SSE endpoint has accepted
339
+ // the request and the subscription is active on the server.
340
+ onConnected?.();
341
+ let buf = "";
342
+ let currentEventType = "";
343
+ res.on("data", (chunk) => {
344
+ buf += chunk.toString();
345
+ const lines = buf.split("\n");
346
+ buf = lines.pop() ?? "";
347
+ for (const line of lines) {
348
+ if (line.startsWith("event: ")) {
349
+ currentEventType = line.slice("event: ".length).trim();
350
+ }
351
+ else if (line.startsWith("data: ")) {
352
+ const raw = line.slice("data: ".length).trim();
353
+ try {
354
+ const data = JSON.parse(raw);
355
+ frames.push({ type: currentEventType, data });
356
+ if (currentEventType === "done") {
357
+ clearTimeout(timer);
358
+ req.destroy();
359
+ resolve(frames);
360
+ }
361
+ }
362
+ catch {
363
+ // ignore parse errors (e.g. ping lines)
364
+ }
365
+ }
366
+ // reset event type on blank line (SSE spec)
367
+ if (line === "") {
368
+ currentEventType = "";
369
+ }
370
+ }
371
+ });
372
+ res.on("error", (err) => {
373
+ clearTimeout(timer);
374
+ // If connection was destroyed after done frame, that's fine
375
+ if (frames.some((f) => f.type === "done")) {
376
+ resolve(frames);
377
+ }
378
+ else {
379
+ reject(err);
380
+ }
381
+ });
382
+ res.on("end", () => {
383
+ clearTimeout(timer);
384
+ resolve(frames);
385
+ });
386
+ });
387
+ req.on("error", (err) => {
388
+ clearTimeout(timer);
389
+ // Destroyed connections after done frame are fine
390
+ if (frames.some((f) => f.type === "done")) {
391
+ resolve(frames);
392
+ }
393
+ else {
394
+ reject(err);
395
+ }
396
+ });
397
+ });
398
+ }
399
+ // Re-export tools for convenience
400
+ export { taskTool, taskCancel, taskResume };
401
+ //# sourceMappingURL=harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.js","sourceRoot":"","sources":["../../../../../../../packages/ai/agent-mcp/src/__tests__/integration/harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,KAAK,MAAM,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAKvE,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;AAoDtE;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAuB,EAAE;IACxD,MAAM,MAAM,GACR,IAAI,CAAC,MAAM;QACX,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,kBAAkB,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAEvE,MAAM,SAAS,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvC,SAAS,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACvC,SAAS,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAEtC,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,CAAsB,CAAC;IAE/D,8DAA8D;IAC9D,eAAe,CACX,SAAS,EACT,EAA2C,EAC3C,iBAAiB,CACpB,CAAC;IAEF,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC;IACjC,8DAA8D;IAC9D,MAAM,KAAK,GAAG,EAAS,CAAC;IAExB,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAChD,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC;IAEvC,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC;QAC5B,cAAc,EAAE,CAAC;QACjB,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,IAAI,EAAE;KACpD,CAAC,CAAC;IAEH,yCAAyC;IACzC,IAAI,QAA8B,CAAC;IAEnC,MAAM,UAAU,GAAG,KAAK,EAAE,MAAc,EAAiB,EAAE;QACvD,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAC/E,MAAM,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAErE,wBAAwB;IACxB,IAAI,SAAkC,CAAC;IACvC,IAAI,OAA2B,CAAC;IAEhC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,uEAAuE;QACvE,qEAAqE;QACrE,SAAS,GAAG,cAAc,CAAC,SAAS,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC;QACtD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAChC,MAAM,QAAQ,GAAG,GAAG,EAAE;gBAClB,MAAM,IAAI,GAAG,SAAU,CAAC,OAAO,EAA6B,CAAC;gBAC7D,OAAO,GAAG,IAAI,EAAE,IAAI,CAAC;gBACrB,OAAO,EAAE,CAAC;YACd,CAAC,CAAC;YACF,IAAI,SAAU,CAAC,SAAS;gBAAE,QAAQ,EAAE,CAAC;;gBAChC,SAAU,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACP,CAAC;IAED,2CAA2C;IAC3C,uEAAuE;IACvE,yEAAyE;IACzE,oEAAoE;IACpE,MAAM,qBAAqB,GAAiB,IAAI,CAAC,eAAe;QAC5D,CAAC,CAAE;YACC,GAAG,EAAE,CAAC,KAAyC,EAAE,EAAE,CAC/C,YAAY,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,eAAgB,EAAE,CAAC;SACnD;QACpB,CAAC,CAAC,YAAY,CAAC;IAEnB,QAAQ,GAAG;QACP,UAAU;QACV,YAAY;QACZ,SAAS;QACT,YAAY,EAAE,qBAAqB;QACnC,KAAK;QACL,MAAM;QACN,KAAK;QACL,OAAO,EAAE,SAAS;QAClB,oBAAoB,EAAE,EAAE;QACxB,gBAAgB,EAAE,KAAK,IAAI,EAAE;YACzB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACvE,CAAC;QACD,EAAE,EAAE,KAAK;QACT,SAAS;KACZ,CAAC;IAEF,wEAAwE;IACxE,sEAAsE;IACtE,2DAA2D;IAC3D,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;QACvB,MAAM,eAAe,GAAG,KAAK;aACxB,MAAM,EAAE;aACR,IAAI,CAAC,UAAU,CAAC;aAChB,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;aACvC,GAAG,EAA2B,CAAC;QAEpC,KAAK,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;YAChC,IAAI,CAAC;gBACD,MAAM,mBAAmB,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC;gBACL,8BAA8B;YAClC,CAAC;QACL,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACvC,0EAA0E;QAC1E,IAAI,SAAS,EAAE,CAAC;YACZ,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,SAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC5E,CAAC;QAED,uEAAuE;QACvE,0EAA0E;QAC1E,8DAA8D;QAC9D,MAAM,OAAO,CAAC,IAAI,CAAC;YACf,KAAK,CAAC,MAAM,EAAE;YACd,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC5B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC,EAAE,MAAM,CAAC,CAC9F;SACJ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACV,yDAAyD;QAC7D,CAAC,CAAC,CAAC;QAEH,4EAA4E;QAC5E,MAAM,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QAEhD,6DAA6D;QAC7D,EAAE;QACF,0EAA0E;QAC1E,2EAA2E;QAC3E,4EAA4E;QAC5E,6EAA6E;QAC7E,qEAAqE;QACrE,2EAA2E;QAC3E,EAAE;QACF,4EAA4E;QAC5E,4EAA4E;QAC5E,2EAA2E;QAC3E,mEAAmE;QACnE,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;YACjB,IAAI,CAAC;gBACD,SAAS,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC;YACjD,CAAC;YAAC,MAAM,CAAC;gBACL,4BAA4B;YAChC,CAAC;YACD,IAAI,CAAC;gBACD,SAAS,CAAC,KAAK,EAAE,CAAC;YACtB,CAAC;YAAC,MAAM,CAAC;gBACL,oDAAoD;YACxD,CAAC;QACL,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;YACxC,IAAI,CAAC;gBACD,EAAE,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;YACxC,CAAC;YAAC,MAAM,CAAC;gBACL,+DAA+D;YACnE,CAAC;QACL,CAAC;IACL,CAAC,CAAC;IAEF,OAAO;QACH,EAAE;QACF,SAAS;QACT,UAAU;QACV,YAAY;QACZ,SAAS;QACT,KAAK;QACL,0EAA0E;QAC1E,yEAAyE;QACzE,YAAY,EAAE,qBAAqB;QACnC,MAAM;QACN,SAAS;QACT,QAAQ,EAAE,QAAoB;QAC9B,KAAK;QACL,QAAQ;QACR,MAAM;QACN,SAAS;QACT,OAAO;KACV,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc,EAAE,OAAuC,EAAE;IAC1F,OAAO,YAAY,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,iFAAiF;AACjF,UAAU;AACV,iFAAiF;AAEjF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAsB,EAAE,SAAS,GAAG,MAAM;IACvE,MAAM,OAAO,CAAC,IAAI,CAAC;QACf,KAAK,CAAC,MAAM,EAAE;QACd,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC5B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,8BAA8B,SAAS,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC,CAC9F;KACJ,CAAC,CAAC;IACH,wEAAwE;IACxE,MAAM,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACvC,OAAgB,EAChB,QAAqB,EACrB,iBAA4C,EAAE;IAE9C,MAAM,SAAS,GAAG,cAAc,UAAU,EAAE,EAAE,CAAC;IAE/C,sEAAsE;IACtE,4DAA4D;IAC5D,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;QACtB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE;QACjD,YAAY,EAAE,2BAA2B;QACzC,UAAU,EAAE,EAAE;QACd,WAAW,EAAE,EAAE;QACf,GAAG,cAAc;KACpB,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC;QACxC,SAAS;QACT,eAAe,EAAE,QAAQ;KAC5B,CAAC,CAAC;IAEH,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;AAChD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC5C,OAAgB,EAChB,SAAiB,EACjB,MAAc,EACd,QAAqB,EACrB,aAAsC,EAAE;IAExC,yEAAyE;IACzE,MAAM,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;IAC9C,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAEhE,MAAM,WAAW,GAAa;QAC1B,GAAG,OAAO,CAAC,QAAQ;QACnB,YAAY,EAAE;YACV,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,CAAC;SACtC;KACpB,CAAC;IAEF,OAAO,QAAQ,CACX,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,UAAU,EAAoC,EACrG,WAAW,CACd,CAAC;AACN,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CACzC,OAAgB,EAChB,SAAiB,EACjB,MAAc,EACd,QAAqB,EACrB,aAAsC,EAAE;IAExC,MAAM,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;IAC9C,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAEhE,MAAM,WAAW,GAAa;QAC1B,GAAG,OAAO,CAAC,QAAQ;QACnB,YAAY,EAAE;YACV,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,CAAC;SACtC;KACpB,CAAC;IAEF,OAAO,QAAQ,CACX,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,UAAU,EAAoC,EACpG,WAAW,CACd,CAAC;AACN,CAAC;AAED,iFAAiF;AACjF,gBAAgB;AAChB,iFAAiF;AAEjF;;;GAGG;AACH,MAAM,OAAO,KAAK;IACN,QAAQ,CAAc;IACtB,OAAO,CAAwB;IAC9B,OAAO,CAAgB;IAEhC;QACI,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC1C,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;YACpB,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC;QACvB,CAAC,CAAC,CAAC;IACP,CAAC;IAED,OAAO;QACH,IAAI,CAAC,QAAQ,EAAE,CAAC;IACpB,CAAC;IAED,IAAI,CAAC,GAAU;QACX,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,mEAAmE;IACnE,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,KAAK;QACxB,OAAO,OAAO,CAAC,IAAI,CAAC;YAChB,IAAI,CAAC,OAAO;YACZ,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC5B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,8BAA8B,SAAS,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC,CAC9F;SACJ,CAAC,CAAC;IACP,CAAC;CACJ;AAWD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAClC,IAAY,EACZ,MAAc,EACd,SAAS,GAAG,KAAK,EACjB,WAAwB;IAExB,OAAO,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC/C,MAAM,MAAM,GAAe,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1B,MAAM,CAAC,IAAI,KAAK,CAAC,oCAAoC,SAAS,IAAI,CAAC,CAAC,CAAC;QACzE,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAChB,oBAAoB,IAAI,UAAU,MAAM,SAAS,EACjD,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE,EAAE,EAC5C,CAAC,GAAG,EAAE,EAAE;YACJ,6DAA6D;YAC7D,+DAA+D;YAC/D,4DAA4D;YAC5D,WAAW,EAAE,EAAE,CAAC;YAChB,IAAI,GAAG,GAAG,EAAE,CAAC;YACb,IAAI,gBAAgB,GAAG,EAAE,CAAC;YAE1B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBAC7B,GAAG,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACxB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC9B,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;gBAExB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACvB,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;wBAC7B,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;oBAC3D,CAAC;yBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;wBACnC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;wBAC/C,IAAI,CAAC;4BACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;4BACxD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;4BAC9C,IAAI,gBAAgB,KAAK,MAAM,EAAE,CAAC;gCAC9B,YAAY,CAAC,KAAK,CAAC,CAAC;gCACpB,GAAG,CAAC,OAAO,EAAE,CAAC;gCACd,OAAO,CAAC,MAAM,CAAC,CAAC;4BACpB,CAAC;wBACL,CAAC;wBAAC,MAAM,CAAC;4BACL,wCAAwC;wBAC5C,CAAC;oBACL,CAAC;oBACD,4CAA4C;oBAC5C,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;wBACd,gBAAgB,GAAG,EAAE,CAAC;oBAC1B,CAAC;gBACL,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACpB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,4DAA4D;gBAC5D,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,CAAC;oBACxC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACpB,CAAC;qBAAM,CAAC;oBACJ,MAAM,CAAC,GAAG,CAAC,CAAC;gBAChB,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACf,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,MAAM,CAAC,CAAC;YACpB,CAAC,CAAC,CAAC;QACP,CAAC,CACJ,CAAC;QAEF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACpB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,kDAAkD;YAClD,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,CAAC;gBACxC,OAAO,CAAC,MAAM,CAAC,CAAC;YACpB,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,GAAG,CAAC,CAAC;YAChB,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC;AAED,kCAAkC;AAClC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ScriptedProvider — deterministic LLM provider for integration tests.
3
+ *
4
+ * Implements the real LLMProvider interface.
5
+ * Returns programmable responses per chat() call in sequence.
6
+ * After all scripted turns are exhausted, defaults to a "completed" response.
7
+ *
8
+ * Supported turn types:
9
+ * - "completed" → returns stopReason "completed" with given content
10
+ * - "tool_calls" → returns stopReason "tool_calls" with given tool calls
11
+ * - "hitl" → returns a single request_human_input tool call
12
+ *
13
+ * Supports a beforeCall hook so tool stubs can coordinate with Latches.
14
+ */
15
+ import type { LLMProvider, ProviderChatResponse } from "../../providers/types.js";
16
+ export interface ToolCallSpec {
17
+ server: string;
18
+ tool: string;
19
+ arguments: unknown;
20
+ /** Override the generated call ID */
21
+ id?: string;
22
+ }
23
+ export type ScriptedTurn = {
24
+ type: "completed";
25
+ content: string;
26
+ } | {
27
+ type: "tool_calls";
28
+ toolCalls: ToolCallSpec[];
29
+ } | {
30
+ type: "hitl";
31
+ prompt: string;
32
+ };
33
+ export declare class ScriptedProvider implements LLMProvider {
34
+ private readonly turns;
35
+ private callIndex;
36
+ /**
37
+ * Optional hook invoked at the START of each chat() call (before returning).
38
+ * Receives the call index (0-based) so tests can key on specific turns.
39
+ */
40
+ beforeCall?: (callIndex: number) => Promise<void>;
41
+ constructor(turns: ScriptedTurn[]);
42
+ chat(): Promise<ProviderChatResponse>;
43
+ }
44
+ //# sourceMappingURL=scripted-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scripted-provider.d.ts","sourceRoot":"","sources":["../../../../../../../packages/ai/agent-mcp/src/__tests__/integration/scripted-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAKlF,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,EAAE,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,YAAY,GAClB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,YAAY,EAAE,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC,qBAAa,gBAAiB,YAAW,WAAW;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,SAAS,CAAK;IAEtB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;gBAEtC,KAAK,EAAE,YAAY,EAAE;IAI3B,IAAI,IAAI,OAAO,CAAC,oBAAoB,CAAC;CA6D9C"}