ts-agent-lib 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1141 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+ var zod = require('zod');
5
+
6
+ // src/executor.ts
7
+
8
+ // src/internal/async_queue.ts
9
+ var AsyncQueue = class {
10
+ capacity;
11
+ items = [];
12
+ getWaiters = [];
13
+ putWaiters = [];
14
+ constructor(capacity = Infinity) {
15
+ if (!Number.isFinite(capacity) && capacity !== Infinity) {
16
+ throw new Error("capacity must be a number or Infinity");
17
+ }
18
+ if (capacity <= 0) {
19
+ throw new Error("capacity must be > 0");
20
+ }
21
+ this.capacity = capacity;
22
+ }
23
+ get size() {
24
+ return this.items.length;
25
+ }
26
+ shift() {
27
+ if (this.items.length === 0) {
28
+ return void 0;
29
+ }
30
+ const item = this.items.shift();
31
+ this.drainPutWaiter();
32
+ return item;
33
+ }
34
+ async put(item) {
35
+ if (this.getWaiters.length > 0) {
36
+ const waiter = this.getWaiters.shift();
37
+ if (waiter.signal && waiter.onAbort) {
38
+ waiter.signal.removeEventListener("abort", waiter.onAbort);
39
+ }
40
+ waiter.resolve(item);
41
+ return;
42
+ }
43
+ if (this.items.length < this.capacity) {
44
+ this.items.push(item);
45
+ return;
46
+ }
47
+ return new Promise((resolve, reject) => {
48
+ this.putWaiters.push({ item, resolve, reject });
49
+ });
50
+ }
51
+ tryPut(item) {
52
+ if (this.getWaiters.length > 0) {
53
+ const waiter = this.getWaiters.shift();
54
+ if (waiter.signal && waiter.onAbort) {
55
+ waiter.signal.removeEventListener("abort", waiter.onAbort);
56
+ }
57
+ waiter.resolve(item);
58
+ return true;
59
+ }
60
+ if (this.items.length < this.capacity) {
61
+ this.items.push(item);
62
+ return true;
63
+ }
64
+ return false;
65
+ }
66
+ async get(signal) {
67
+ if (this.items.length > 0) {
68
+ const item = this.items.shift();
69
+ this.drainPutWaiter();
70
+ return item;
71
+ }
72
+ if (signal?.aborted) {
73
+ throw abortError();
74
+ }
75
+ return new Promise((resolve, reject) => {
76
+ const waiter = {
77
+ resolve,
78
+ reject,
79
+ signal
80
+ };
81
+ if (signal) {
82
+ const onAbort = () => {
83
+ this.removeGetWaiter(waiter);
84
+ reject(abortError());
85
+ };
86
+ waiter.onAbort = onAbort;
87
+ signal.addEventListener("abort", onAbort, { once: true });
88
+ }
89
+ this.getWaiters.push(waiter);
90
+ });
91
+ }
92
+ removeGetWaiter(waiter) {
93
+ const idx = this.getWaiters.indexOf(waiter);
94
+ if (idx >= 0) {
95
+ this.getWaiters.splice(idx, 1);
96
+ }
97
+ if (waiter.signal && waiter.onAbort) {
98
+ waiter.signal.removeEventListener("abort", waiter.onAbort);
99
+ }
100
+ }
101
+ drainPutWaiter() {
102
+ if (this.items.length >= this.capacity) {
103
+ return;
104
+ }
105
+ if (this.putWaiters.length === 0) {
106
+ return;
107
+ }
108
+ const waiter = this.putWaiters.shift();
109
+ this.items.push(waiter.item);
110
+ waiter.resolve();
111
+ }
112
+ };
113
+ function abortError() {
114
+ const error = new Error("aborted");
115
+ error.name = "AbortError";
116
+ return error;
117
+ }
118
+ var stepStatusValues = [
119
+ "pending",
120
+ "running",
121
+ "completed",
122
+ "failed",
123
+ "blocked",
124
+ "cancelled"
125
+ ];
126
+ var StepStatus = {
127
+ PENDING: "pending",
128
+ RUNNING: "running",
129
+ COMPLETED: "completed",
130
+ FAILED: "failed",
131
+ BLOCKED: "blocked",
132
+ CANCELLED: "cancelled"
133
+ };
134
+ var StepStatusSchema = zod.z.enum(stepStatusValues);
135
+ var executionStatusValues = [
136
+ "pending",
137
+ "running",
138
+ "completed",
139
+ "failed",
140
+ "cancelled"
141
+ ];
142
+ var ExecutionStatus = {
143
+ PENDING: "pending",
144
+ RUNNING: "running",
145
+ COMPLETED: "completed",
146
+ FAILED: "failed",
147
+ CANCELLED: "cancelled"
148
+ };
149
+ var ExecutionStatusSchema = zod.z.enum(executionStatusValues);
150
+ var depsSchema = zod.z.array(zod.z.string()).optional().transform((val) => val ?? []);
151
+ var StepSchema = zod.z.object({
152
+ id: zod.z.string(),
153
+ action: zod.z.string(),
154
+ deps: depsSchema,
155
+ payload: zod.z.unknown().optional()
156
+ });
157
+ function utcNow() {
158
+ return /* @__PURE__ */ new Date();
159
+ }
160
+ var ErrorInfoSchema = zod.z.object({
161
+ message: zod.z.string(),
162
+ type: zod.z.string().optional(),
163
+ traceback: zod.z.string().optional()
164
+ });
165
+ var StepResultSchema = zod.z.object({
166
+ stepId: zod.z.string(),
167
+ status: StepStatusSchema.default(StepStatus.PENDING),
168
+ output: zod.z.unknown().optional(),
169
+ error: ErrorInfoSchema.optional(),
170
+ startedAt: zod.z.coerce.date().optional(),
171
+ finishedAt: zod.z.coerce.date().optional()
172
+ });
173
+ var stepResultMapSchema = zod.z.preprocess(
174
+ (val) => {
175
+ if (val instanceof Map) {
176
+ return val;
177
+ }
178
+ if (val && typeof val === "object") {
179
+ return new Map(Object.entries(val));
180
+ }
181
+ return val;
182
+ },
183
+ zod.z.map(zod.z.string(), StepResultSchema)
184
+ );
185
+ var ExecutionStateSchema = zod.z.object({
186
+ executionId: zod.z.string(),
187
+ status: ExecutionStatusSchema.default(ExecutionStatus.PENDING),
188
+ steps: stepResultMapSchema,
189
+ startedAt: zod.z.coerce.date().optional(),
190
+ finishedAt: zod.z.coerce.date().optional()
191
+ });
192
+ var ExecutionEventSchema = zod.z.object({
193
+ executionId: zod.z.string(),
194
+ ts: zod.z.coerce.date(),
195
+ type: zod.z.string()
196
+ });
197
+ var ExecutionStartedSchema = ExecutionEventSchema.extend({
198
+ type: zod.z.literal("execution_started"),
199
+ startedAt: zod.z.coerce.date()
200
+ });
201
+ var ExecutionCompletedSchema = ExecutionEventSchema.extend({
202
+ type: zod.z.literal("execution_completed"),
203
+ status: ExecutionStatusSchema,
204
+ finishedAt: zod.z.coerce.date()
205
+ });
206
+ var ExecutionCancelledSchema = ExecutionEventSchema.extend({
207
+ type: zod.z.literal("execution_cancelled"),
208
+ finishedAt: zod.z.coerce.date()
209
+ });
210
+ var StepScheduledSchema = ExecutionEventSchema.extend({
211
+ type: zod.z.literal("step_scheduled"),
212
+ stepId: zod.z.string()
213
+ });
214
+ var StepStartedSchema = ExecutionEventSchema.extend({
215
+ type: zod.z.literal("step_started"),
216
+ stepId: zod.z.string()
217
+ });
218
+ var StepCompletedSchema = ExecutionEventSchema.extend({
219
+ type: zod.z.literal("step_completed"),
220
+ stepId: zod.z.string()
221
+ });
222
+ var StepFailedSchema = ExecutionEventSchema.extend({
223
+ type: zod.z.literal("step_failed"),
224
+ stepId: zod.z.string(),
225
+ error: ErrorInfoSchema
226
+ });
227
+ var StepBlockedSchema = ExecutionEventSchema.extend({
228
+ type: zod.z.literal("step_blocked"),
229
+ stepId: zod.z.string()
230
+ });
231
+ var StepCancelledSchema = ExecutionEventSchema.extend({
232
+ type: zod.z.literal("step_cancelled"),
233
+ stepId: zod.z.string()
234
+ });
235
+ var ExecutionEventTypeSchema = zod.z.union([
236
+ ExecutionStartedSchema,
237
+ ExecutionCompletedSchema,
238
+ ExecutionCancelledSchema,
239
+ StepScheduledSchema,
240
+ StepStartedSchema,
241
+ StepCompletedSchema,
242
+ StepFailedSchema,
243
+ StepBlockedSchema,
244
+ StepCancelledSchema
245
+ ]);
246
+ var stepMapSchema = zod.z.preprocess(
247
+ (val) => {
248
+ if (val instanceof Map) {
249
+ return val;
250
+ }
251
+ if (val && typeof val === "object") {
252
+ return new Map(Object.entries(val));
253
+ }
254
+ return val;
255
+ },
256
+ zod.z.map(zod.z.string(), StepSchema)
257
+ );
258
+ var PlanSchema = zod.z.object({
259
+ steps: stepMapSchema
260
+ });
261
+ var Plan = class {
262
+ _steps;
263
+ constructor(steps) {
264
+ const parsedSteps = stepMapSchema.parse(steps);
265
+ const normalized = /* @__PURE__ */ new Map();
266
+ for (const [key, step] of parsedSteps.entries()) {
267
+ if (key !== step.id) {
268
+ throw new Error(`step key '${key}' does not match step.id '${step.id}'`);
269
+ }
270
+ normalized.set(step.id, freezeStep(step));
271
+ }
272
+ this._steps = normalized;
273
+ }
274
+ [Symbol.iterator]() {
275
+ return this._steps.values();
276
+ }
277
+ get steps() {
278
+ return this._steps;
279
+ }
280
+ getStep(stepId) {
281
+ const step = this._steps.get(stepId);
282
+ if (!step) {
283
+ throw new Error(`unknown step '${stepId}'`);
284
+ }
285
+ return step;
286
+ }
287
+ };
288
+ function freezeStep(step) {
289
+ const deps = Array.isArray(step.deps) ? [...step.deps] : [];
290
+ Object.freeze(deps);
291
+ const frozen = {
292
+ id: step.id,
293
+ action: step.action,
294
+ deps,
295
+ payload: step.payload
296
+ };
297
+ return Object.freeze(frozen);
298
+ }
299
+
300
+ // src/executor.ts
301
+ var StepContext = class {
302
+ _executionId;
303
+ _stepId;
304
+ _signal;
305
+ _plan;
306
+ _getResult;
307
+ constructor(params) {
308
+ this._executionId = params.executionId;
309
+ this._stepId = params.stepId;
310
+ this._signal = params.signal;
311
+ this._plan = params.plan;
312
+ this._getResult = params.getResult;
313
+ }
314
+ get executionId() {
315
+ return this._executionId;
316
+ }
317
+ get stepId() {
318
+ return this._stepId;
319
+ }
320
+ get signal() {
321
+ return this._signal;
322
+ }
323
+ isCancelled() {
324
+ return this._signal?.aborted ?? false;
325
+ }
326
+ getStep(stepId) {
327
+ return this._plan.getStep(stepId);
328
+ }
329
+ getResult(stepId) {
330
+ const res = this._getResult(stepId);
331
+ return res ? cloneStepResult(res) : void 0;
332
+ }
333
+ requireResult(stepId) {
334
+ const res = this.getResult(stepId);
335
+ if (!res) {
336
+ throw new Error(`missing result for step '${stepId}'`);
337
+ }
338
+ return res;
339
+ }
340
+ getDependencyResults() {
341
+ const step = this._plan.getStep(this._stepId);
342
+ const deps = {};
343
+ for (const depId of step.deps) {
344
+ const res = this._getResult(depId);
345
+ if (res && res.status === StepStatus.COMPLETED) {
346
+ deps[depId] = cloneStepResult(res);
347
+ }
348
+ }
349
+ return deps;
350
+ }
351
+ getDependencyOutputs() {
352
+ const results = this.getDependencyResults();
353
+ const outputs = {};
354
+ for (const [depId, res] of Object.entries(results)) {
355
+ outputs[depId] = res.output;
356
+ }
357
+ return outputs;
358
+ }
359
+ };
360
+ var DagExecutor = class {
361
+ maxParallelSteps;
362
+ failFast;
363
+ constructor(params = {}) {
364
+ const { maxParallelSteps = 8, failFast = false } = params;
365
+ if (maxParallelSteps <= 0) {
366
+ throw new Error("maxParallelSteps must be > 0");
367
+ }
368
+ this.maxParallelSteps = maxParallelSteps;
369
+ this.failFast = failFast;
370
+ }
371
+ async executeAsync(plan, handlers, options = {}) {
372
+ const execId = options.executionId ?? crypto.randomUUID();
373
+ const state = {
374
+ executionId: execId,
375
+ status: ExecutionStatus.RUNNING,
376
+ steps: /* @__PURE__ */ new Map(),
377
+ startedAt: utcNow()
378
+ };
379
+ const allSteps = /* @__PURE__ */ new Map();
380
+ for (const step of plan) {
381
+ allSteps.set(step.id, step);
382
+ state.steps.set(step.id, {
383
+ stepId: step.id,
384
+ status: StepStatus.PENDING
385
+ });
386
+ }
387
+ const emit = async (event) => {
388
+ if (options.onEvent) {
389
+ await options.onEvent(event);
390
+ }
391
+ };
392
+ await emit({
393
+ type: "execution_started",
394
+ executionId: execId,
395
+ ts: state.startedAt ?? utcNow(),
396
+ startedAt: state.startedAt ?? utcNow()
397
+ });
398
+ const ready = [];
399
+ const readySet = /* @__PURE__ */ new Set();
400
+ const schedState = {
401
+ running: /* @__PURE__ */ new Map(),
402
+ failFast: this.failFast,
403
+ stopScheduling: false
404
+ };
405
+ const isCancelled = () => options.cancelSignal?.aborted ?? false;
406
+ const canRun = (step) => {
407
+ const res = state.steps.get(step.id);
408
+ if (!res || res.status !== StepStatus.PENDING) {
409
+ return false;
410
+ }
411
+ for (const depId of step.deps) {
412
+ const depRes = state.steps.get(depId);
413
+ if (!depRes) {
414
+ throw new Error(`unknown dependency '${depId}' for step '${step.id}'`);
415
+ }
416
+ if (depRes.status !== StepStatus.COMPLETED) {
417
+ return false;
418
+ }
419
+ }
420
+ return true;
421
+ };
422
+ const refreshReady = () => {
423
+ for (const step of allSteps.values()) {
424
+ if (readySet.has(step.id)) {
425
+ continue;
426
+ }
427
+ if (canRun(step)) {
428
+ ready.push(step.id);
429
+ readySet.add(step.id);
430
+ }
431
+ }
432
+ };
433
+ const cancelStep = async (stepId, res) => {
434
+ res.status = StepStatus.CANCELLED;
435
+ if (!res.finishedAt) {
436
+ res.finishedAt = utcNow();
437
+ }
438
+ await emit({
439
+ type: "step_cancelled",
440
+ executionId: execId,
441
+ ts: utcNow(),
442
+ stepId
443
+ });
444
+ };
445
+ const failStep = async (stepId, res, err) => {
446
+ res.status = StepStatus.FAILED;
447
+ res.error = err;
448
+ if (!res.finishedAt) {
449
+ res.finishedAt = utcNow();
450
+ }
451
+ await emit({
452
+ type: "step_failed",
453
+ executionId: execId,
454
+ ts: utcNow(),
455
+ stepId,
456
+ error: err
457
+ });
458
+ if (schedState.failFast) {
459
+ schedState.stopScheduling = true;
460
+ }
461
+ };
462
+ const blockStep = async (stepId, res) => {
463
+ res.status = StepStatus.BLOCKED;
464
+ if (!res.finishedAt) {
465
+ res.finishedAt = utcNow();
466
+ }
467
+ await emit({
468
+ type: "step_blocked",
469
+ executionId: execId,
470
+ ts: utcNow(),
471
+ stepId
472
+ });
473
+ };
474
+ const runStep = async (stepId) => {
475
+ const step = allSteps.get(stepId);
476
+ const res = state.steps.get(stepId);
477
+ if (isCancelled()) {
478
+ await cancelStep(stepId, res);
479
+ return;
480
+ }
481
+ res.status = StepStatus.RUNNING;
482
+ res.startedAt = utcNow();
483
+ await emit({
484
+ type: "step_started",
485
+ executionId: execId,
486
+ ts: utcNow(),
487
+ stepId
488
+ });
489
+ const handler = handlers[step.action];
490
+ if (!handler) {
491
+ await failStep(stepId, res, {
492
+ message: `no handler for action '${step.action}'`,
493
+ type: "MissingHandler"
494
+ });
495
+ return;
496
+ }
497
+ const ctx = new StepContext({
498
+ executionId: execId,
499
+ stepId,
500
+ signal: options.cancelSignal,
501
+ plan,
502
+ getResult: (id) => state.steps.get(id)
503
+ });
504
+ let result;
505
+ try {
506
+ result = StepResultSchema.parse(await handler(step, ctx));
507
+ } catch (err) {
508
+ if (isAbortError(err)) {
509
+ await cancelStep(stepId, res);
510
+ return;
511
+ }
512
+ const errorInfo = {
513
+ message: err instanceof Error ? err.message : String(err),
514
+ type: err instanceof Error ? err.name : "Error",
515
+ traceback: err instanceof Error ? err.stack : void 0
516
+ };
517
+ await failStep(stepId, res, errorInfo);
518
+ return;
519
+ }
520
+ let status = result.status;
521
+ if (status === StepStatus.PENDING || status === StepStatus.RUNNING) {
522
+ status = result.error ? StepStatus.FAILED : StepStatus.COMPLETED;
523
+ } else if (status === StepStatus.COMPLETED && result.error) {
524
+ status = StepStatus.FAILED;
525
+ }
526
+ res.status = status;
527
+ res.output = result.output;
528
+ res.error = result.error;
529
+ res.startedAt = res.startedAt ?? result.startedAt ?? utcNow();
530
+ res.finishedAt = result.finishedAt ?? utcNow();
531
+ if (res.status === StepStatus.COMPLETED) {
532
+ await emit({
533
+ type: "step_completed",
534
+ executionId: execId,
535
+ ts: utcNow(),
536
+ stepId
537
+ });
538
+ } else if (res.status === StepStatus.FAILED) {
539
+ await failStep(
540
+ stepId,
541
+ res,
542
+ res.error ?? { message: "step failed", type: "Unknown" }
543
+ );
544
+ } else if (res.status === StepStatus.CANCELLED) {
545
+ await cancelStep(stepId, res);
546
+ } else if (res.status === StepStatus.BLOCKED) {
547
+ await blockStep(stepId, res);
548
+ }
549
+ };
550
+ refreshReady();
551
+ while (true) {
552
+ if (isCancelled()) {
553
+ schedState.stopScheduling = true;
554
+ }
555
+ while (!schedState.stopScheduling && schedState.running.size < this.maxParallelSteps) {
556
+ if (ready.length === 0) {
557
+ break;
558
+ }
559
+ const stepId = ready.shift();
560
+ readySet.delete(stepId);
561
+ const stepRes = state.steps.get(stepId);
562
+ if (!stepRes || stepRes.status !== StepStatus.PENDING) {
563
+ continue;
564
+ }
565
+ await emit({
566
+ type: "step_scheduled",
567
+ executionId: execId,
568
+ ts: utcNow(),
569
+ stepId
570
+ });
571
+ const task = runStep(stepId);
572
+ schedState.running.set(task, stepId);
573
+ }
574
+ if (schedState.running.size === 0) {
575
+ if (schedState.stopScheduling || ready.length === 0) {
576
+ break;
577
+ }
578
+ }
579
+ if (schedState.running.size > 0) {
580
+ try {
581
+ const { task: finished } = await waitForAny(schedState.running.keys());
582
+ schedState.running.delete(finished);
583
+ } catch (err) {
584
+ if (isTaskError(err)) {
585
+ schedState.running.delete(err.task);
586
+ throw err.error;
587
+ }
588
+ throw err;
589
+ }
590
+ }
591
+ refreshReady();
592
+ }
593
+ if (isCancelled()) {
594
+ for (const [stepId, res] of state.steps.entries()) {
595
+ if (res.status === StepStatus.PENDING) {
596
+ await cancelStep(stepId, res);
597
+ }
598
+ }
599
+ state.status = ExecutionStatus.CANCELLED;
600
+ state.finishedAt = utcNow();
601
+ await emit({
602
+ type: "execution_cancelled",
603
+ executionId: execId,
604
+ ts: utcNow(),
605
+ finishedAt: state.finishedAt
606
+ });
607
+ return state;
608
+ }
609
+ for (const [stepId, res] of state.steps.entries()) {
610
+ if (res.status === StepStatus.PENDING) {
611
+ await blockStep(stepId, res);
612
+ }
613
+ }
614
+ const allCompleted = Array.from(state.steps.values()).every(
615
+ (res) => res.status === StepStatus.COMPLETED
616
+ );
617
+ state.status = allCompleted ? ExecutionStatus.COMPLETED : ExecutionStatus.FAILED;
618
+ state.finishedAt = utcNow();
619
+ await emit({
620
+ type: "execution_completed",
621
+ executionId: execId,
622
+ ts: utcNow(),
623
+ status: state.status,
624
+ finishedAt: state.finishedAt
625
+ });
626
+ return state;
627
+ }
628
+ execute(plan, handlers, options = {}) {
629
+ return this.executeAsync(plan, handlers, options);
630
+ }
631
+ async *streamExecute(plan, handlers, options = {}) {
632
+ const queue = new AsyncQueue(Infinity);
633
+ const controller = new AbortController();
634
+ const signal = mergeAbortSignals(options.cancelSignal, controller.signal);
635
+ let execError;
636
+ const task = this.executeAsync(plan, handlers, {
637
+ ...options,
638
+ cancelSignal: signal,
639
+ onEvent: async (event) => {
640
+ await queue.put(event);
641
+ }
642
+ }).catch((err) => {
643
+ execError = err;
644
+ }).finally(async () => {
645
+ await queue.put(null);
646
+ });
647
+ try {
648
+ while (true) {
649
+ const event = await queue.get();
650
+ if (event === null) {
651
+ break;
652
+ }
653
+ yield event;
654
+ }
655
+ } finally {
656
+ controller.abort();
657
+ await task;
658
+ if (execError) {
659
+ throw execError;
660
+ }
661
+ }
662
+ }
663
+ };
664
+ function isAbortError(err) {
665
+ if (!err || typeof err !== "object") {
666
+ return false;
667
+ }
668
+ const name = "name" in err ? String(err.name) : "";
669
+ return name === "AbortError";
670
+ }
671
+ function isTaskError(err) {
672
+ return typeof err === "object" && err !== null && "task" in err && "error" in err;
673
+ }
674
+ async function waitForAny(tasks) {
675
+ const list = Array.from(tasks);
676
+ if (list.length === 0) {
677
+ throw new Error("waitForAny called with no tasks");
678
+ }
679
+ return Promise.race(
680
+ list.map(
681
+ (task) => task.then(
682
+ () => ({ task }),
683
+ (error) => {
684
+ const taskError = { task, error };
685
+ throw taskError;
686
+ }
687
+ )
688
+ )
689
+ );
690
+ }
691
+ function mergeAbortSignals(...signals) {
692
+ const active = signals.filter(Boolean);
693
+ if (active.length === 0) {
694
+ return void 0;
695
+ }
696
+ if (active.length === 1) {
697
+ return active[0];
698
+ }
699
+ const controller = new AbortController();
700
+ const onAbort = () => {
701
+ controller.abort();
702
+ };
703
+ for (const signal of active) {
704
+ if (signal.aborted) {
705
+ controller.abort();
706
+ break;
707
+ }
708
+ signal.addEventListener("abort", onAbort, { once: true });
709
+ }
710
+ return controller.signal;
711
+ }
712
+ function cloneStepResult(res) {
713
+ return {
714
+ ...res,
715
+ error: res.error ? { ...res.error } : void 0,
716
+ startedAt: res.startedAt ? new Date(res.startedAt) : void 0,
717
+ finishedAt: res.finishedAt ? new Date(res.finishedAt) : void 0
718
+ };
719
+ }
720
+
721
+ // src/errors.ts
722
+ var ExecutionError = class _ExecutionError extends Error {
723
+ executionId;
724
+ status;
725
+ errors;
726
+ constructor(params) {
727
+ const { executionId, status, errors } = params;
728
+ const prefix = `Execution ${executionId} ended with status ${status}`;
729
+ const detail = errors.length > 0 ? `${errors[0].type ?? "Error"}: ${errors[0].message}` : "";
730
+ super(detail ? `${prefix}: ${detail}` : prefix);
731
+ this.executionId = executionId;
732
+ this.status = status;
733
+ this.errors = errors;
734
+ }
735
+ static fromState(state) {
736
+ const errors = [];
737
+ for (const result of state.steps.values()) {
738
+ if (result.error) {
739
+ errors.push(result.error);
740
+ } else if (result.status === StepStatus.BLOCKED || result.status === StepStatus.CANCELLED) {
741
+ errors.push({
742
+ message: `step ${result.stepId} ended with status ${result.status}`,
743
+ type: "ExecutionStatus"
744
+ });
745
+ }
746
+ }
747
+ if (errors.length === 0) {
748
+ errors.push({ message: "execution failed without detailed errors", type: "ExecutionError" });
749
+ }
750
+ return new _ExecutionError({
751
+ executionId: state.executionId,
752
+ status: state.status,
753
+ errors
754
+ });
755
+ }
756
+ };
757
+ function raiseIfFailed(state) {
758
+ if (state.status !== ExecutionStatus.COMPLETED) {
759
+ throw ExecutionError.fromState(state);
760
+ }
761
+ }
762
+
763
+ // src/planning.ts
764
+ var PlanBuilder = class {
765
+ steps = /* @__PURE__ */ new Map();
766
+ addStep(params) {
767
+ const { id, action, deps, payload } = params;
768
+ if (this.steps.has(id)) {
769
+ throw new Error(`step id '${id}' already exists in plan`);
770
+ }
771
+ const depsList = deps ? Array.from(deps) : [];
772
+ const step = {
773
+ id,
774
+ action,
775
+ deps: depsList,
776
+ payload
777
+ };
778
+ this.steps.set(id, step);
779
+ return this;
780
+ }
781
+ addStepObj(step) {
782
+ StepSchema.parse(step);
783
+ if (this.steps.has(step.id)) {
784
+ throw new Error(`step id '${step.id}' already exists in plan`);
785
+ }
786
+ this.steps.set(step.id, step);
787
+ return this;
788
+ }
789
+ build() {
790
+ const stepsCopy = new Map(this.steps);
791
+ this.validateDependencies(stepsCopy);
792
+ this.validateAcyclic(stepsCopy);
793
+ return new Plan(stepsCopy);
794
+ }
795
+ validateDependencies(steps) {
796
+ const ids = new Set(steps.keys());
797
+ for (const step of steps.values()) {
798
+ for (const dep of step.deps) {
799
+ if (!ids.has(dep)) {
800
+ throw new Error(`step '${step.id}' depends on unknown step '${dep}'`);
801
+ }
802
+ if (dep === step.id) {
803
+ throw new Error(`step '${step.id}' cannot depend on itself`);
804
+ }
805
+ }
806
+ }
807
+ }
808
+ validateAcyclic(steps) {
809
+ const cycle = findCycle(steps);
810
+ if (cycle) {
811
+ throw new Error(`cycle detected in plan: ${cycle.join(" -> ")}`);
812
+ }
813
+ }
814
+ };
815
+ function findCycle(steps) {
816
+ const state = /* @__PURE__ */ new Map();
817
+ const stack = [];
818
+ const visit = (node) => {
819
+ const current = state.get(node);
820
+ if (current === "visiting") {
821
+ const idx = stack.indexOf(node);
822
+ return idx >= 0 ? stack.slice(idx).concat(node) : [node, node];
823
+ }
824
+ if (current === "visited") {
825
+ return null;
826
+ }
827
+ state.set(node, "visiting");
828
+ stack.push(node);
829
+ const step = steps.get(node);
830
+ if (step) {
831
+ for (const dep of step.deps) {
832
+ const cycle = visit(dep);
833
+ if (cycle) {
834
+ return cycle;
835
+ }
836
+ }
837
+ }
838
+ stack.pop();
839
+ state.set(node, "visited");
840
+ return null;
841
+ };
842
+ for (const id of steps.keys()) {
843
+ if (!state.has(id)) {
844
+ const cycle = visit(id);
845
+ if (cycle) {
846
+ return cycle;
847
+ }
848
+ }
849
+ }
850
+ return null;
851
+ }
852
+
853
+ // src/observers.ts
854
+ var ObserverFailurePolicy = {
855
+ LENIENT: "lenient",
856
+ STRICT: "strict"
857
+ };
858
+ async function callObserverErrorHandler(handler, observer, event, error) {
859
+ if (!handler) {
860
+ return;
861
+ }
862
+ await handler(observer, event, error);
863
+ }
864
+ function composeObservers(observers, options = {}) {
865
+ const list = Array.from(observers);
866
+ const failurePolicy = options.failurePolicy ?? ObserverFailurePolicy.LENIENT;
867
+ return async (event) => {
868
+ for (const obs of list) {
869
+ try {
870
+ await obs.onEvent(event);
871
+ } catch (err) {
872
+ const error = err instanceof Error ? err : new Error(String(err));
873
+ await callObserverErrorHandler(options.onError, obs, event, error);
874
+ if (failurePolicy === ObserverFailurePolicy.STRICT) {
875
+ throw error;
876
+ }
877
+ }
878
+ }
879
+ };
880
+ }
881
+ function composeObserversParallel(observers, options = {}) {
882
+ const list = Array.from(observers);
883
+ const failurePolicy = options.failurePolicy ?? ObserverFailurePolicy.LENIENT;
884
+ return async (event) => {
885
+ const results = await Promise.allSettled(list.map((obs) => obs.onEvent(event)));
886
+ const errors = [];
887
+ results.forEach((result, index) => {
888
+ if (result.status === "rejected") {
889
+ const error = result.reason instanceof Error ? result.reason : new Error(String(result.reason));
890
+ errors.push({ observer: list[index], error });
891
+ }
892
+ });
893
+ if (errors.length === 0) {
894
+ return;
895
+ }
896
+ for (const entry of errors) {
897
+ await callObserverErrorHandler(options.onError, entry.observer, event, entry.error);
898
+ }
899
+ if (failurePolicy === ObserverFailurePolicy.STRICT) {
900
+ throw errors[0].error;
901
+ }
902
+ };
903
+ }
904
+ var OverflowPolicy = {
905
+ BLOCK: "block",
906
+ DROP_NEW: "drop_new",
907
+ DROP_OLDEST: "drop_oldest"
908
+ };
909
+ var BufferedObserver = class {
910
+ maxQueueSize;
911
+ overflowPolicy;
912
+ onError;
913
+ queue;
914
+ task = null;
915
+ started = false;
916
+ abortController = null;
917
+ dropped = 0;
918
+ failed = false;
919
+ lastError = null;
920
+ constructor(params = {}) {
921
+ this.maxQueueSize = params.maxQueueSize ?? 1e3;
922
+ this.overflowPolicy = params.overflowPolicy ?? OverflowPolicy.BLOCK;
923
+ this.onError = params.onError;
924
+ this.queue = new AsyncQueue(this.maxQueueSize);
925
+ }
926
+ async start() {
927
+ if (this.started) {
928
+ return;
929
+ }
930
+ this.started = true;
931
+ this.abortController = new AbortController();
932
+ this.task = this.run(this.abortController.signal);
933
+ }
934
+ async stop() {
935
+ if (!this.started) {
936
+ return;
937
+ }
938
+ this.started = false;
939
+ this.abortController?.abort();
940
+ if (this.task) {
941
+ try {
942
+ await this.task;
943
+ } catch {
944
+ }
945
+ }
946
+ }
947
+ async onEvent(event) {
948
+ if (this.failed) {
949
+ if (this.lastError) {
950
+ throw this.lastError;
951
+ }
952
+ throw new Error("buffered observer worker has failed");
953
+ }
954
+ if (!this.started) {
955
+ await this.process(event);
956
+ return;
957
+ }
958
+ if (this.overflowPolicy === OverflowPolicy.BLOCK) {
959
+ await this.queue.put(event);
960
+ return;
961
+ }
962
+ if (this.queue.tryPut(event)) {
963
+ return;
964
+ }
965
+ this.dropped += 1;
966
+ if (this.overflowPolicy === OverflowPolicy.DROP_NEW) {
967
+ return;
968
+ }
969
+ if (this.overflowPolicy === OverflowPolicy.DROP_OLDEST) {
970
+ this.queue.shift();
971
+ if (!this.queue.tryPut(event)) {
972
+ return;
973
+ }
974
+ }
975
+ }
976
+ async run(signal) {
977
+ while (!signal.aborted) {
978
+ let event;
979
+ try {
980
+ event = await this.queue.get(signal);
981
+ } catch (err) {
982
+ if (isAbortError2(err)) {
983
+ break;
984
+ }
985
+ throw err;
986
+ }
987
+ try {
988
+ await this.process(event);
989
+ } catch (err) {
990
+ const error = err instanceof Error ? err : new Error(String(err));
991
+ this.failed = true;
992
+ this.lastError = error;
993
+ if (this.onError) {
994
+ await this.onError(event, error);
995
+ }
996
+ break;
997
+ }
998
+ }
999
+ }
1000
+ };
1001
+ function isAbortError2(err) {
1002
+ if (!err || typeof err !== "object") {
1003
+ return false;
1004
+ }
1005
+ const name = "name" in err ? String(err.name) : "";
1006
+ return name === "AbortError";
1007
+ }
1008
+
1009
+ // src/usage.ts
1010
+ var Usage = class _Usage {
1011
+ provider;
1012
+ model;
1013
+ metrics;
1014
+ constructor(params = {}) {
1015
+ this.provider = params.provider;
1016
+ this.model = params.model;
1017
+ this.metrics = { ...params.metrics ?? {} };
1018
+ }
1019
+ add(other) {
1020
+ const provider = this.provider === other.provider ? this.provider : void 0;
1021
+ const model = this.model === other.model ? this.model : void 0;
1022
+ const merged = { ...this.metrics };
1023
+ for (const [key, value] of Object.entries(other.metrics)) {
1024
+ merged[key] = (merged[key] ?? 0) + Number(value);
1025
+ }
1026
+ return new _Usage({ provider, model, metrics: merged });
1027
+ }
1028
+ };
1029
+ function aggregateUsage(state, extract) {
1030
+ let total = new Usage();
1031
+ let first = true;
1032
+ for (const res of state.steps.values()) {
1033
+ const usage = extract(res);
1034
+ if (!usage) {
1035
+ continue;
1036
+ }
1037
+ if (first) {
1038
+ total = usage;
1039
+ first = false;
1040
+ } else {
1041
+ total = total.add(usage);
1042
+ }
1043
+ }
1044
+ return total;
1045
+ }
1046
+ function estimateCost(usage, rules) {
1047
+ const costs = {};
1048
+ for (const rule of rules) {
1049
+ const value = Number(usage.metrics[rule.metric] ?? 0);
1050
+ if (!value) {
1051
+ continue;
1052
+ }
1053
+ const scale = rule.scale ?? 1;
1054
+ const cost = value / scale * rule.perUnit;
1055
+ costs[rule.metric] = (costs[rule.metric] ?? 0) + cost;
1056
+ }
1057
+ const total = Object.values(costs).reduce((sum, value) => sum + value, 0);
1058
+ return { byMetric: costs, total };
1059
+ }
1060
+
1061
+ // src/runner.ts
1062
+ var TaskOutput = class {
1063
+ value;
1064
+ usage;
1065
+ meta;
1066
+ constructor(params) {
1067
+ this.value = params.value;
1068
+ this.usage = params.usage;
1069
+ this.meta = params.meta ?? {};
1070
+ }
1071
+ };
1072
+ function defaultUsageExtractor(res) {
1073
+ const out = res.output;
1074
+ if (out instanceof TaskOutput) {
1075
+ return out.usage;
1076
+ }
1077
+ return void 0;
1078
+ }
1079
+ async function runPlan(plan, handlers, options = {}) {
1080
+ const execImpl = options.executor ?? new DagExecutor();
1081
+ const state = await execImpl.executeAsync(plan, handlers);
1082
+ if (options.failOnError ?? true) {
1083
+ raiseIfFailed(state);
1084
+ }
1085
+ let usage;
1086
+ let costs;
1087
+ const extractor = options.usageExtractor === void 0 ? defaultUsageExtractor : options.usageExtractor;
1088
+ if (extractor) {
1089
+ const aggregated = aggregateUsage(state, extractor);
1090
+ if (!aggregated.provider && !aggregated.model && Object.keys(aggregated.metrics).length === 0) {
1091
+ usage = void 0;
1092
+ } else {
1093
+ usage = aggregated;
1094
+ }
1095
+ }
1096
+ if (usage && options.pricingRules && options.pricingRules.length > 0) {
1097
+ costs = estimateCost(usage, options.pricingRules);
1098
+ }
1099
+ return [state, usage, costs];
1100
+ }
1101
+
1102
+ exports.BufferedObserver = BufferedObserver;
1103
+ exports.DagExecutor = DagExecutor;
1104
+ exports.ErrorInfoSchema = ErrorInfoSchema;
1105
+ exports.ExecutionCancelledSchema = ExecutionCancelledSchema;
1106
+ exports.ExecutionCompletedSchema = ExecutionCompletedSchema;
1107
+ exports.ExecutionError = ExecutionError;
1108
+ exports.ExecutionEventSchema = ExecutionEventSchema;
1109
+ exports.ExecutionEventTypeSchema = ExecutionEventTypeSchema;
1110
+ exports.ExecutionStartedSchema = ExecutionStartedSchema;
1111
+ exports.ExecutionStateSchema = ExecutionStateSchema;
1112
+ exports.ExecutionStatus = ExecutionStatus;
1113
+ exports.ExecutionStatusSchema = ExecutionStatusSchema;
1114
+ exports.ObserverFailurePolicy = ObserverFailurePolicy;
1115
+ exports.OverflowPolicy = OverflowPolicy;
1116
+ exports.Plan = Plan;
1117
+ exports.PlanBuilder = PlanBuilder;
1118
+ exports.PlanSchema = PlanSchema;
1119
+ exports.StepBlockedSchema = StepBlockedSchema;
1120
+ exports.StepCancelledSchema = StepCancelledSchema;
1121
+ exports.StepCompletedSchema = StepCompletedSchema;
1122
+ exports.StepContext = StepContext;
1123
+ exports.StepFailedSchema = StepFailedSchema;
1124
+ exports.StepResultSchema = StepResultSchema;
1125
+ exports.StepScheduledSchema = StepScheduledSchema;
1126
+ exports.StepSchema = StepSchema;
1127
+ exports.StepStartedSchema = StepStartedSchema;
1128
+ exports.StepStatus = StepStatus;
1129
+ exports.StepStatusSchema = StepStatusSchema;
1130
+ exports.TaskOutput = TaskOutput;
1131
+ exports.Usage = Usage;
1132
+ exports.aggregateUsage = aggregateUsage;
1133
+ exports.composeObservers = composeObservers;
1134
+ exports.composeObserversParallel = composeObserversParallel;
1135
+ exports.defaultUsageExtractor = defaultUsageExtractor;
1136
+ exports.estimateCost = estimateCost;
1137
+ exports.raiseIfFailed = raiseIfFailed;
1138
+ exports.runPlan = runPlan;
1139
+ exports.utcNow = utcNow;
1140
+ //# sourceMappingURL=index.cjs.map
1141
+ //# sourceMappingURL=index.cjs.map