@witqq/agent-sdk 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/{agent-DxY68NZL.d.cts → agent-C6H2CgJA.d.cts} +2 -0
  2. package/dist/{agent-CW9XbmG_.d.ts → agent-F7oB6eKp.d.ts} +2 -0
  3. package/dist/backends/claude.cjs.map +1 -1
  4. package/dist/backends/claude.d.cts +2 -2
  5. package/dist/backends/claude.d.ts +2 -2
  6. package/dist/backends/claude.js.map +1 -1
  7. package/dist/backends/copilot.cjs +8 -15
  8. package/dist/backends/copilot.cjs.map +1 -1
  9. package/dist/backends/copilot.d.cts +2 -2
  10. package/dist/backends/copilot.d.ts +2 -2
  11. package/dist/backends/copilot.js +8 -15
  12. package/dist/backends/copilot.js.map +1 -1
  13. package/dist/backends/mock-llm.cjs +719 -0
  14. package/dist/backends/mock-llm.cjs.map +1 -0
  15. package/dist/backends/mock-llm.d.cts +37 -0
  16. package/dist/backends/mock-llm.d.ts +37 -0
  17. package/dist/backends/mock-llm.js +717 -0
  18. package/dist/backends/mock-llm.js.map +1 -0
  19. package/dist/backends/vercel-ai.cjs +8 -1
  20. package/dist/backends/vercel-ai.cjs.map +1 -1
  21. package/dist/backends/vercel-ai.d.cts +2 -2
  22. package/dist/backends/vercel-ai.d.ts +2 -2
  23. package/dist/backends/vercel-ai.js +8 -1
  24. package/dist/backends/vercel-ai.js.map +1 -1
  25. package/dist/backends-Cno0gZjy.d.cts +114 -0
  26. package/dist/backends-Cno0gZjy.d.ts +114 -0
  27. package/dist/chat/accumulator.cjs.map +1 -1
  28. package/dist/chat/accumulator.d.cts +2 -2
  29. package/dist/chat/accumulator.d.ts +2 -2
  30. package/dist/chat/accumulator.js.map +1 -1
  31. package/dist/chat/backends.cjs +350 -77
  32. package/dist/chat/backends.cjs.map +1 -1
  33. package/dist/chat/backends.d.cts +7 -7
  34. package/dist/chat/backends.d.ts +7 -7
  35. package/dist/chat/backends.js +349 -78
  36. package/dist/chat/backends.js.map +1 -1
  37. package/dist/chat/context.d.cts +2 -2
  38. package/dist/chat/context.d.ts +2 -2
  39. package/dist/chat/core.cjs +35 -25
  40. package/dist/chat/core.cjs.map +1 -1
  41. package/dist/chat/core.d.cts +15 -5
  42. package/dist/chat/core.d.ts +15 -5
  43. package/dist/chat/core.js +35 -26
  44. package/dist/chat/core.js.map +1 -1
  45. package/dist/chat/events.d.cts +2 -2
  46. package/dist/chat/events.d.ts +2 -2
  47. package/dist/chat/index.cjs +418 -122
  48. package/dist/chat/index.cjs.map +1 -1
  49. package/dist/chat/index.d.cts +7 -7
  50. package/dist/chat/index.d.ts +7 -7
  51. package/dist/chat/index.js +418 -124
  52. package/dist/chat/index.js.map +1 -1
  53. package/dist/chat/react.cjs +216 -12
  54. package/dist/chat/react.cjs.map +1 -1
  55. package/dist/chat/react.d.cts +78 -4
  56. package/dist/chat/react.d.ts +78 -4
  57. package/dist/chat/react.js +215 -13
  58. package/dist/chat/react.js.map +1 -1
  59. package/dist/chat/runtime.cjs +6 -2
  60. package/dist/chat/runtime.cjs.map +1 -1
  61. package/dist/chat/runtime.d.cts +2 -2
  62. package/dist/chat/runtime.d.ts +2 -2
  63. package/dist/chat/runtime.js +6 -2
  64. package/dist/chat/runtime.js.map +1 -1
  65. package/dist/chat/server.cjs +15 -5
  66. package/dist/chat/server.cjs.map +1 -1
  67. package/dist/chat/server.d.cts +3 -3
  68. package/dist/chat/server.d.ts +3 -3
  69. package/dist/chat/server.js +15 -5
  70. package/dist/chat/server.js.map +1 -1
  71. package/dist/chat/sessions.cjs +39 -23
  72. package/dist/chat/sessions.cjs.map +1 -1
  73. package/dist/chat/sessions.d.cts +2 -2
  74. package/dist/chat/sessions.d.ts +2 -2
  75. package/dist/chat/sessions.js +40 -24
  76. package/dist/chat/sessions.js.map +1 -1
  77. package/dist/chat/sqlite.cjs +95 -0
  78. package/dist/chat/sqlite.cjs.map +1 -1
  79. package/dist/chat/sqlite.d.cts +39 -3
  80. package/dist/chat/sqlite.d.ts +39 -3
  81. package/dist/chat/sqlite.js +93 -1
  82. package/dist/chat/sqlite.js.map +1 -1
  83. package/dist/chat/state.d.cts +2 -2
  84. package/dist/chat/state.d.ts +2 -2
  85. package/dist/chat/storage.cjs +39 -23
  86. package/dist/chat/storage.cjs.map +1 -1
  87. package/dist/chat/storage.d.cts +7 -3
  88. package/dist/chat/storage.d.ts +7 -3
  89. package/dist/chat/storage.js +40 -24
  90. package/dist/chat/storage.js.map +1 -1
  91. package/dist/{in-process-transport-C1JnJGVR.d.ts → in-process-transport-7EIit9Xk.d.ts} +51 -17
  92. package/dist/{in-process-transport-C7DSqPyX.d.cts → in-process-transport-Ct9YcX8I.d.cts} +51 -17
  93. package/dist/index.cjs +14 -14
  94. package/dist/index.cjs.map +1 -1
  95. package/dist/index.d.cts +4 -2
  96. package/dist/index.d.ts +4 -2
  97. package/dist/index.js +13 -13
  98. package/dist/index.js.map +1 -1
  99. package/dist/testing.cjs +724 -0
  100. package/dist/testing.cjs.map +1 -1
  101. package/dist/testing.d.cts +14 -2
  102. package/dist/testing.d.ts +14 -2
  103. package/dist/testing.js +724 -0
  104. package/dist/testing.js.map +1 -1
  105. package/dist/{transport-Cdh3M0tS.d.cts → transport-DLWCN18G.d.cts} +1 -1
  106. package/dist/{transport-Ciap4PWK.d.ts → transport-DsuS-GeM.d.ts} +1 -1
  107. package/dist/{types-ajANVzf7.d.ts → types-DgtI1hzh.d.ts} +2 -1
  108. package/dist/{types-DRgd_9R7.d.cts → types-DkSXALKg.d.cts} +2 -1
  109. package/package.json +18 -7
  110. package/LICENSE +0 -21
  111. package/README.md +0 -1054
  112. package/dist/backends-BSrsBYFn.d.cts +0 -39
  113. package/dist/backends-BSrsBYFn.d.ts +0 -39
@@ -0,0 +1,719 @@
1
+ 'use strict';
2
+
3
+ // src/types/errors.ts
4
+ var RECOVERABLE_CODES = /* @__PURE__ */ new Set([
5
+ "TIMEOUT" /* TIMEOUT */,
6
+ "RATE_LIMIT" /* RATE_LIMIT */,
7
+ "NETWORK" /* NETWORK */,
8
+ "TOOL_EXECUTION" /* TOOL_EXECUTION */,
9
+ "MODEL_OVERLOADED" /* MODEL_OVERLOADED */,
10
+ "PROVIDER_ERROR" /* PROVIDER_ERROR */
11
+ ]);
12
+ function isRecoverableErrorCode(code) {
13
+ return RECOVERABLE_CODES.has(code);
14
+ }
15
+
16
+ // src/errors.ts
17
+ var AgentSDKError = class extends Error {
18
+ /** @internal Marker for cross-bundle identity checks */
19
+ _agentSDKError = true;
20
+ /** Machine-readable error code. Prefer values from the ErrorCode enum. */
21
+ code;
22
+ /** Whether this error is safe to retry */
23
+ retryable;
24
+ /** HTTP status code hint for error classification */
25
+ httpStatus;
26
+ constructor(message, options) {
27
+ super(message, options);
28
+ this.name = "AgentSDKError";
29
+ this.code = options?.code;
30
+ this.retryable = options?.retryable ?? false;
31
+ this.httpStatus = options?.httpStatus;
32
+ }
33
+ /** Check if an error is an AgentSDKError (works across bundled copies) */
34
+ static is(error) {
35
+ return error instanceof Error && "_agentSDKError" in error && error._agentSDKError === true;
36
+ }
37
+ };
38
+ var ReentrancyError = class extends AgentSDKError {
39
+ constructor() {
40
+ super("Agent is already running. Await the current run before starting another.", {
41
+ code: "REENTRANCY" /* REENTRANCY */
42
+ });
43
+ this.name = "ReentrancyError";
44
+ }
45
+ };
46
+ var DisposedError = class extends AgentSDKError {
47
+ constructor(entity) {
48
+ super(`${entity} has been disposed and cannot be used.`, {
49
+ code: "DISPOSED" /* DISPOSED */
50
+ });
51
+ this.name = "DisposedError";
52
+ }
53
+ };
54
+ var AbortError = class extends AgentSDKError {
55
+ constructor() {
56
+ super("Agent run was aborted.", { code: "ABORTED" /* ABORTED */ });
57
+ this.name = "AbortError";
58
+ }
59
+ };
60
+ var ActivityTimeoutError = class extends AgentSDKError {
61
+ constructor(timeoutMs) {
62
+ super(`Stream activity timeout: no event received within ${timeoutMs}ms.`, {
63
+ code: "TIMEOUT" /* TIMEOUT */,
64
+ retryable: true
65
+ });
66
+ this.name = "ActivityTimeoutError";
67
+ }
68
+ };
69
+
70
+ // src/base-agent.ts
71
+ var BaseAgent = class {
72
+ state = "idle";
73
+ abortController = null;
74
+ config;
75
+ _cleanupExternalSignal = null;
76
+ _streamMiddleware = [];
77
+ /** CLI session ID for persistent mode. Override in backends that support it. */
78
+ get sessionId() {
79
+ return void 0;
80
+ }
81
+ constructor(config) {
82
+ this.config = Object.freeze({ ...config });
83
+ }
84
+ // ─── Public Interface ─────────────────────────────────────────
85
+ async run(prompt, options) {
86
+ this.guardReentrancy();
87
+ this.guardDisposed();
88
+ const ac = this.createAbortController(options?.signal);
89
+ this.state = "running";
90
+ try {
91
+ const messages = [{ role: "user", content: prompt }];
92
+ const result = await this.withRetry(
93
+ () => this.executeRun(messages, options, ac.signal),
94
+ options
95
+ );
96
+ this.enrichAndNotifyUsage(result, options);
97
+ return result;
98
+ } finally {
99
+ this.cleanupRun();
100
+ }
101
+ }
102
+ async runWithContext(messages, options) {
103
+ this.guardReentrancy();
104
+ this.guardDisposed();
105
+ const ac = this.createAbortController(options?.signal);
106
+ this.state = "running";
107
+ try {
108
+ const result = await this.withRetry(
109
+ () => this.executeRun(messages, options, ac.signal),
110
+ options
111
+ );
112
+ this.enrichAndNotifyUsage(result, options);
113
+ return result;
114
+ } finally {
115
+ this.cleanupRun();
116
+ }
117
+ }
118
+ async runStructured(prompt, schema, options) {
119
+ this.guardReentrancy();
120
+ this.guardDisposed();
121
+ const ac = this.createAbortController(options?.signal);
122
+ this.state = "running";
123
+ try {
124
+ const messages = [{ role: "user", content: prompt }];
125
+ const result = await this.withRetry(
126
+ () => this.executeRunStructured(messages, schema, options, ac.signal),
127
+ options
128
+ );
129
+ this.enrichAndNotifyUsage(result, options);
130
+ return result;
131
+ } finally {
132
+ this.cleanupRun();
133
+ }
134
+ }
135
+ async *stream(prompt, options) {
136
+ this.guardReentrancy();
137
+ this.guardDisposed();
138
+ const ac = this.createAbortController(options?.signal);
139
+ this.state = "streaming";
140
+ try {
141
+ const messages = [{ role: "user", content: prompt }];
142
+ yield* this.streamWithRetry(
143
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
144
+ options
145
+ );
146
+ } finally {
147
+ this.cleanupRun();
148
+ }
149
+ }
150
+ async *streamWithContext(messages, options) {
151
+ this.guardReentrancy();
152
+ this.guardDisposed();
153
+ const ac = this.createAbortController(options?.signal);
154
+ this.state = "streaming";
155
+ try {
156
+ yield* this.streamWithRetry(
157
+ () => this.applyStreamPipeline(this.executeStream(messages, options, ac.signal), options, ac),
158
+ options
159
+ );
160
+ } finally {
161
+ this.cleanupRun();
162
+ }
163
+ }
164
+ /** Register a stream middleware. Applied in registration order after built-in transforms. */
165
+ addStreamMiddleware(middleware) {
166
+ this.guardDisposed();
167
+ this._streamMiddleware.push(middleware);
168
+ }
169
+ /** Apply built-in transforms (enrich→timeout→heartbeat) then custom middleware */
170
+ async *applyStreamPipeline(source, options, ac) {
171
+ let stream = this.enrichStream(source, options);
172
+ stream = this.activityTimeoutStream(stream, options?.activityTimeoutMs, ac);
173
+ stream = this.heartbeatStream(stream);
174
+ if (this._streamMiddleware.length > 0) {
175
+ const ctx = {
176
+ model: options.model,
177
+ backend: this.backendName,
178
+ abortController: ac,
179
+ config: Object.freeze({ ...this.config })
180
+ };
181
+ for (const mw of this._streamMiddleware) {
182
+ stream = mw(stream, ctx);
183
+ }
184
+ }
185
+ yield* stream;
186
+ }
187
+ abort() {
188
+ if (this.abortController) {
189
+ this.abortController.abort();
190
+ }
191
+ }
192
+ /** Default interrupt — falls back to abort(). Backends may override with graceful shutdown. */
193
+ async interrupt() {
194
+ this.abort();
195
+ }
196
+ getState() {
197
+ return this.state;
198
+ }
199
+ getConfig() {
200
+ return this.config;
201
+ }
202
+ /** Mark agent as disposed. Override to add cleanup. */
203
+ dispose() {
204
+ this._cleanupExternalSignal?.();
205
+ this._cleanupExternalSignal = null;
206
+ this.abort();
207
+ this.state = "disposed";
208
+ }
209
+ // ─── Retry Logic ─────────────────────────────────────────────
210
+ /** Check if an error should be retried given the retry configuration. */
211
+ isRetryableError(error, retry) {
212
+ if (error instanceof AbortError || error instanceof ReentrancyError || error instanceof DisposedError) {
213
+ return false;
214
+ }
215
+ if (AgentSDKError.is(error)) {
216
+ if (retry.retryableErrors && retry.retryableErrors.length > 0 && error.code) {
217
+ return retry.retryableErrors.includes(error.code);
218
+ }
219
+ if (error.retryable) return true;
220
+ if (error.code) return isRecoverableErrorCode(error.code);
221
+ }
222
+ return false;
223
+ }
224
+ /** Execute a function with retry logic per RetryConfig. */
225
+ async withRetry(fn, options) {
226
+ const retry = options?.retry;
227
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
228
+ return fn();
229
+ }
230
+ const maxRetries = retry.maxRetries;
231
+ const initialDelay = retry.initialDelayMs ?? 1e3;
232
+ const multiplier = retry.backoffMultiplier ?? 2;
233
+ let lastError;
234
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
235
+ try {
236
+ return await fn();
237
+ } catch (err) {
238
+ lastError = err;
239
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
240
+ throw err;
241
+ }
242
+ const delay = initialDelay * Math.pow(multiplier, attempt);
243
+ await new Promise((resolve) => setTimeout(resolve, delay));
244
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
245
+ throw err;
246
+ }
247
+ }
248
+ }
249
+ throw lastError;
250
+ }
251
+ /** Execute a stream factory with pre-stream retry: retries until first event, then committed. */
252
+ async *streamWithRetry(factory, options) {
253
+ const retry = options?.retry;
254
+ if (!retry || !retry.maxRetries || retry.maxRetries <= 0) {
255
+ yield* factory();
256
+ return;
257
+ }
258
+ const maxRetries = retry.maxRetries;
259
+ const initialDelay = retry.initialDelayMs ?? 1e3;
260
+ const multiplier = retry.backoffMultiplier ?? 2;
261
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
262
+ try {
263
+ const stream = factory();
264
+ const iterator = stream[Symbol.asyncIterator]();
265
+ const first = await iterator.next();
266
+ if (first.done) return;
267
+ yield first.value;
268
+ while (true) {
269
+ const next = await iterator.next();
270
+ if (next.done) break;
271
+ yield next.value;
272
+ }
273
+ return;
274
+ } catch (err) {
275
+ if (attempt >= maxRetries || !this.isRetryableError(err, retry)) {
276
+ throw err;
277
+ }
278
+ const delay = initialDelay * Math.pow(multiplier, attempt);
279
+ await new Promise((resolve) => setTimeout(resolve, delay));
280
+ if (options?.signal?.aborted || this.abortController?.signal.aborted) {
281
+ throw err;
282
+ }
283
+ }
284
+ }
285
+ }
286
+ // ─── CallOptions Resolution ──────────────────────────────────
287
+ /** Resolve tools to use for this call (per-call override > config default) */
288
+ resolveTools(options) {
289
+ return options?.tools ?? this.config.tools ?? [];
290
+ }
291
+ // ─── Usage Enrichment ───────────────────────────────────────────
292
+ /** Enrich result usage with model/backend and fire onUsage callback */
293
+ enrichAndNotifyUsage(result, options) {
294
+ if (result.usage) {
295
+ result.usage = {
296
+ ...result.usage,
297
+ model: options.model,
298
+ backend: this.backendName
299
+ };
300
+ this.callOnUsage(result.usage);
301
+ }
302
+ }
303
+ /** Wrap a stream to enrich usage_update events and fire onUsage callback */
304
+ async *enrichStream(source, options) {
305
+ const model = options.model;
306
+ for await (const event of source) {
307
+ if (event.type === "usage_update") {
308
+ const usage = {
309
+ promptTokens: event.promptTokens,
310
+ completionTokens: event.completionTokens,
311
+ model,
312
+ backend: this.backendName
313
+ };
314
+ this.callOnUsage(usage);
315
+ yield { type: "usage_update", ...usage };
316
+ } else {
317
+ yield event;
318
+ }
319
+ }
320
+ }
321
+ /** Fire onUsage callback (fire-and-forget: errors logged, not propagated) */
322
+ callOnUsage(usage) {
323
+ if (!this.config.onUsage) return;
324
+ try {
325
+ this.config.onUsage(usage);
326
+ } catch (e) {
327
+ console.warn(
328
+ "[agent-sdk] onUsage callback error:",
329
+ e instanceof Error ? e.message : String(e)
330
+ );
331
+ }
332
+ }
333
+ // ─── Heartbeat ───────────────────────────────────────────────
334
+ /** Wrap a stream to emit heartbeat events at configured intervals.
335
+ * When heartbeatInterval is not set, passes through directly. */
336
+ async *heartbeatStream(source) {
337
+ const interval = this.config.heartbeatInterval;
338
+ if (!interval || interval <= 0) {
339
+ yield* source;
340
+ return;
341
+ }
342
+ const iterator = source[Symbol.asyncIterator]();
343
+ let pendingEvent = null;
344
+ let heartbeatResolve = null;
345
+ const timer = setInterval(() => {
346
+ if (heartbeatResolve) {
347
+ const resolve = heartbeatResolve;
348
+ heartbeatResolve = null;
349
+ resolve();
350
+ }
351
+ }, interval);
352
+ try {
353
+ while (true) {
354
+ if (!pendingEvent) {
355
+ pendingEvent = iterator.next();
356
+ }
357
+ const heartbeatPromise = new Promise((resolve) => {
358
+ heartbeatResolve = resolve;
359
+ });
360
+ const eventDone = pendingEvent.then(
361
+ (r) => ({ kind: "event", result: r })
362
+ );
363
+ const heartbeatDone = heartbeatPromise.then(
364
+ () => ({ kind: "heartbeat" })
365
+ );
366
+ const winner = await Promise.race([eventDone, heartbeatDone]);
367
+ if (winner.kind === "heartbeat") {
368
+ yield { type: "heartbeat" };
369
+ } else {
370
+ pendingEvent = null;
371
+ heartbeatResolve = null;
372
+ if (winner.result.done) break;
373
+ yield winner.result.value;
374
+ }
375
+ }
376
+ } finally {
377
+ clearInterval(timer);
378
+ heartbeatResolve = null;
379
+ }
380
+ }
381
+ // ─── Activity Timeout ────────────────────────────────────────
382
+ /** Wrap a stream to abort on inactivity. Resets timer on every event.
383
+ * When timeoutMs is not set, passes through directly. */
384
+ async *activityTimeoutStream(source, timeoutMs, ac) {
385
+ if (!timeoutMs || timeoutMs <= 0) {
386
+ yield* source;
387
+ return;
388
+ }
389
+ const iterator = source[Symbol.asyncIterator]();
390
+ let timerId;
391
+ try {
392
+ while (true) {
393
+ const timeoutPromise = new Promise((_, reject) => {
394
+ timerId = setTimeout(() => reject(new ActivityTimeoutError(timeoutMs)), timeoutMs);
395
+ });
396
+ const result = await Promise.race([iterator.next(), timeoutPromise]);
397
+ clearTimeout(timerId);
398
+ if (result.done) break;
399
+ yield result.value;
400
+ }
401
+ } catch (err) {
402
+ if (err instanceof ActivityTimeoutError) {
403
+ ac.abort(err);
404
+ }
405
+ throw err;
406
+ } finally {
407
+ clearTimeout(timerId);
408
+ }
409
+ }
410
+ // ─── Guards ───────────────────────────────────────────────────
411
+ guardReentrancy() {
412
+ if (this.state === "running" || this.state === "streaming") {
413
+ throw new ReentrancyError();
414
+ }
415
+ }
416
+ guardDisposed() {
417
+ if (this.state === "disposed") {
418
+ throw new DisposedError("Agent");
419
+ }
420
+ }
421
+ /** Throw AbortError if signal is already aborted */
422
+ checkAbort(signal) {
423
+ if (signal.aborted) {
424
+ throw new AbortError();
425
+ }
426
+ }
427
+ // ─── Internal Helpers ─────────────────────────────────────────
428
+ /** Clean up after a run completes (success, error, or abort). */
429
+ cleanupRun() {
430
+ this._cleanupExternalSignal?.();
431
+ this._cleanupExternalSignal = null;
432
+ this.state = "idle";
433
+ this.abortController = null;
434
+ }
435
+ createAbortController(externalSignal) {
436
+ const ac = new AbortController();
437
+ this.abortController = ac;
438
+ this._cleanupExternalSignal = null;
439
+ if (externalSignal) {
440
+ if (externalSignal.aborted) {
441
+ ac.abort();
442
+ } else {
443
+ const listener = () => ac.abort();
444
+ externalSignal.addEventListener("abort", listener, { once: true });
445
+ this._cleanupExternalSignal = () => externalSignal.removeEventListener("abort", listener);
446
+ }
447
+ }
448
+ return ac;
449
+ }
450
+ };
451
+
452
+ // src/backends/mock-llm.ts
453
+ function extractPrompt(messages) {
454
+ for (let i = messages.length - 1; i >= 0; i--) {
455
+ const msg = messages[i];
456
+ if (msg.role === "user") {
457
+ return typeof msg.content === "string" ? msg.content : msg.content.filter((p) => p.type === "text").map((p) => p.text).join("");
458
+ }
459
+ }
460
+ return "";
461
+ }
462
+ function resolveResponse(mode, messages, callIndex) {
463
+ switch (mode.type) {
464
+ case "echo":
465
+ return extractPrompt(messages);
466
+ case "static":
467
+ return mode.response;
468
+ case "scripted": {
469
+ if (mode.loop) {
470
+ return mode.responses[callIndex % mode.responses.length];
471
+ }
472
+ if (callIndex < mode.responses.length) {
473
+ return mode.responses[callIndex];
474
+ }
475
+ return mode.responses[mode.responses.length - 1];
476
+ }
477
+ case "error":
478
+ throw new AgentSDKError(mode.error, {
479
+ code: mode.code ?? "backend_error",
480
+ retryable: mode.recoverable ?? false
481
+ });
482
+ }
483
+ }
484
+ async function applyLatency(latency, signal) {
485
+ if (!latency) return;
486
+ const ms = latency.type === "fixed" ? latency.ms : latency.minMs + Math.random() * (latency.maxMs - latency.minMs);
487
+ if (ms <= 0) return;
488
+ await new Promise((resolve, reject) => {
489
+ const timer = setTimeout(resolve, ms);
490
+ const onAbort = () => {
491
+ clearTimeout(timer);
492
+ reject(new Error("aborted"));
493
+ };
494
+ if (signal.aborted) {
495
+ clearTimeout(timer);
496
+ reject(new Error("aborted"));
497
+ return;
498
+ }
499
+ signal.addEventListener("abort", onAbort, { once: true });
500
+ });
501
+ }
502
+ function chunkText(text, streaming) {
503
+ if (streaming?.chunkSize && streaming.chunkSize > 0) {
504
+ const chunks = [];
505
+ for (let i = 0; i < text.length; i += streaming.chunkSize) {
506
+ chunks.push(text.slice(i, i + streaming.chunkSize));
507
+ }
508
+ return chunks;
509
+ }
510
+ return text.split(/(\s+)/).filter(Boolean);
511
+ }
512
+ async function chunkDelay(streaming, signal) {
513
+ const ms = streaming?.chunkDelayMs;
514
+ if (!ms || ms <= 0) return;
515
+ await new Promise((resolve, reject) => {
516
+ const timer = setTimeout(resolve, ms);
517
+ const onAbort = () => {
518
+ clearTimeout(timer);
519
+ reject(new Error("aborted"));
520
+ };
521
+ if (signal.aborted) {
522
+ clearTimeout(timer);
523
+ reject(new Error("aborted"));
524
+ return;
525
+ }
526
+ signal.addEventListener("abort", onAbort, { once: true });
527
+ });
528
+ }
529
+ var MockLLMAgent = class extends BaseAgent {
530
+ backendName = "mock-llm";
531
+ mode;
532
+ latency;
533
+ streaming;
534
+ finishReason;
535
+ permissions;
536
+ toolCallConfigs;
537
+ configuredStructuredOutput;
538
+ callIndex = 0;
539
+ constructor(config, options) {
540
+ super(config);
541
+ this.mode = options.mode ?? { type: "echo" };
542
+ this.latency = options.latency;
543
+ this.streaming = options.streaming;
544
+ this.finishReason = options.finishReason ?? "stop";
545
+ this.permissions = options.permissions;
546
+ this.toolCallConfigs = options.toolCalls ?? [];
547
+ this.configuredStructuredOutput = options.structuredOutput;
548
+ }
549
+ async executeRun(messages, _options, signal) {
550
+ this.checkAbort(signal);
551
+ await applyLatency(this.latency, signal);
552
+ this.checkAbort(signal);
553
+ const idx = this.callIndex++;
554
+ const output = resolveResponse(this.mode, messages, idx);
555
+ const toolCalls = this.toolCallConfigs.map((tc) => ({
556
+ toolName: tc.toolName,
557
+ args: tc.args ?? {},
558
+ result: tc.result ?? null,
559
+ approved: true
560
+ }));
561
+ return {
562
+ output,
563
+ structuredOutput: void 0,
564
+ toolCalls,
565
+ messages: [
566
+ ...messages,
567
+ { role: "assistant", content: output }
568
+ ],
569
+ usage: { promptTokens: 10, completionTokens: output.length }
570
+ };
571
+ }
572
+ async executeRunStructured(messages, _schema, _options, signal) {
573
+ this.checkAbort(signal);
574
+ await applyLatency(this.latency, signal);
575
+ this.checkAbort(signal);
576
+ const idx = this.callIndex++;
577
+ const output = resolveResponse(this.mode, messages, idx);
578
+ let parsed;
579
+ if (this.configuredStructuredOutput !== void 0) {
580
+ parsed = this.configuredStructuredOutput;
581
+ } else {
582
+ try {
583
+ parsed = JSON.parse(output);
584
+ } catch {
585
+ parsed = output;
586
+ }
587
+ }
588
+ return {
589
+ output,
590
+ structuredOutput: parsed,
591
+ toolCalls: [],
592
+ messages: [
593
+ ...messages,
594
+ { role: "assistant", content: output }
595
+ ],
596
+ usage: { promptTokens: 10, completionTokens: output.length }
597
+ };
598
+ }
599
+ async *executeStream(messages, _options, signal) {
600
+ this.checkAbort(signal);
601
+ await applyLatency(this.latency, signal);
602
+ this.checkAbort(signal);
603
+ if (this.permissions) {
604
+ yield* this.simulatePermissions(signal);
605
+ }
606
+ if (this.toolCallConfigs.length > 0) {
607
+ yield* this.simulateToolCalls(signal);
608
+ }
609
+ const idx = this.callIndex++;
610
+ const output = resolveResponse(this.mode, messages, idx);
611
+ const chunks = chunkText(output, this.streaming);
612
+ for (let i = 0; i < chunks.length; i++) {
613
+ this.checkAbort(signal);
614
+ if (i > 0) {
615
+ await chunkDelay(this.streaming, signal);
616
+ }
617
+ yield { type: "text_delta", text: chunks[i] };
618
+ }
619
+ yield {
620
+ type: "usage_update",
621
+ promptTokens: 10,
622
+ completionTokens: output.length
623
+ };
624
+ yield {
625
+ type: "done",
626
+ finalOutput: output,
627
+ finishReason: this.finishReason
628
+ };
629
+ }
630
+ async *simulateToolCalls(signal) {
631
+ for (let i = 0; i < this.toolCallConfigs.length; i++) {
632
+ this.checkAbort(signal);
633
+ const tc = this.toolCallConfigs[i];
634
+ const toolCallId = tc.toolCallId ?? `mock-tc-${i}`;
635
+ yield {
636
+ type: "tool_call_start",
637
+ toolCallId,
638
+ toolName: tc.toolName,
639
+ args: tc.args ?? {}
640
+ };
641
+ yield {
642
+ type: "tool_call_end",
643
+ toolCallId,
644
+ toolName: tc.toolName,
645
+ result: tc.result ?? null
646
+ };
647
+ }
648
+ }
649
+ async *simulatePermissions(signal) {
650
+ const perms = this.permissions;
651
+ for (const toolName of perms.toolNames) {
652
+ this.checkAbort(signal);
653
+ const request = {
654
+ toolName,
655
+ toolArgs: {}
656
+ };
657
+ yield { type: "permission_request", request };
658
+ if (perms.denyTools?.includes(toolName)) {
659
+ yield {
660
+ type: "permission_response",
661
+ toolName,
662
+ decision: { allowed: false, reason: "Denied by mock configuration" }
663
+ };
664
+ } else if (perms.autoApprove) {
665
+ yield {
666
+ type: "permission_response",
667
+ toolName,
668
+ decision: { allowed: true, scope: "once" }
669
+ };
670
+ } else {
671
+ const supervisor = this.getConfig().supervisor;
672
+ if (supervisor?.onPermission) {
673
+ const decision = await supervisor.onPermission(request, signal);
674
+ yield { type: "permission_response", toolName, decision };
675
+ } else {
676
+ yield {
677
+ type: "permission_response",
678
+ toolName,
679
+ decision: { allowed: true, scope: "once" }
680
+ };
681
+ }
682
+ }
683
+ }
684
+ }
685
+ };
686
+ var MockLLMService = class {
687
+ name = "mock-llm";
688
+ options;
689
+ models;
690
+ constructor(options = {}) {
691
+ this.options = options;
692
+ this.models = (options.models ?? [
693
+ { id: "mock-fast", name: "Mock Fast" },
694
+ { id: "mock-quality", name: "Mock Quality" }
695
+ ]).map((m) => ({
696
+ id: m.id,
697
+ name: m.name,
698
+ description: m.description
699
+ }));
700
+ }
701
+ createAgent(config) {
702
+ return new MockLLMAgent(config, this.options);
703
+ }
704
+ async listModels() {
705
+ return this.models;
706
+ }
707
+ async validate() {
708
+ return { valid: true, errors: [] };
709
+ }
710
+ async dispose() {
711
+ }
712
+ };
713
+ function createMockLLMService(options = {}) {
714
+ return new MockLLMService(options);
715
+ }
716
+
717
+ exports.createMockLLMService = createMockLLMService;
718
+ //# sourceMappingURL=mock-llm.cjs.map
719
+ //# sourceMappingURL=mock-llm.cjs.map