@dogpile/sdk 0.1.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 (88) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/LICENSE +16 -0
  3. package/README.md +842 -0
  4. package/dist/browser/index.d.ts +8 -0
  5. package/dist/browser/index.d.ts.map +1 -0
  6. package/dist/browser/index.js +4493 -0
  7. package/dist/browser/index.js.map +1 -0
  8. package/dist/index.d.ts +17 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +14 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/providers/openai-compatible.d.ts +44 -0
  13. package/dist/providers/openai-compatible.d.ts.map +1 -0
  14. package/dist/providers/openai-compatible.js +305 -0
  15. package/dist/providers/openai-compatible.js.map +1 -0
  16. package/dist/runtime/broadcast.d.ts +18 -0
  17. package/dist/runtime/broadcast.d.ts.map +1 -0
  18. package/dist/runtime/broadcast.js +335 -0
  19. package/dist/runtime/broadcast.js.map +1 -0
  20. package/dist/runtime/cancellation.d.ts +6 -0
  21. package/dist/runtime/cancellation.d.ts.map +1 -0
  22. package/dist/runtime/cancellation.js +35 -0
  23. package/dist/runtime/cancellation.js.map +1 -0
  24. package/dist/runtime/coordinator.d.ts +18 -0
  25. package/dist/runtime/coordinator.d.ts.map +1 -0
  26. package/dist/runtime/coordinator.js +434 -0
  27. package/dist/runtime/coordinator.js.map +1 -0
  28. package/dist/runtime/decisions.d.ts +5 -0
  29. package/dist/runtime/decisions.d.ts.map +1 -0
  30. package/dist/runtime/decisions.js +31 -0
  31. package/dist/runtime/decisions.js.map +1 -0
  32. package/dist/runtime/defaults.d.ts +63 -0
  33. package/dist/runtime/defaults.d.ts.map +1 -0
  34. package/dist/runtime/defaults.js +426 -0
  35. package/dist/runtime/defaults.js.map +1 -0
  36. package/dist/runtime/engine.d.ts +79 -0
  37. package/dist/runtime/engine.d.ts.map +1 -0
  38. package/dist/runtime/engine.js +723 -0
  39. package/dist/runtime/engine.js.map +1 -0
  40. package/dist/runtime/model.d.ts +14 -0
  41. package/dist/runtime/model.d.ts.map +1 -0
  42. package/dist/runtime/model.js +82 -0
  43. package/dist/runtime/model.js.map +1 -0
  44. package/dist/runtime/sequential.d.ts +18 -0
  45. package/dist/runtime/sequential.d.ts.map +1 -0
  46. package/dist/runtime/sequential.js +277 -0
  47. package/dist/runtime/sequential.js.map +1 -0
  48. package/dist/runtime/shared.d.ts +18 -0
  49. package/dist/runtime/shared.d.ts.map +1 -0
  50. package/dist/runtime/shared.js +288 -0
  51. package/dist/runtime/shared.js.map +1 -0
  52. package/dist/runtime/termination.d.ts +77 -0
  53. package/dist/runtime/termination.d.ts.map +1 -0
  54. package/dist/runtime/termination.js +355 -0
  55. package/dist/runtime/termination.js.map +1 -0
  56. package/dist/runtime/tools.d.ts +314 -0
  57. package/dist/runtime/tools.d.ts.map +1 -0
  58. package/dist/runtime/tools.js +969 -0
  59. package/dist/runtime/tools.js.map +1 -0
  60. package/dist/runtime/validation.d.ts +23 -0
  61. package/dist/runtime/validation.d.ts.map +1 -0
  62. package/dist/runtime/validation.js +656 -0
  63. package/dist/runtime/validation.js.map +1 -0
  64. package/dist/types.d.ts +2434 -0
  65. package/dist/types.d.ts.map +1 -0
  66. package/dist/types.js +81 -0
  67. package/dist/types.js.map +1 -0
  68. package/package.json +157 -0
  69. package/src/browser/index.ts +7 -0
  70. package/src/index.ts +195 -0
  71. package/src/providers/openai-compatible.ts +406 -0
  72. package/src/runtime/broadcast.test.ts +355 -0
  73. package/src/runtime/broadcast.ts +428 -0
  74. package/src/runtime/cancellation.ts +40 -0
  75. package/src/runtime/coordinator.test.ts +468 -0
  76. package/src/runtime/coordinator.ts +581 -0
  77. package/src/runtime/decisions.ts +38 -0
  78. package/src/runtime/defaults.ts +547 -0
  79. package/src/runtime/engine.ts +880 -0
  80. package/src/runtime/model.ts +117 -0
  81. package/src/runtime/sequential.test.ts +262 -0
  82. package/src/runtime/sequential.ts +357 -0
  83. package/src/runtime/shared.test.ts +265 -0
  84. package/src/runtime/shared.ts +367 -0
  85. package/src/runtime/termination.ts +463 -0
  86. package/src/runtime/tools.ts +1518 -0
  87. package/src/runtime/validation.ts +771 -0
  88. package/src/types.ts +2729 -0
@@ -0,0 +1,723 @@
1
+ import { DogpileError } from "../types.js";
2
+ import { runBroadcast } from "./broadcast.js";
3
+ import { runCoordinator } from "./coordinator.js";
4
+ import { createReplayTraceFinalOutput, createReplayTraceBudgetStateChanges, canonicalizeRunResult, canonicalizeSerializable, createRunAccounting, createRunEventLog, createRunMetadata, createRunUsage, defaultAgents, normalizeProtocol, orderAgentsForTemperature, tierTemperature } from "./defaults.js";
5
+ import { runSequential } from "./sequential.js";
6
+ import { runShared } from "./shared.js";
7
+ import { createAbortErrorFromSignal, createTimeoutError } from "./cancellation.js";
8
+ import { budget as budgetCondition } from "./termination.js";
9
+ import { validateDogpileOptions, validateEngineOptions, validateMissionIntent } from "./validation.js";
10
+ const defaultHighLevelProtocol = "sequential";
11
+ const defaultHighLevelTier = "balanced";
12
+ /**
13
+ * Create a reusable low-level protocol engine.
14
+ *
15
+ * @remarks
16
+ * Use this escape hatch to hold protocol, tier, model, agents, and budget caps
17
+ * constant across repeated missions. Most application code can call
18
+ * {@link run}, {@link stream}, or {@link Dogpile.pile} directly.
19
+ *
20
+ * The returned engine is stateless between calls: each `run()` or `stream()`
21
+ * invocation produces its own serializable trace, event log, and transcript.
22
+ */
23
+ export function createEngine(options) {
24
+ validateEngineOptions(options);
25
+ const protocol = normalizeProtocol(options.protocol);
26
+ const tools = options.tools ?? [];
27
+ const temperature = options.temperature ?? tierTemperature(options.tier);
28
+ const agents = orderAgentsForTemperature(options.agents ?? defaultAgents(), temperature, options.seed);
29
+ const terminate = options.terminate ?? (options.budget ? conditionFromBudget(options.budget) : undefined);
30
+ return {
31
+ run(intent) {
32
+ validateMissionIntent(intent);
33
+ return runNonStreamingProtocol({
34
+ intent,
35
+ protocol,
36
+ tier: options.tier,
37
+ model: options.model,
38
+ agents,
39
+ tools,
40
+ temperature,
41
+ ...(options.budget ? { budget: options.budget } : {}),
42
+ ...(options.seed !== undefined ? { seed: options.seed } : {}),
43
+ ...(options.signal !== undefined ? { signal: options.signal } : {}),
44
+ ...(terminate ? { terminate } : {}),
45
+ ...(options.evaluate ? { evaluate: options.evaluate } : {})
46
+ });
47
+ },
48
+ stream(intent) {
49
+ validateMissionIntent(intent);
50
+ const pendingEvents = [];
51
+ const pendingResolvers = [];
52
+ const emittedEvents = [];
53
+ const subscribers = new Set();
54
+ const abortController = new AbortController();
55
+ const timeoutLifecycle = createTimeoutAbortLifecycle({
56
+ abortController,
57
+ timeoutMs: runtimeTimeoutMs({ budget: options.budget, terminate }),
58
+ providerId: options.model.id
59
+ });
60
+ const abortRace = createAbortRace(abortController.signal, options.model.id);
61
+ let complete = false;
62
+ let lastRunId = "";
63
+ let pendingFinalEvent;
64
+ let status = "running";
65
+ let resolveResult;
66
+ let rejectResult;
67
+ let removeCallerAbortListener = () => { };
68
+ const result = new Promise((resolve, reject) => {
69
+ resolveResult = resolve;
70
+ rejectResult = reject;
71
+ });
72
+ removeCallerAbortListener = wireCallerAbortSignal(options.signal, abortController, cancelRun);
73
+ void execute();
74
+ return {
75
+ get status() {
76
+ return status;
77
+ },
78
+ result,
79
+ cancel() {
80
+ cancelRun();
81
+ },
82
+ subscribe(subscriber) {
83
+ subscribers.add(subscriber);
84
+ for (const event of emittedEvents) {
85
+ subscriber(event);
86
+ }
87
+ return {
88
+ unsubscribe() {
89
+ subscribers.delete(subscriber);
90
+ }
91
+ };
92
+ },
93
+ [Symbol.asyncIterator]() {
94
+ return {
95
+ next() {
96
+ const event = pendingEvents.shift();
97
+ if (event) {
98
+ return Promise.resolve({ done: false, value: event });
99
+ }
100
+ if (complete) {
101
+ return Promise.resolve({ done: true, value: undefined });
102
+ }
103
+ return new Promise((resolve) => {
104
+ pendingResolvers.push(resolve);
105
+ });
106
+ }
107
+ };
108
+ }
109
+ };
110
+ async function execute() {
111
+ if (status !== "running") {
112
+ return;
113
+ }
114
+ try {
115
+ const baseResult = await abortRace.run(runProtocol({
116
+ intent,
117
+ protocol,
118
+ tier: options.tier,
119
+ model: options.model,
120
+ agents,
121
+ tools,
122
+ temperature,
123
+ ...(options.budget ? { budget: options.budget } : {}),
124
+ ...(options.seed !== undefined ? { seed: options.seed } : {}),
125
+ signal: abortController.signal,
126
+ ...(terminate ? { terminate } : {}),
127
+ emit(event) {
128
+ if (status !== "running") {
129
+ return;
130
+ }
131
+ lastRunId = event.runId;
132
+ if (event.type === "final") {
133
+ pendingFinalEvent = event;
134
+ return;
135
+ }
136
+ publish(event);
137
+ }
138
+ }));
139
+ if (status !== "running") {
140
+ return;
141
+ }
142
+ const finalizedResult = await abortRace.run(applyRunEvaluation(baseResult, options.evaluate));
143
+ if (status !== "running") {
144
+ return;
145
+ }
146
+ const finalEvent = finalizedResult.trace.events.at(-1);
147
+ if (finalEvent?.type === "final") {
148
+ publish(finalEvent);
149
+ }
150
+ else if (pendingFinalEvent) {
151
+ publish(pendingFinalEvent);
152
+ }
153
+ status = "completed";
154
+ closeStream();
155
+ resolveResult(finalizedResult);
156
+ }
157
+ catch (error) {
158
+ if (isStreamHandleStatus(status, "cancelled")) {
159
+ return;
160
+ }
161
+ const runtimeError = timeoutLifecycle.translateError(error);
162
+ status = isCancellationError(runtimeError) ? "cancelled" : "failed";
163
+ publish(createStreamErrorEvent(runtimeError, lastRunId));
164
+ closeStream();
165
+ rejectResult(runtimeError);
166
+ }
167
+ }
168
+ function cancelRun(cause) {
169
+ if (status !== "running") {
170
+ return;
171
+ }
172
+ const error = createStreamCancellationError(options.model.id, cause);
173
+ status = "cancelled";
174
+ abortController.abort(error);
175
+ publish(createStreamErrorEvent(error, lastRunId));
176
+ closeStream();
177
+ rejectResult(error);
178
+ }
179
+ function closeStream() {
180
+ if (complete) {
181
+ return;
182
+ }
183
+ complete = true;
184
+ removeCallerAbortListener();
185
+ timeoutLifecycle.cleanup();
186
+ abortRace.cleanup();
187
+ subscribers.clear();
188
+ for (const resolver of pendingResolvers.splice(0)) {
189
+ resolver({ done: true, value: undefined });
190
+ }
191
+ }
192
+ function publish(event) {
193
+ if (complete) {
194
+ return;
195
+ }
196
+ const canonicalEvent = canonicalizeSerializable(event);
197
+ emittedEvents.push(canonicalEvent);
198
+ for (const subscriber of subscribers) {
199
+ try {
200
+ subscriber(canonicalEvent);
201
+ }
202
+ catch {
203
+ // Subscriber failures should not cancel the underlying SDK run.
204
+ }
205
+ }
206
+ const resolver = pendingResolvers.shift();
207
+ if (resolver) {
208
+ resolver({ done: false, value: canonicalEvent });
209
+ return;
210
+ }
211
+ pendingEvents.push(canonicalEvent);
212
+ }
213
+ }
214
+ };
215
+ }
216
+ function isStreamHandleStatus(status, expected) {
217
+ return status === expected;
218
+ }
219
+ function conditionFromBudget(budget) {
220
+ return budgetCondition({
221
+ ...(budget.maxUsd !== undefined ? { maxUsd: budget.maxUsd } : {}),
222
+ ...(budget.maxTokens !== undefined ? { maxTokens: budget.maxTokens } : {}),
223
+ ...(budget.maxIterations !== undefined ? { maxIterations: budget.maxIterations } : {}),
224
+ ...(budget.timeoutMs !== undefined ? { timeoutMs: budget.timeoutMs } : {})
225
+ });
226
+ }
227
+ function createNonStreamingAbortLifecycle(options) {
228
+ if (options.timeoutMs === undefined) {
229
+ return {
230
+ signal: options.callerSignal,
231
+ async run(operation) {
232
+ return await operation;
233
+ },
234
+ translateError(error) {
235
+ return error;
236
+ },
237
+ cleanup() { }
238
+ };
239
+ }
240
+ const abortController = new AbortController();
241
+ const timeoutLifecycle = createTimeoutAbortLifecycle({
242
+ abortController,
243
+ timeoutMs: options.timeoutMs,
244
+ providerId: options.providerId
245
+ });
246
+ const abortRace = createAbortRace(abortController.signal, options.providerId);
247
+ const removeCallerAbortListener = wireCallerAbortSignal(options.callerSignal, abortController, () => {
248
+ abortController.abort(readAbortSignalReason(options.callerSignal));
249
+ });
250
+ return {
251
+ signal: abortController.signal,
252
+ async run(operation) {
253
+ return await abortRace.run(operation);
254
+ },
255
+ translateError(error) {
256
+ return timeoutLifecycle.translateError(error);
257
+ },
258
+ cleanup() {
259
+ timeoutLifecycle.cleanup();
260
+ abortRace.cleanup();
261
+ removeCallerAbortListener();
262
+ }
263
+ };
264
+ }
265
+ function createTimeoutAbortLifecycle(options) {
266
+ if (options.timeoutMs === undefined) {
267
+ return {
268
+ translateError(error) {
269
+ return error;
270
+ },
271
+ cleanup() { }
272
+ };
273
+ }
274
+ const timeoutError = createTimeoutError(options.providerId, options.timeoutMs);
275
+ const timeoutId = setTimeout(() => {
276
+ options.abortController.abort(timeoutError);
277
+ }, options.timeoutMs);
278
+ return {
279
+ translateError(error) {
280
+ return options.abortController.signal.reason === timeoutError ? timeoutError : error;
281
+ },
282
+ cleanup() {
283
+ clearTimeout(timeoutId);
284
+ }
285
+ };
286
+ }
287
+ function createAbortRace(signal, providerId) {
288
+ let cleanupAbortListener = () => { };
289
+ return {
290
+ signal,
291
+ async run(operation) {
292
+ if (signal.aborted) {
293
+ throw createAbortErrorFromSignal(signal, providerId);
294
+ }
295
+ const abortPromise = new Promise((_, reject) => {
296
+ const abortHandler = () => {
297
+ cleanupAbortListener();
298
+ reject(createAbortErrorFromSignal(signal, providerId));
299
+ };
300
+ cleanupAbortListener = () => {
301
+ signal.removeEventListener("abort", abortHandler);
302
+ };
303
+ signal.addEventListener("abort", abortHandler, { once: true });
304
+ });
305
+ try {
306
+ return await Promise.race([operation, abortPromise]);
307
+ }
308
+ finally {
309
+ cleanupAbortListener();
310
+ cleanupAbortListener = () => { };
311
+ }
312
+ },
313
+ translateError(error) {
314
+ return error;
315
+ },
316
+ cleanup() {
317
+ cleanupAbortListener();
318
+ cleanupAbortListener = () => { };
319
+ }
320
+ };
321
+ }
322
+ function runtimeTimeoutMs(options) {
323
+ const budgetTimeoutMs = options.budget?.timeoutMs;
324
+ const terminationTimeoutMs = timeoutMsFromTermination(options.terminate);
325
+ if (budgetTimeoutMs === undefined) {
326
+ return terminationTimeoutMs;
327
+ }
328
+ if (terminationTimeoutMs === undefined) {
329
+ return budgetTimeoutMs;
330
+ }
331
+ return Math.min(budgetTimeoutMs, terminationTimeoutMs);
332
+ }
333
+ function timeoutMsFromTermination(condition) {
334
+ if (!condition) {
335
+ return undefined;
336
+ }
337
+ switch (condition.kind) {
338
+ case "budget":
339
+ return condition.timeoutMs;
340
+ case "firstOf":
341
+ return condition.conditions.reduce((current, child) => {
342
+ const childTimeoutMs = timeoutMsFromTermination(child);
343
+ if (childTimeoutMs === undefined) {
344
+ return current;
345
+ }
346
+ return current === undefined ? childTimeoutMs : Math.min(current, childTimeoutMs);
347
+ }, undefined);
348
+ case "convergence":
349
+ case "judge":
350
+ return undefined;
351
+ }
352
+ }
353
+ function readAbortSignalReason(signal) {
354
+ return signal?.aborted ? signal.reason : undefined;
355
+ }
356
+ function createStreamErrorEvent(error, runId) {
357
+ if (DogpileError.isInstance(error)) {
358
+ return {
359
+ type: "error",
360
+ runId,
361
+ at: new Date().toISOString(),
362
+ name: error.name,
363
+ message: error.message,
364
+ detail: dogpileErrorStreamDetail(error)
365
+ };
366
+ }
367
+ if (error instanceof Error) {
368
+ return {
369
+ type: "error",
370
+ runId,
371
+ at: new Date().toISOString(),
372
+ name: error.name,
373
+ message: error.message
374
+ };
375
+ }
376
+ return {
377
+ type: "error",
378
+ runId,
379
+ at: new Date().toISOString(),
380
+ name: "Error",
381
+ message: String(error)
382
+ };
383
+ }
384
+ function dogpileErrorStreamDetail(error) {
385
+ const detail = {
386
+ code: error.code
387
+ };
388
+ if (error.providerId !== undefined) {
389
+ detail.providerId = error.providerId;
390
+ }
391
+ if (error.retryable !== undefined) {
392
+ detail.retryable = error.retryable;
393
+ }
394
+ if (error.detail !== undefined) {
395
+ for (const [key, value] of Object.entries(error.detail)) {
396
+ detail[key] = value;
397
+ }
398
+ }
399
+ return detail;
400
+ }
401
+ async function runNonStreamingProtocol(options) {
402
+ const abortLifecycle = createNonStreamingAbortLifecycle({
403
+ callerSignal: options.signal,
404
+ timeoutMs: runtimeTimeoutMs(options),
405
+ providerId: options.model.id
406
+ });
407
+ try {
408
+ const emittedEvents = [];
409
+ const result = await abortLifecycle.run(runProtocol({
410
+ ...options,
411
+ ...(abortLifecycle.signal !== undefined ? { signal: abortLifecycle.signal } : {}),
412
+ emit(event) {
413
+ emittedEvents.push(event);
414
+ }
415
+ }));
416
+ const events = emittedEvents.length > 0 ? emittedEvents : result.trace.events;
417
+ const trace = {
418
+ ...result.trace,
419
+ events,
420
+ budgetStateChanges: createReplayTraceBudgetStateChanges(events),
421
+ finalOutput: createReplayTraceFinalOutput(result.output, events.at(-1) ?? result.trace.events.at(-1))
422
+ };
423
+ const runResult = {
424
+ ...result,
425
+ accounting: createRunAccounting({
426
+ tier: trace.tier,
427
+ ...(trace.budget.caps ? { budget: trace.budget.caps } : {}),
428
+ ...(trace.budget.termination ? { termination: trace.budget.termination } : {}),
429
+ cost: result.cost,
430
+ events
431
+ }),
432
+ eventLog: createRunEventLog(trace.runId, trace.protocol, events),
433
+ trace
434
+ };
435
+ return canonicalizeRunResult(await abortLifecycle.run(applyRunEvaluation(runResult, options.evaluate)));
436
+ }
437
+ catch (error) {
438
+ throw abortLifecycle.translateError(error);
439
+ }
440
+ finally {
441
+ abortLifecycle.cleanup();
442
+ }
443
+ }
444
+ async function applyRunEvaluation(result, evaluate) {
445
+ if (!evaluate) {
446
+ return canonicalizeRunResult(result);
447
+ }
448
+ const evaluation = await evaluate(result);
449
+ const events = result.trace.events.map((event, index) => {
450
+ if (index !== result.trace.events.length - 1 || event.type !== "final") {
451
+ return event;
452
+ }
453
+ return finalEventWithEvaluation(event, evaluation);
454
+ });
455
+ const trace = {
456
+ ...result.trace,
457
+ events
458
+ };
459
+ return canonicalizeRunResult({
460
+ ...result,
461
+ quality: evaluation.quality,
462
+ evaluation,
463
+ trace,
464
+ eventLog: createRunEventLog(trace.runId, trace.protocol, events)
465
+ });
466
+ }
467
+ function finalEventWithEvaluation(event, evaluation) {
468
+ return {
469
+ ...event,
470
+ quality: evaluation.quality,
471
+ evaluation
472
+ };
473
+ }
474
+ function runProtocol(options) {
475
+ switch (options.protocol.kind) {
476
+ case "sequential":
477
+ return runSequential({
478
+ intent: options.intent,
479
+ protocol: options.protocol,
480
+ tier: options.tier,
481
+ model: options.model,
482
+ agents: options.agents,
483
+ tools: options.tools,
484
+ temperature: options.temperature,
485
+ ...(options.budget ? { budget: options.budget } : {}),
486
+ ...(options.seed !== undefined ? { seed: options.seed } : {}),
487
+ ...(options.signal !== undefined ? { signal: options.signal } : {}),
488
+ ...(options.terminate ? { terminate: options.terminate } : {}),
489
+ ...(options.emit ? { emit: options.emit } : {})
490
+ });
491
+ case "broadcast":
492
+ return runBroadcast({
493
+ intent: options.intent,
494
+ protocol: options.protocol,
495
+ tier: options.tier,
496
+ model: options.model,
497
+ agents: options.agents,
498
+ tools: options.tools,
499
+ temperature: options.temperature,
500
+ ...(options.budget ? { budget: options.budget } : {}),
501
+ ...(options.seed !== undefined ? { seed: options.seed } : {}),
502
+ ...(options.signal !== undefined ? { signal: options.signal } : {}),
503
+ ...(options.terminate ? { terminate: options.terminate } : {}),
504
+ ...(options.emit ? { emit: options.emit } : {})
505
+ });
506
+ case "coordinator":
507
+ return runCoordinator({
508
+ intent: options.intent,
509
+ protocol: options.protocol,
510
+ tier: options.tier,
511
+ model: options.model,
512
+ agents: options.agents,
513
+ tools: options.tools,
514
+ temperature: options.temperature,
515
+ ...(options.budget ? { budget: options.budget } : {}),
516
+ ...(options.seed !== undefined ? { seed: options.seed } : {}),
517
+ ...(options.signal !== undefined ? { signal: options.signal } : {}),
518
+ ...(options.terminate ? { terminate: options.terminate } : {}),
519
+ ...(options.emit ? { emit: options.emit } : {})
520
+ });
521
+ case "shared":
522
+ return runShared({
523
+ intent: options.intent,
524
+ protocol: options.protocol,
525
+ tier: options.tier,
526
+ model: options.model,
527
+ agents: options.agents,
528
+ tools: options.tools,
529
+ temperature: options.temperature,
530
+ ...(options.budget ? { budget: options.budget } : {}),
531
+ ...(options.seed !== undefined ? { seed: options.seed } : {}),
532
+ ...(options.signal !== undefined ? { signal: options.signal } : {}),
533
+ ...(options.terminate ? { terminate: options.terminate } : {}),
534
+ ...(options.emit ? { emit: options.emit } : {})
535
+ });
536
+ }
537
+ }
538
+ /**
539
+ * Run a multi-agent workflow in a single call.
540
+ *
541
+ * @remarks
542
+ * Supply a mission through `intent` and provide a configured model provider.
543
+ * Omitted high-level controls default to Sequential coordination and the
544
+ * `balanced` tier. The returned
545
+ * {@link RunResult} contains the final `output`, a JSON-serializable `trace`,
546
+ * direct `transcript` access, aggregate `cost`, and optional `quality`.
547
+ *
548
+ * Use {@link createEngine} when a research harness needs to reuse normalized
549
+ * protocol/model/agent settings across many missions.
550
+ */
551
+ export function run(options) {
552
+ validateDogpileOptions(options);
553
+ const { intent, ...engineOptions } = withHighLevelDefaults(options);
554
+ return createEngine(engineOptions).run(intent);
555
+ }
556
+ /**
557
+ * Stream a multi-agent workflow and await the final result.
558
+ *
559
+ * @remarks
560
+ * The returned handle is an async iterable of {@link RunEvent} values with a
561
+ * `result` promise for the same {@link RunResult} shape returned by
562
+ * {@link run}. This supports live event logs and trace UIs without requiring
563
+ * SDK-managed storage.
564
+ *
565
+ * Streaming and final traces use the same event shapes, so callers can render
566
+ * progress live and persist the completed trace without translation.
567
+ */
568
+ export function stream(options) {
569
+ validateDogpileOptions(options);
570
+ const { intent, ...engineOptions } = withHighLevelDefaults(options);
571
+ return createEngine(engineOptions).stream(intent);
572
+ }
573
+ /**
574
+ * Rehydrate the public result shape from a saved completed trace artifact.
575
+ *
576
+ * @remarks
577
+ * This is the caller-facing replay entrypoint for persisted traces. It does
578
+ * not call the model provider or require SDK-owned storage; it reconstructs
579
+ * the ergonomic {@link RunResult} wrapper from the JSON-serializable
580
+ * {@link Trace} returned by a previous `run()`, `stream()`, or
581
+ * `Dogpile.pile()` call.
582
+ */
583
+ export function replay(trace) {
584
+ const cost = trace.finalOutput.cost;
585
+ const lastEvent = trace.events.at(-1);
586
+ const baseResult = {
587
+ output: trace.finalOutput.output,
588
+ eventLog: createRunEventLog(trace.runId, trace.protocol, trace.events),
589
+ trace,
590
+ transcript: trace.transcript,
591
+ usage: createRunUsage(cost),
592
+ metadata: createRunMetadata({
593
+ runId: trace.runId,
594
+ protocol: trace.protocol,
595
+ tier: trace.tier,
596
+ modelProviderId: trace.modelProviderId,
597
+ agentsUsed: trace.agentsUsed,
598
+ events: trace.events
599
+ }),
600
+ accounting: createRunAccounting({
601
+ tier: trace.tier,
602
+ ...(trace.budget.caps ? { budget: trace.budget.caps } : {}),
603
+ ...(trace.budget.termination ? { termination: trace.budget.termination } : {}),
604
+ cost,
605
+ events: trace.events
606
+ }),
607
+ cost
608
+ };
609
+ if (lastEvent?.type !== "final") {
610
+ return baseResult;
611
+ }
612
+ return {
613
+ ...baseResult,
614
+ ...(lastEvent.quality !== undefined ? { quality: lastEvent.quality } : {}),
615
+ ...(lastEvent.evaluation !== undefined ? { evaluation: lastEvent.evaluation } : {})
616
+ };
617
+ }
618
+ /**
619
+ * Replay a saved completed trace as a stream without invoking a model provider.
620
+ *
621
+ * @remarks
622
+ * This is the streaming counterpart to {@link replay}. It yields the exact
623
+ * saved {@link Trace.events} in order and resolves {@link StreamHandle.result}
624
+ * to the rehydrated {@link RunResult}. Since all data comes from the trace,
625
+ * replay remains storage-free and provider-free.
626
+ */
627
+ export function replayStream(trace) {
628
+ const result = Promise.resolve(replay(trace));
629
+ return {
630
+ get status() {
631
+ return "completed";
632
+ },
633
+ result,
634
+ cancel() {
635
+ // Replay streams are already completed snapshots, so cancellation is a no-op.
636
+ },
637
+ subscribe(subscriber) {
638
+ for (const event of trace.events) {
639
+ subscriber(event);
640
+ }
641
+ return {
642
+ unsubscribe() {
643
+ // Replay subscriptions are finite snapshots; there is no live source to detach from.
644
+ }
645
+ };
646
+ },
647
+ [Symbol.asyncIterator]() {
648
+ let index = 0;
649
+ return {
650
+ next() {
651
+ const event = trace.events[index];
652
+ if (event) {
653
+ index += 1;
654
+ return Promise.resolve({ done: false, value: event });
655
+ }
656
+ return Promise.resolve({ done: true, value: undefined });
657
+ }
658
+ };
659
+ }
660
+ };
661
+ }
662
+ function wireCallerAbortSignal(callerSignal, abortController, cancelRun) {
663
+ if (!callerSignal) {
664
+ return () => { };
665
+ }
666
+ const cancelFromCaller = () => {
667
+ cancelRun(readAbortSignalReason(callerSignal));
668
+ };
669
+ if (callerSignal.aborted) {
670
+ cancelFromCaller();
671
+ return () => { };
672
+ }
673
+ callerSignal.addEventListener("abort", cancelFromCaller, { once: true });
674
+ const remove = () => {
675
+ callerSignal.removeEventListener("abort", cancelFromCaller);
676
+ };
677
+ abortController.signal.addEventListener("abort", remove, { once: true });
678
+ return remove;
679
+ }
680
+ function createStreamCancellationError(providerId, cause) {
681
+ return new DogpileError({
682
+ code: "aborted",
683
+ message: "The operation was aborted.",
684
+ retryable: false,
685
+ providerId,
686
+ ...(cause !== undefined ? { cause } : {}),
687
+ detail: {
688
+ status: "cancelled"
689
+ }
690
+ });
691
+ }
692
+ function isCancellationError(error) {
693
+ if (DogpileError.isInstance(error)) {
694
+ return error.code === "aborted";
695
+ }
696
+ return error instanceof Error && error.name === "AbortError";
697
+ }
698
+ function withHighLevelDefaults(options) {
699
+ return {
700
+ ...options,
701
+ protocol: options.protocol ?? defaultHighLevelProtocol,
702
+ tier: options.tier ?? defaultHighLevelTier
703
+ };
704
+ }
705
+ /**
706
+ * Branded high-level SDK namespace.
707
+ *
708
+ * `Dogpile.pile()` is the ergonomic caller-facing workflow API. It uses the
709
+ * non-streaming execution path and resolves only after the protocol completes,
710
+ * returning `{ output, eventLog, transcript, usage, metadata, trace, cost,
711
+ * quality }`.
712
+ */
713
+ function pile(options) {
714
+ return run(options);
715
+ }
716
+ export const Dogpile = {
717
+ pile,
718
+ replay,
719
+ replayStream,
720
+ stream,
721
+ createEngine
722
+ };
723
+ //# sourceMappingURL=engine.js.map