@noetaris/harness 0.5.0 → 0.7.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/README.md +229 -5
- package/dist/index.d.ts +122 -2
- package/dist/index.js +259 -143
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -510,7 +510,27 @@ function safeInvoke(hookName, call, sink) {
|
|
|
510
510
|
reportObserverError(error, { hookName }, sink);
|
|
511
511
|
}
|
|
512
512
|
}
|
|
513
|
+
async function runWrapped(wrap, fn, onObserverError) {
|
|
514
|
+
let p;
|
|
515
|
+
const inner = () => {
|
|
516
|
+
if (p === void 0) {
|
|
517
|
+
p = new Promise((resolve) => resolve(fn()));
|
|
518
|
+
p.catch(() => {
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
return p;
|
|
522
|
+
};
|
|
523
|
+
try {
|
|
524
|
+
await wrap(inner);
|
|
525
|
+
} catch (e) {
|
|
526
|
+
const started = p;
|
|
527
|
+
const passedThrough = started !== void 0 && await started.then(() => false, (err) => err === e);
|
|
528
|
+
if (!passedThrough) reportObserverError(e, { hookName: "wrap" }, onObserverError);
|
|
529
|
+
}
|
|
530
|
+
return inner();
|
|
531
|
+
}
|
|
513
532
|
function composeObservers(observers, onObserverError) {
|
|
533
|
+
const wrappers = observers.filter((o) => o.wrap !== void 0);
|
|
514
534
|
return {
|
|
515
535
|
onRunStart: (ctx) => {
|
|
516
536
|
for (const o of observers) safeInvoke("onRunStart", () => o.onRunStart?.(ctx), onObserverError);
|
|
@@ -532,6 +552,14 @@ function composeObservers(observers, onObserverError) {
|
|
|
532
552
|
},
|
|
533
553
|
onEvent: (ctx, type, payload) => {
|
|
534
554
|
for (const o of observers) safeInvoke("onEvent", () => o.onEvent?.(ctx, type, payload), onObserverError);
|
|
555
|
+
},
|
|
556
|
+
onStepSettled: (ctx, event) => {
|
|
557
|
+
for (const o of observers) safeInvoke("onStepSettled", () => o.onStepSettled?.(ctx, event), onObserverError);
|
|
558
|
+
},
|
|
559
|
+
// First observer outermost; each layer isolated on its own by runWrapped.
|
|
560
|
+
wrap: (ctx, scope, fn) => {
|
|
561
|
+
const layer = (i) => i === wrappers.length ? fn() : runWrapped((inner) => wrappers[i].wrap(ctx, scope, inner), () => layer(i + 1), onObserverError);
|
|
562
|
+
return layer(0);
|
|
535
563
|
}
|
|
536
564
|
};
|
|
537
565
|
}
|
|
@@ -585,6 +613,21 @@ function extractRunListeners(resources) {
|
|
|
585
613
|
return raw;
|
|
586
614
|
}
|
|
587
615
|
|
|
616
|
+
// src/agent/ctx-within.ts
|
|
617
|
+
function createWithinFn(observer, currentStep, onObserverError) {
|
|
618
|
+
return (type, payload, fn) => {
|
|
619
|
+
const wrap = observer?.wrap;
|
|
620
|
+
const stepCtx = currentStep();
|
|
621
|
+
if (wrap === void 0 || stepCtx === null) return fn();
|
|
622
|
+
return runWrapped((inner) => wrap(stepCtx, { kind: "event", type, payload }, inner), fn, onObserverError);
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
function runStepScope(observer, stepCtx, fn, onObserverError) {
|
|
626
|
+
const wrap = observer?.wrap;
|
|
627
|
+
if (wrap === void 0) return fn();
|
|
628
|
+
return runWrapped((inner) => wrap(stepCtx, { kind: "step" }, inner), fn, onObserverError);
|
|
629
|
+
}
|
|
630
|
+
|
|
588
631
|
// src/loop/loop-executor.ts
|
|
589
632
|
var NOOP_OBSERVER = {};
|
|
590
633
|
function isForkCursor(cursor) {
|
|
@@ -645,6 +688,24 @@ function applyUpdate(state, update, schema, touchedKeys) {
|
|
|
645
688
|
touchedKeys?.add(key);
|
|
646
689
|
}
|
|
647
690
|
}
|
|
691
|
+
function fireSettled(obs, stepCtx, stepStart, fields, onObserverError) {
|
|
692
|
+
const hook = obs?.onStepSettled;
|
|
693
|
+
if (hook === void 0) return;
|
|
694
|
+
const event = { ...fields, durationMs: Date.now() - stepStart };
|
|
695
|
+
safeInvoke("onStepSettled", () => hook(stepCtx, event), onObserverError);
|
|
696
|
+
}
|
|
697
|
+
function stepContextOf(runCtx, stepName) {
|
|
698
|
+
return {
|
|
699
|
+
agentId: runCtx.agentId,
|
|
700
|
+
sessionId: runCtx.sessionId,
|
|
701
|
+
runId: runCtx.runId,
|
|
702
|
+
stepName,
|
|
703
|
+
...runCtx.branchPath !== void 0 ? { branchPath: runCtx.branchPath } : {}
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
function asError(e) {
|
|
707
|
+
return e instanceof Error ? e : new Error(String(e));
|
|
708
|
+
}
|
|
648
709
|
async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callbacks) {
|
|
649
710
|
const { onBeforeStep, onAfterStep, onError, onComplete, onInterrupt } = callbacks ?? {};
|
|
650
711
|
const obs = callbacks?.observer;
|
|
@@ -663,150 +724,177 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
|
|
|
663
724
|
...callbacks?.branchPath !== void 0 && callbacks.branchPath.length > 0 ? { branchPath: callbacks.branchPath } : {}
|
|
664
725
|
};
|
|
665
726
|
safeInvoke("onRunStart", () => obs?.onRunStart?.(runCtx), onObserverError);
|
|
727
|
+
let runEnded = false;
|
|
728
|
+
const endRun = (signal) => {
|
|
729
|
+
runEnded = true;
|
|
730
|
+
safeInvoke("onRunEnd", () => obs?.onRunEnd?.(runCtx, { signal, durationMs: Date.now() - runStart }), onObserverError);
|
|
731
|
+
};
|
|
666
732
|
const callCountRef = { current: 0 };
|
|
667
733
|
const stepCtxRef = { current: null };
|
|
668
734
|
ctx["interrupt"] = createInterruptFn(state, callCountRef, callbacks?.branchPath);
|
|
669
735
|
ctx["emit"] = createEmitFn(callbacks?.listeners ?? {}, obs, stepCtxRef, onObserverError);
|
|
736
|
+
ctx["within"] = createWithinFn(obs, () => stepCtxRef.current, onObserverError);
|
|
737
|
+
const telemetryObserver = obs !== void 0 ? composeObservers([obs], onObserverError) : NOOP_OBSERVER;
|
|
670
738
|
let cursor = startCursor ?? graph.entryStep;
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
if (isForkCursor(cursor)) {
|
|
677
|
-
const forkCursor = cursor;
|
|
678
|
-
const forkNode = graph.steps.find((s) => s.name === forkCursor.fork);
|
|
679
|
-
if (!isForkDef(forkNode)) {
|
|
680
|
-
throw new NoNextStepError(forkCursor.fork);
|
|
681
|
-
}
|
|
682
|
-
onBeforeStep?.(forkNode.name, { ...state });
|
|
683
|
-
const outcome = await handleFork(
|
|
684
|
-
forkNode,
|
|
685
|
-
forkCursor.branches,
|
|
686
|
-
state,
|
|
687
|
-
ctx,
|
|
688
|
-
schema,
|
|
689
|
-
shouldStop,
|
|
690
|
-
callbacks,
|
|
691
|
-
obs,
|
|
692
|
-
runCtx,
|
|
693
|
-
runStart,
|
|
694
|
-
implicitNextMap,
|
|
695
|
-
graph
|
|
696
|
-
);
|
|
697
|
-
if (outcome.done) {
|
|
698
|
-
return outcome.result;
|
|
699
|
-
}
|
|
700
|
-
cursor = outcome.nextCursor;
|
|
701
|
-
continue;
|
|
702
|
-
}
|
|
703
|
-
onBeforeStep?.(cursor, { ...state });
|
|
704
|
-
const node = graph.steps.find((s) => s.name === cursor);
|
|
705
|
-
if (isForkDef(node)) {
|
|
706
|
-
const outcome = await handleFork(
|
|
707
|
-
node,
|
|
708
|
-
void 0,
|
|
709
|
-
state,
|
|
710
|
-
ctx,
|
|
711
|
-
schema,
|
|
712
|
-
shouldStop,
|
|
713
|
-
callbacks,
|
|
714
|
-
obs,
|
|
715
|
-
runCtx,
|
|
716
|
-
runStart,
|
|
717
|
-
implicitNextMap,
|
|
718
|
-
graph
|
|
719
|
-
);
|
|
720
|
-
if (outcome.done) {
|
|
721
|
-
return outcome.result;
|
|
722
|
-
}
|
|
723
|
-
cursor = outcome.nextCursor;
|
|
724
|
-
continue;
|
|
725
|
-
}
|
|
726
|
-
const step = node;
|
|
727
|
-
callCountRef.current = 0;
|
|
728
|
-
const stepCtx = {
|
|
729
|
-
agentId: ctx.agentId,
|
|
730
|
-
sessionId: ctx.sessionId,
|
|
731
|
-
stepName: cursor,
|
|
732
|
-
...callbacks?.branchPath !== void 0 && callbacks.branchPath.length > 0 ? { branchPath: callbacks.branchPath } : {}
|
|
733
|
-
};
|
|
734
|
-
stepCtxRef.current = stepCtx;
|
|
735
|
-
const stepStart = Date.now();
|
|
736
|
-
safeInvoke("onStepStart", () => obs?.onStepStart?.(stepCtx), onObserverError);
|
|
737
|
-
if (callbacks?.telemetrySlots !== void 0) {
|
|
738
|
-
const telemetry = { observer: obs ?? NOOP_OBSERVER, stepContext: stepCtx };
|
|
739
|
-
for (const { key, slot } of callbacks.telemetrySlots) {
|
|
740
|
-
;
|
|
741
|
-
ctx[key] = slot.withTelemetry(telemetry);
|
|
739
|
+
try {
|
|
740
|
+
while (true) {
|
|
741
|
+
if (shouldStop?.()) {
|
|
742
|
+
endRun("$stopped");
|
|
743
|
+
return { state, signal: null, cursor, paused: true };
|
|
742
744
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
745
|
+
if (isForkCursor(cursor)) {
|
|
746
|
+
const forkCursor = cursor;
|
|
747
|
+
const forkNode = graph.steps.find((s) => s.name === forkCursor.fork);
|
|
748
|
+
if (!isForkDef(forkNode)) {
|
|
749
|
+
throw new NoNextStepError(forkCursor.fork);
|
|
750
|
+
}
|
|
751
|
+
onBeforeStep?.(forkNode.name, { ...state });
|
|
752
|
+
const outcome = await handleFork(
|
|
753
|
+
forkNode,
|
|
754
|
+
forkCursor.branches,
|
|
749
755
|
state,
|
|
750
|
-
ctx
|
|
756
|
+
ctx,
|
|
757
|
+
schema,
|
|
758
|
+
shouldStop,
|
|
759
|
+
callbacks,
|
|
760
|
+
obs,
|
|
761
|
+
runCtx,
|
|
762
|
+
endRun,
|
|
763
|
+
implicitNextMap,
|
|
764
|
+
graph
|
|
751
765
|
);
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
state.$interrupt = null;
|
|
755
|
-
state.$interruptResponses = {};
|
|
766
|
+
if (outcome.done) {
|
|
767
|
+
return outcome.result;
|
|
756
768
|
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
769
|
+
cursor = outcome.nextCursor;
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
onBeforeStep?.(cursor, { ...state });
|
|
773
|
+
const node = graph.steps.find((s) => s.name === cursor);
|
|
774
|
+
if (isForkDef(node)) {
|
|
775
|
+
const outcome = await handleFork(
|
|
776
|
+
node,
|
|
777
|
+
void 0,
|
|
778
|
+
state,
|
|
779
|
+
ctx,
|
|
780
|
+
schema,
|
|
781
|
+
shouldStop,
|
|
782
|
+
callbacks,
|
|
783
|
+
obs,
|
|
784
|
+
runCtx,
|
|
785
|
+
endRun,
|
|
786
|
+
implicitNextMap,
|
|
787
|
+
graph
|
|
788
|
+
);
|
|
789
|
+
if (outcome.done) {
|
|
790
|
+
return outcome.result;
|
|
764
791
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
safeInvoke("onStepError", () => obs?.onStepError?.(stepCtx, { error: state.$error, durationMs: Date.now() - stepStart }), onObserverError);
|
|
792
|
+
cursor = outcome.nextCursor;
|
|
793
|
+
continue;
|
|
768
794
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
795
|
+
const step = node;
|
|
796
|
+
callCountRef.current = 0;
|
|
797
|
+
const stepCtx = stepContextOf(runCtx, cursor);
|
|
798
|
+
stepCtxRef.current = stepCtx;
|
|
799
|
+
const stepStart = Date.now();
|
|
800
|
+
safeInvoke("onStepStart", () => obs?.onStepStart?.(stepCtx), onObserverError);
|
|
801
|
+
if (callbacks?.telemetrySlots !== void 0) {
|
|
802
|
+
const telemetry = {
|
|
803
|
+
observer: telemetryObserver,
|
|
804
|
+
stepContext: stepCtx,
|
|
805
|
+
within: createWithinFn(obs, () => stepCtx, onObserverError)
|
|
806
|
+
};
|
|
807
|
+
for (const { key, slot } of callbacks.telemetrySlots) {
|
|
808
|
+
;
|
|
809
|
+
ctx[key] = slot.withTelemetry(telemetry);
|
|
810
|
+
}
|
|
785
811
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
812
|
+
let runSucceeded = false;
|
|
813
|
+
let applied;
|
|
814
|
+
const settle = (fields) => fireSettled(obs, stepCtx, stepStart, { ...fields, ...applied !== void 0 ? { update: applied } : {} }, onObserverError);
|
|
815
|
+
if (step.run !== void 0) {
|
|
816
|
+
const hadResponses = Object.keys(state.$interruptResponses).length > 0;
|
|
817
|
+
try {
|
|
818
|
+
const run = step.run;
|
|
819
|
+
const update = await runStepScope(obs, stepCtx, () => run(
|
|
820
|
+
state,
|
|
821
|
+
ctx
|
|
822
|
+
), onObserverError);
|
|
823
|
+
applyUpdate(state, update, schema, callbacks?.touchedKeys);
|
|
824
|
+
applied = update;
|
|
825
|
+
if (hadResponses) {
|
|
826
|
+
state.$interrupt = null;
|
|
827
|
+
state.$interruptResponses = {};
|
|
828
|
+
}
|
|
829
|
+
runSucceeded = true;
|
|
830
|
+
} catch (e) {
|
|
831
|
+
if (isInterruptPause(e)) {
|
|
832
|
+
onInterrupt?.(e.prompt, e.interruptId);
|
|
833
|
+
safeInvoke("onInterrupt", () => obs?.onInterrupt?.(stepCtx, { prompt: e.prompt, interruptId: e.interruptId }), onObserverError);
|
|
834
|
+
settle({ outcome: "interrupt", interrupt: { interruptId: e.interruptId, prompt: e.prompt }, next: { kind: "pause" } });
|
|
835
|
+
endRun("$interrupt");
|
|
836
|
+
return { state, signal: "$interrupt", cursor, paused: true };
|
|
837
|
+
}
|
|
838
|
+
state.$error = e instanceof Error ? e : new Error(String(e));
|
|
839
|
+
onError?.(state.$error, cursor);
|
|
840
|
+
safeInvoke("onStepError", () => obs?.onStepError?.(stepCtx, { error: state.$error, durationMs: Date.now() - stepStart }), onObserverError);
|
|
841
|
+
}
|
|
789
842
|
}
|
|
790
|
-
if (
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
843
|
+
if (runSucceeded) {
|
|
844
|
+
state.$error = null;
|
|
845
|
+
onAfterStep?.(cursor, state);
|
|
846
|
+
safeInvoke("onStepEnd", () => obs?.onStepEnd?.(stepCtx, { durationMs: Date.now() - stepStart }), onObserverError);
|
|
794
847
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
848
|
+
const callRoute = step.route !== void 0 && (runSucceeded || step.run === void 0 || step.errorAware);
|
|
849
|
+
if (callRoute) {
|
|
850
|
+
const routeFn = step.route;
|
|
851
|
+
let signal;
|
|
852
|
+
try {
|
|
853
|
+
signal = routeFn(state);
|
|
854
|
+
} catch (e) {
|
|
855
|
+
state.$error = e instanceof Error ? e : new Error(String(e));
|
|
856
|
+
settle({ outcome: "error", error: state.$error, next: { kind: "pause" } });
|
|
857
|
+
endRun("$error");
|
|
858
|
+
return { state, signal: "$error", cursor, paused: true };
|
|
859
|
+
}
|
|
860
|
+
const routed = runSucceeded || step.run === void 0 ? { outcome: "ok", signal } : { outcome: "error", signal, error: state.$error };
|
|
861
|
+
const transition = step.transitions.find((t) => t.signal === signal);
|
|
862
|
+
if (transition === void 0) {
|
|
863
|
+
const unknownSignal = new UnknownSignalError(cursor, signal);
|
|
864
|
+
settle({ outcome: "error", signal, error: unknownSignal, next: { kind: "throw" } });
|
|
865
|
+
throw unknownSignal;
|
|
866
|
+
}
|
|
867
|
+
if (transition.target.kind === "end") {
|
|
868
|
+
settle({ ...routed, next: { kind: "end" } });
|
|
869
|
+
onComplete?.(state, signal);
|
|
870
|
+
endRun(signal);
|
|
871
|
+
return { state, signal, cursor: null, paused: false };
|
|
872
|
+
}
|
|
873
|
+
settle({ ...routed, next: { kind: "step", name: transition.target.name } });
|
|
874
|
+
cursor = transition.target.name;
|
|
875
|
+
} else if (step.run !== void 0 && !runSucceeded) {
|
|
876
|
+
if (graph.onError !== void 0) {
|
|
877
|
+
settle({ outcome: "error", error: state.$error, next: { kind: "step", name: graph.onError } });
|
|
878
|
+
cursor = graph.onError;
|
|
879
|
+
} else {
|
|
880
|
+
settle({ outcome: "error", error: state.$error, next: { kind: "pause" } });
|
|
881
|
+
endRun("$error");
|
|
882
|
+
return { state, signal: "$error", cursor, paused: true };
|
|
883
|
+
}
|
|
799
884
|
} else {
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
885
|
+
const next = step.next ?? implicitNextMap.get(cursor) ?? null;
|
|
886
|
+
if (next === null) {
|
|
887
|
+
const noNext = new NoNextStepError(cursor);
|
|
888
|
+
settle({ outcome: "error", error: noNext, next: { kind: "throw" } });
|
|
889
|
+
throw noNext;
|
|
890
|
+
}
|
|
891
|
+
settle({ outcome: "ok", next: { kind: "step", name: next } });
|
|
892
|
+
cursor = next;
|
|
807
893
|
}
|
|
808
|
-
cursor = next;
|
|
809
894
|
}
|
|
895
|
+
} catch (e) {
|
|
896
|
+
if (!runEnded) endRun("$error");
|
|
897
|
+
throw e;
|
|
810
898
|
}
|
|
811
899
|
}
|
|
812
900
|
async function runBranch(branch, prior, outerState, outerCtx, schema, shouldStop, branchSignal, branchCallbacks) {
|
|
@@ -842,16 +930,25 @@ async function dispatchFork(forkNode, priorBranches, outerState, outerCtx, schem
|
|
|
842
930
|
);
|
|
843
931
|
const { onBeforeStep: _outerOnBeforeStep, onAfterStep: _outerOnAfterStep, onComplete: _outerOnComplete, ...branchCallbacksRest } = callbacks ?? {};
|
|
844
932
|
const branchCallbacksBase = callbacks !== void 0 ? branchCallbacksRest : void 0;
|
|
845
|
-
const
|
|
933
|
+
const settled = await Promise.allSettled(
|
|
846
934
|
forkNode.branches.map(async (branch, i) => {
|
|
847
935
|
const prior = priorByName.get(branch.name);
|
|
848
|
-
|
|
936
|
+
let outcome;
|
|
937
|
+
try {
|
|
938
|
+
outcome = await runBranch(branch, prior, outerState, outerCtx, schema, shouldStop, branchSignals[i], branchCallbacksBase);
|
|
939
|
+
} catch (e) {
|
|
940
|
+
for (const controller of localAborts) controller.abort();
|
|
941
|
+
throw e;
|
|
942
|
+
}
|
|
849
943
|
if (outcome.result.paused && outcome.result.signal === "$error") {
|
|
850
944
|
for (const controller of localAborts) controller.abort();
|
|
851
945
|
}
|
|
852
946
|
return outcome;
|
|
853
947
|
})
|
|
854
948
|
);
|
|
949
|
+
const rejected = settled.find((r) => r.status === "rejected");
|
|
950
|
+
if (rejected !== void 0) throw rejected.reason;
|
|
951
|
+
const outcomes = settled.map((r) => r.value);
|
|
855
952
|
const failed = outcomes.find((o) => o.result.paused && o.result.signal === "$error");
|
|
856
953
|
if (failed !== void 0) {
|
|
857
954
|
return { kind: "failed", error: failed.result.state.$error };
|
|
@@ -875,7 +972,8 @@ async function dispatchFork(forkNode, priorBranches, outerState, outerCtx, schem
|
|
|
875
972
|
touchedKeys: [...o.touchedKeys]
|
|
876
973
|
};
|
|
877
974
|
});
|
|
878
|
-
|
|
975
|
+
const interrupted = outcomes.some((o) => o.result.paused && o.result.signal === "$interrupt");
|
|
976
|
+
return { kind: "paused", forkCursor: { kind: "fork", fork: forkNode.name, branches }, interrupted };
|
|
879
977
|
}
|
|
880
978
|
const keyOwners = /* @__PURE__ */ new Map();
|
|
881
979
|
for (const outcome of outcomes) {
|
|
@@ -899,31 +997,46 @@ async function dispatchFork(forkNode, priorBranches, outerState, outerCtx, schem
|
|
|
899
997
|
}
|
|
900
998
|
}
|
|
901
999
|
}
|
|
902
|
-
return {
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
const stepCtx = {
|
|
906
|
-
agentId: ctx.agentId,
|
|
907
|
-
sessionId: ctx.sessionId,
|
|
908
|
-
stepName: forkNode.name,
|
|
909
|
-
...callbacks?.branchPath !== void 0 && callbacks.branchPath.length > 0 ? { branchPath: callbacks.branchPath } : {}
|
|
1000
|
+
return {
|
|
1001
|
+
kind: "success",
|
|
1002
|
+
branches: outcomes.map((o) => ({ name: o.name, status: "done", touchedKeys: [...o.touchedKeys] }))
|
|
910
1003
|
};
|
|
1004
|
+
}
|
|
1005
|
+
async function handleFork(forkNode, priorBranches, state, ctx, schema, shouldStop, callbacks, obs, runCtx, endRun, implicitNextMap, graph) {
|
|
1006
|
+
const stepCtx = stepContextOf(runCtx, forkNode.name);
|
|
911
1007
|
const stepStart = Date.now();
|
|
912
1008
|
const onObserverError = callbacks?.onObserverError;
|
|
913
1009
|
safeInvoke("onStepStart", () => obs?.onStepStart?.(stepCtx), onObserverError);
|
|
914
|
-
|
|
1010
|
+
let dispatch;
|
|
1011
|
+
try {
|
|
1012
|
+
dispatch = await dispatchFork(forkNode, priorBranches, state, ctx, schema, shouldStop, callbacks);
|
|
1013
|
+
} catch (e) {
|
|
1014
|
+
fireSettled(obs, stepCtx, stepStart, { outcome: "error", error: asError(e), next: { kind: "throw" } }, onObserverError);
|
|
1015
|
+
throw e;
|
|
1016
|
+
}
|
|
915
1017
|
if (dispatch.kind === "paused") {
|
|
916
|
-
|
|
917
|
-
|
|
1018
|
+
const fork = {
|
|
1019
|
+
branches: dispatch.forkCursor.branches.map((b) => ({ name: b.name, status: b.status, touchedKeys: b.touchedKeys }))
|
|
1020
|
+
};
|
|
1021
|
+
if (dispatch.interrupted === true) {
|
|
1022
|
+
fireSettled(obs, stepCtx, stepStart, { outcome: "interrupt", next: { kind: "pause" }, fork }, onObserverError);
|
|
1023
|
+
endRun("$interrupt");
|
|
1024
|
+
return { done: true, result: { state, signal: "$interrupt", cursor: dispatch.forkCursor, paused: true } };
|
|
1025
|
+
}
|
|
1026
|
+
fireSettled(obs, stepCtx, stepStart, { outcome: "stopped", next: { kind: "pause" }, fork }, onObserverError);
|
|
1027
|
+
endRun("$stopped");
|
|
1028
|
+
return { done: true, result: { state, signal: null, cursor: dispatch.forkCursor, paused: true } };
|
|
918
1029
|
}
|
|
919
1030
|
if (dispatch.kind === "failed") {
|
|
920
1031
|
state.$error = dispatch.error;
|
|
921
1032
|
callbacks?.onError?.(state.$error, forkNode.name);
|
|
922
1033
|
safeInvoke("onStepError", () => obs?.onStepError?.(stepCtx, { error: state.$error, durationMs: Date.now() - stepStart }), onObserverError);
|
|
923
1034
|
if (graph.onError !== void 0) {
|
|
1035
|
+
fireSettled(obs, stepCtx, stepStart, { outcome: "error", error: state.$error, next: { kind: "step", name: graph.onError } }, onObserverError);
|
|
924
1036
|
return { done: false, nextCursor: graph.onError };
|
|
925
1037
|
}
|
|
926
|
-
|
|
1038
|
+
fireSettled(obs, stepCtx, stepStart, { outcome: "error", error: state.$error, next: { kind: "pause" } }, onObserverError);
|
|
1039
|
+
endRun("$error");
|
|
927
1040
|
return { done: true, result: { state, signal: "$error", cursor: forkNode.name, paused: true } };
|
|
928
1041
|
}
|
|
929
1042
|
state.$error = null;
|
|
@@ -931,8 +1044,11 @@ async function handleFork(forkNode, priorBranches, state, ctx, schema, shouldSto
|
|
|
931
1044
|
callbacks?.onAfterStep?.(forkNode.name, state);
|
|
932
1045
|
const next = forkNode.next ?? implicitNextMap.get(forkNode.name) ?? null;
|
|
933
1046
|
if (next === null) {
|
|
934
|
-
|
|
1047
|
+
const noNext = new NoNextStepError(forkNode.name);
|
|
1048
|
+
fireSettled(obs, stepCtx, stepStart, { outcome: "error", error: noNext, next: { kind: "throw" } }, onObserverError);
|
|
1049
|
+
throw noNext;
|
|
935
1050
|
}
|
|
1051
|
+
fireSettled(obs, stepCtx, stepStart, { outcome: "ok", next: { kind: "step", name: next }, fork: { branches: dispatch.branches } }, onObserverError);
|
|
936
1052
|
return { done: false, nextCursor: next };
|
|
937
1053
|
}
|
|
938
1054
|
|