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