@noetaris/harness 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -2
- package/dist/index.d.ts +56 -1
- package/dist/index.js +82 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -156,6 +156,13 @@ const resumed = run.resume(response, interruptId)
|
|
|
156
156
|
const resumed = agent.resume(response, sessionId, interruptId)
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
+
`agent.resume()` takes an optional fourth argument with these reserved resources (a subset of
|
|
160
|
+
`run()`'s): `observer`, `onObserverError`, `events: { onStoreError }`, and `parentRunId` (the caller's run id,
|
|
161
|
+
for a sub agent resumed from a step). `parentRunId` applies to that resumed run only: calling
|
|
162
|
+
`resume()` again on the handle it returns keeps the other resources but not `parentRunId`, which
|
|
163
|
+
is stale by then. With a store, both `agent.resume()` and `run.resume()` record the agent's
|
|
164
|
+
`instanceId` in the resumed run's `metadata`.
|
|
165
|
+
|
|
159
166
|
**Resume replays the interrupted step from the top.** On resume, the step that called
|
|
160
167
|
`ctx.interrupt()` runs again from its first line; each `ctx.interrupt()` call it reaches
|
|
161
168
|
returns the stored response instead of pausing. State updates from the paused attempt are
|
|
@@ -263,6 +270,7 @@ throws on an adapter event never fails a step. Every hook is optional.
|
|
|
263
270
|
| `onEvent(ctx, type, payload)` | on `ctx.emit()` and adapter events such as `llm.response` |
|
|
264
271
|
| `onStepSettled(ctx, event)` | once per step, when its outcome and destination are final |
|
|
265
272
|
| `wrap(ctx, scope, fn)` | around a step's `run`, and around calls made with `ctx.within` |
|
|
273
|
+
| `carry(ctx)` | once per top-level run, right after `onRunStart`; returns a string map to keep on the run's record (see [Linking runs](#linking-runs)) |
|
|
266
274
|
|
|
267
275
|
Step hooks receive a `StepContext`: `agentId`, `sessionId`, `runId`, `stepName` and, inside a
|
|
268
276
|
fork branch, `branchPath`. `runId` is the same value as `RunContext.runId`. Steps inside a branch
|
|
@@ -314,6 +322,27 @@ await createAgent('demo', h, {}).run({}, { observer: logger })
|
|
|
314
322
|
// decide: ok end (signal done)
|
|
315
323
|
```
|
|
316
324
|
|
|
325
|
+
### Linking runs
|
|
326
|
+
|
|
327
|
+
Some runs continue an earlier one. Their `RunContext` says which, so an observer can link them:
|
|
328
|
+
|
|
329
|
+
| The run is | `RunContext` field |
|
|
330
|
+
|---|---|
|
|
331
|
+
| the next run on a session that paused — on an interrupt, a stop, or an unhandled error — whether started by `agent.resume()`, `run.resume()` (with or without a store), or `agent.run()` on that session | `resumedFrom: { runId, carrier? }` — the run that paused |
|
|
332
|
+
| the first run of a session created by `SessionStore.branch()` | `branchedFrom: { sessionId, runId, carrier? }` — the run it was branched from, in the source session |
|
|
333
|
+
|
|
334
|
+
A run has at most one of the two, only on its top-level context (never on a fork branch's), and a
|
|
335
|
+
session that already completed runs nothing, so it has neither.
|
|
336
|
+
|
|
337
|
+
`carrier` is what the earlier run's observer returned from `carry(ctx)`: core calls `carry` once,
|
|
338
|
+
right after `onRunStart`, keeps its string values, and saves them as `StoredRun.carrier` (or on the
|
|
339
|
+
handle, for `run.resume()` without a store). It describes that run only — when the earlier run had
|
|
340
|
+
no observer with `carry`, `carrier` is absent rather than taken from an older run.
|
|
341
|
+
`@noetaris/harness-otel` uses this to link each run's root span to the one before it.
|
|
342
|
+
`composeObservers` merges the maps its observers return (on a shared key the later observer wins);
|
|
343
|
+
a `carry` that throws is reported to `onObserverError` with hook name `'carry'` and counts as
|
|
344
|
+
returning nothing.
|
|
345
|
+
|
|
317
346
|
### Running code inside a span
|
|
318
347
|
|
|
319
348
|
Hooks are notifications: they return before the step runs, so they cannot make a span the
|
|
@@ -387,14 +416,16 @@ The harness never trusts a wrapper. `fn` always runs exactly once, and the calle
|
|
|
387
416
|
| `runtime()` | Marks a provider slot as required at `agent.run()`. |
|
|
388
417
|
| `composeObservers([a, b], onObserverError?)` | Merges multiple `Observer` instances into one fan-out observer; a throwing observer is isolated and reported to `onObserverError`. |
|
|
389
418
|
| `SessionStore` | Interface for session persistence backends. |
|
|
390
|
-
| `StoredRun` | Type for a persisted run snapshot. Includes `agentId`, `runId`, `sessionId`, `phase`, and
|
|
419
|
+
| `StoredRun` | Type for a persisted run snapshot. Includes `agentId`, `runId`, `sessionId`, `phase`, state, and optionally `carrier` (the run's `carry` result) and `branchedFrom` (on a branch seed). |
|
|
420
|
+
| `RunRef` | `{ runId, carrier? }` — points at an earlier run; used by `RunContext.resumedFrom` and `branchedFrom` (which adds `sessionId`). |
|
|
421
|
+
| `RunCarrier` | `Readonly<Record<string, string>>` — the opaque map an observer's `carry` returns. |
|
|
391
422
|
| `Observer` | Interface for telemetry hooks on run and step lifecycle events (see [Observers](#observers)). |
|
|
392
423
|
| `StepSettledEvent` | Payload of `Observer.onStepSettled` — outcome, duration, update, signal, next, error, interrupt, fork. |
|
|
393
424
|
| `StepSettledNext` | Where the run goes after a step settles: `step`, `end`, `pause`, or `throw`. |
|
|
394
425
|
| `ObserverAware` | Interface for provider objects that want per-step telemetry: the harness calls `withTelemetry(telemetry)` once per step and installs the returned view on `ctx`. |
|
|
395
426
|
| `TelemetryContext` | What `withTelemetry` receives: `observer` (the run's observer, fault-isolated), `stepContext`, and `within(type, payload, fn)` bound to that step. |
|
|
396
427
|
| `WrapScope` | What `Observer.wrap` is asked to enclose: `{ kind: 'step' }` or `{ kind: 'event', type, payload }`. |
|
|
397
|
-
| `RunContext` | Context passed to run-level observer hooks — `agentId`, `sessionId`, `runId`, `parentRunId?`, `instanceId?`, `branchPath
|
|
428
|
+
| `RunContext` | Context passed to run-level observer hooks — `agentId`, `sessionId`, `runId`, `parentRunId?`, `instanceId?`, `branchPath?`, `resumedFrom?`, `branchedFrom?` (see [Linking runs](#linking-runs)). |
|
|
398
429
|
| `StepContext` | Context passed to step-level observer hooks — `agentId`, `sessionId`, `runId`, `stepName`, `branchPath?`. |
|
|
399
430
|
| `NoInterruptError` | Thrown when `resume()` is called but the session is not paused on a matching interrupt. |
|
|
400
431
|
| `SessionInFlightError` | Thrown when a session is already running. |
|
package/dist/index.d.ts
CHANGED
|
@@ -130,6 +130,19 @@ interface RunContext {
|
|
|
130
130
|
* from the real top-level pair. Absent for the top-level run.
|
|
131
131
|
*/
|
|
132
132
|
readonly branchPath?: readonly string[];
|
|
133
|
+
/**
|
|
134
|
+
* The run this one continues: the run that paused the session (on an interrupt, a stop or
|
|
135
|
+
* an unhandled error), with the carrier its observer produced. Present on a resumed
|
|
136
|
+
* top-level run only — never on a fork branch's context, never together with `branchedFrom`.
|
|
137
|
+
*/
|
|
138
|
+
readonly resumedFrom?: RunRef;
|
|
139
|
+
/**
|
|
140
|
+
* The run this session was branched from (`SessionStore.branch`), in the source session.
|
|
141
|
+
* Present on the branched session's first top-level run only.
|
|
142
|
+
*/
|
|
143
|
+
readonly branchedFrom?: RunRef & {
|
|
144
|
+
readonly sessionId: string;
|
|
145
|
+
};
|
|
133
146
|
}
|
|
134
147
|
/**
|
|
135
148
|
* Identifies the agent, session, and current step for step-level observer callbacks.
|
|
@@ -304,6 +317,15 @@ interface Observer {
|
|
|
304
317
|
* Not guarded: a wrapper that never settles hangs the step it encloses.
|
|
305
318
|
*/
|
|
306
319
|
wrap?: <T>(ctx: StepContext, scope: WrapScope, fn: () => Promise<T>) => Promise<T>;
|
|
320
|
+
/**
|
|
321
|
+
* Called once per top-level run, right after `onRunStart` (never for a fork branch). Return
|
|
322
|
+
* an opaque string map that lets a later run refer back to this one — e.g. the run's trace
|
|
323
|
+
* context. Core saves it on the run's record and hands it back as `RunContext.resumedFrom.carrier`
|
|
324
|
+
* (or `branchedFrom.carrier`) to the run that continues or branches from it. Return
|
|
325
|
+
* `undefined` for nothing. Only string values are kept; a throw is reported via
|
|
326
|
+
* `onObserverError` (hook name `'carry'`) and counts as `undefined`.
|
|
327
|
+
*/
|
|
328
|
+
carry?: (ctx: RunContext) => Record<string, string> | undefined;
|
|
307
329
|
}
|
|
308
330
|
/**
|
|
309
331
|
* The telemetry a resource needs to attribute an `invoke()` call to the correct
|
|
@@ -462,6 +484,18 @@ interface StoredRunMetadata {
|
|
|
462
484
|
/** Open extension point — domain-specific fields. */
|
|
463
485
|
[key: string]: unknown;
|
|
464
486
|
}
|
|
487
|
+
/**
|
|
488
|
+
* Opaque string map an observer's `carry` hook returns for a run (e.g. a trace
|
|
489
|
+
* context). Core stores it on the run's record and hands it back to later runs; it never
|
|
490
|
+
* reads it.
|
|
491
|
+
*/
|
|
492
|
+
type RunCarrier = Readonly<Record<string, string>>;
|
|
493
|
+
/** Points at one earlier run, with the carrier that run's observer produced (if any). */
|
|
494
|
+
interface RunRef {
|
|
495
|
+
readonly runId: string;
|
|
496
|
+
/** Absent when that run had no observer that carries, or its `carry` returned nothing. */
|
|
497
|
+
readonly carrier?: RunCarrier;
|
|
498
|
+
}
|
|
465
499
|
/**
|
|
466
500
|
* Persistence contract for agent sessions.
|
|
467
501
|
*
|
|
@@ -499,6 +533,12 @@ interface SessionStore {
|
|
|
499
533
|
* whose initial state equals the forked run's `finalState`.
|
|
500
534
|
* Optional — omit if your store does not support branching.
|
|
501
535
|
*
|
|
536
|
+
* Save the new session's version-0 record as a seed the next run continues: `phase: 'paused'`,
|
|
537
|
+
* no `step`, both states equal to the source run's `finalState`, and `branchedFrom` set to the
|
|
538
|
+
* source session, `runId` and the source run's `carrier` (when it has one). When the source is
|
|
539
|
+
* itself such a seed, copy its `branchedFrom` instead (or leave it out if it has none): a seed's
|
|
540
|
+
* own `runId` never ran. The seed has no `carrier` of its own.
|
|
541
|
+
*
|
|
502
542
|
* @throws {@link BranchNotFoundError} when `runId` is not found in history.
|
|
503
543
|
*/
|
|
504
544
|
branch?(agentId: string, sessionId: string, runId: string): Promise<string>;
|
|
@@ -567,6 +607,19 @@ interface StoredRun {
|
|
|
567
607
|
* domain-defined. Absent for runs produced before this field was added.
|
|
568
608
|
*/
|
|
569
609
|
readonly metadata?: StoredRunMetadata;
|
|
610
|
+
/**
|
|
611
|
+
* What the run's observer returned from `carry` (e.g. its trace context). Describes this
|
|
612
|
+
* run only. Absent when no observer carried anything.
|
|
613
|
+
*/
|
|
614
|
+
readonly carrier?: RunCarrier;
|
|
615
|
+
/**
|
|
616
|
+
* Set by `SessionStore.branch` on the seed record of a branched session: the run it was
|
|
617
|
+
* branched from, in the source session. The branched session's first run reports it as
|
|
618
|
+
* `RunContext.branchedFrom`.
|
|
619
|
+
*/
|
|
620
|
+
readonly branchedFrom?: RunRef & {
|
|
621
|
+
readonly sessionId: string;
|
|
622
|
+
};
|
|
570
623
|
}
|
|
571
624
|
/**
|
|
572
625
|
* Discriminated union returned by {@link Agent.status} describing the lifecycle
|
|
@@ -1006,6 +1059,8 @@ interface Agent {
|
|
|
1006
1059
|
*
|
|
1007
1060
|
* The optional fourth argument `resources` accepts:
|
|
1008
1061
|
* - `observer?: Observer` — structured telemetry for the resumed run (see Observability)
|
|
1062
|
+
* - `parentRunId?: string` — the caller's run id, as for `run()`; set on this resumed run's
|
|
1063
|
+
* `RunContext` only (a later `handle.resume()` on the returned handle does not inherit it)
|
|
1009
1064
|
* - `events?.onStoreError?` — raw store-error callback (unchanged from prior shape)
|
|
1010
1065
|
*/
|
|
1011
1066
|
resume(response: unknown, sessionId: string, interruptId: string, resources?: Record<string, unknown>): RunHandle;
|
|
@@ -1110,4 +1165,4 @@ declare class LeaseExpiredError extends Error {
|
|
|
1110
1165
|
constructor(sessionId: string);
|
|
1111
1166
|
}
|
|
1112
1167
|
|
|
1113
|
-
export { type Agent, type BranchCursor, type BranchDef, type ClaimOptions, type Cursor, type DeepWithMarkers, type FieldDefinition, type ForkCursor, type ForkDef, type FrameworkState, type Harness, type Lease, LeaseExpiredError, type LoopDefinition, type LoopNode, LoopNotDefinedError, NoInterruptError, type Observer, type ObserverAware, type ObserverErrorContext, type ObserverErrorSink, REQUIRED_TAG, RUNTIME_TAG, type RequiredMarker, type RouteFn, type RunContext, type RunFn, type RuntimeMarker, SessionBusyError, SessionInFlightError, SessionPendingInterruptError, type SessionStore, type SignalTransition, type StateFromSchema, type StepContext, type StepDef, type StepSettledEvent, type StepSettledNext, type StepState, StoreLoadError, type StoredRun, type StoredRunMetadata, type TelemetryContext, type TransitionTarget, type WrapScope, composeObservers, createAgent, createHarness, field, isForkCursor, isForkDef, isRequiredMarker, isRuntimeMarker, required, runtime };
|
|
1168
|
+
export { type Agent, type BranchCursor, type BranchDef, type ClaimOptions, type Cursor, type DeepWithMarkers, type FieldDefinition, type ForkCursor, type ForkDef, type FrameworkState, type Harness, type Lease, LeaseExpiredError, type LoopDefinition, type LoopNode, LoopNotDefinedError, NoInterruptError, type Observer, type ObserverAware, type ObserverErrorContext, type ObserverErrorSink, REQUIRED_TAG, RUNTIME_TAG, type RequiredMarker, type RouteFn, type RunCarrier, type RunContext, type RunFn, type RunRef, type RuntimeMarker, SessionBusyError, SessionInFlightError, SessionPendingInterruptError, type SessionStore, type SignalTransition, type StateFromSchema, type StepContext, type StepDef, type StepSettledEvent, type StepSettledNext, type StepState, StoreLoadError, type StoredRun, type StoredRunMetadata, type TelemetryContext, type TransitionTarget, type WrapScope, composeObservers, createAgent, createHarness, field, isForkCursor, isForkDef, isRequiredMarker, isRuntimeMarker, required, runtime };
|
package/dist/index.js
CHANGED
|
@@ -510,6 +510,14 @@ function safeInvoke(hookName, call, sink) {
|
|
|
510
510
|
reportObserverError(error, { hookName }, sink);
|
|
511
511
|
}
|
|
512
512
|
}
|
|
513
|
+
function safeCall(hookName, call, sink) {
|
|
514
|
+
try {
|
|
515
|
+
return call();
|
|
516
|
+
} catch (error) {
|
|
517
|
+
reportObserverError(error, { hookName }, sink);
|
|
518
|
+
return void 0;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
513
521
|
async function runWrapped(wrap, fn, onObserverError) {
|
|
514
522
|
let p;
|
|
515
523
|
const inner = () => {
|
|
@@ -556,6 +564,16 @@ function composeObservers(observers, onObserverError) {
|
|
|
556
564
|
onStepSettled: (ctx, event) => {
|
|
557
565
|
for (const o of observers) safeInvoke("onStepSettled", () => o.onStepSettled?.(ctx, event), onObserverError);
|
|
558
566
|
},
|
|
567
|
+
// Each observer isolated on its own; later keys win.
|
|
568
|
+
carry: (ctx) => {
|
|
569
|
+
let merged;
|
|
570
|
+
for (const o of observers) {
|
|
571
|
+
const carried = safeCall("carry", () => o.carry?.(ctx), onObserverError);
|
|
572
|
+
if (typeof carried !== "object" || carried === null || Array.isArray(carried)) continue;
|
|
573
|
+
merged = Object.assign(merged ?? {}, carried);
|
|
574
|
+
}
|
|
575
|
+
return merged;
|
|
576
|
+
},
|
|
559
577
|
// First observer outermost; each layer isolated on its own by runWrapped.
|
|
560
578
|
wrap: (ctx, scope, fn) => {
|
|
561
579
|
const layer = (i) => i === wrappers.length ? fn() : runWrapped((inner) => wrappers[i].wrap(ctx, scope, inner), () => layer(i + 1), onObserverError);
|
|
@@ -706,6 +724,11 @@ function stepContextOf(runCtx, stepName) {
|
|
|
706
724
|
function asError(e) {
|
|
707
725
|
return e instanceof Error ? e : new Error(String(e));
|
|
708
726
|
}
|
|
727
|
+
function normalizeCarrier(carried) {
|
|
728
|
+
if (typeof carried !== "object" || carried === null || Array.isArray(carried)) return void 0;
|
|
729
|
+
const entries = Object.entries(carried).filter(([, v]) => typeof v === "string");
|
|
730
|
+
return entries.length > 0 ? Object.freeze(Object.fromEntries(entries)) : void 0;
|
|
731
|
+
}
|
|
709
732
|
async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callbacks) {
|
|
710
733
|
const { onBeforeStep, onAfterStep, onError, onComplete, onInterrupt } = callbacks ?? {};
|
|
711
734
|
const obs = callbacks?.observer;
|
|
@@ -715,15 +738,22 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
|
|
|
715
738
|
if (!("$interrupt" in state)) state.$interrupt = null;
|
|
716
739
|
if (!("$interruptResponses" in state)) state.$interruptResponses = {};
|
|
717
740
|
const runStart = Date.now();
|
|
741
|
+
const branchPath = callbacks?.branchPath;
|
|
742
|
+
const isBranch = branchPath !== void 0 && branchPath.length > 0;
|
|
718
743
|
const runCtx = {
|
|
719
744
|
agentId: ctx.agentId,
|
|
720
745
|
sessionId: ctx.sessionId,
|
|
721
746
|
runId: typeof callbacks?.runId === "string" ? callbacks.runId : "",
|
|
722
747
|
...typeof callbacks?.parentRunId === "string" ? { parentRunId: callbacks.parentRunId } : {},
|
|
723
748
|
...typeof ctx.instanceId === "string" ? { instanceId: ctx.instanceId } : {},
|
|
724
|
-
...
|
|
749
|
+
...isBranch ? { branchPath } : {},
|
|
750
|
+
// Branch runLoops receive the outer callbacks by spread, so the refs are dropped here.
|
|
751
|
+
...!isBranch && callbacks?.resumedFrom !== void 0 ? { resumedFrom: callbacks.resumedFrom } : {},
|
|
752
|
+
...!isBranch && callbacks?.branchedFrom !== void 0 ? { branchedFrom: callbacks.branchedFrom } : {}
|
|
725
753
|
};
|
|
726
754
|
safeInvoke("onRunStart", () => obs?.onRunStart?.(runCtx), onObserverError);
|
|
755
|
+
const carrier = isBranch ? void 0 : normalizeCarrier(safeCall("carry", () => obs?.carry?.(runCtx), onObserverError));
|
|
756
|
+
const withCarrier = (result) => carrier !== void 0 ? { ...result, carrier } : result;
|
|
727
757
|
let runEnded = false;
|
|
728
758
|
const endRun = (signal, failure) => {
|
|
729
759
|
runEnded = true;
|
|
@@ -741,7 +771,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
|
|
|
741
771
|
while (true) {
|
|
742
772
|
if (shouldStop?.()) {
|
|
743
773
|
endRun("$stopped");
|
|
744
|
-
return { state, signal: null, cursor, paused: true };
|
|
774
|
+
return withCarrier({ state, signal: null, cursor, paused: true });
|
|
745
775
|
}
|
|
746
776
|
if (isForkCursor(cursor)) {
|
|
747
777
|
const forkCursor = cursor;
|
|
@@ -765,7 +795,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
|
|
|
765
795
|
graph
|
|
766
796
|
);
|
|
767
797
|
if (outcome.done) {
|
|
768
|
-
return outcome.result;
|
|
798
|
+
return withCarrier(outcome.result);
|
|
769
799
|
}
|
|
770
800
|
cursor = outcome.nextCursor;
|
|
771
801
|
continue;
|
|
@@ -788,7 +818,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
|
|
|
788
818
|
graph
|
|
789
819
|
);
|
|
790
820
|
if (outcome.done) {
|
|
791
|
-
return outcome.result;
|
|
821
|
+
return withCarrier(outcome.result);
|
|
792
822
|
}
|
|
793
823
|
cursor = outcome.nextCursor;
|
|
794
824
|
continue;
|
|
@@ -834,7 +864,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
|
|
|
834
864
|
safeInvoke("onInterrupt", () => obs?.onInterrupt?.(stepCtx, { prompt: e.prompt, interruptId: e.interruptId }), onObserverError);
|
|
835
865
|
settle({ outcome: "interrupt", interrupt: { interruptId: e.interruptId, prompt: e.prompt }, next: { kind: "pause" } });
|
|
836
866
|
endRun("$interrupt");
|
|
837
|
-
return { state, signal: "$interrupt", cursor, paused: true };
|
|
867
|
+
return withCarrier({ state, signal: "$interrupt", cursor, paused: true });
|
|
838
868
|
}
|
|
839
869
|
state.$error = e instanceof Error ? e : new Error(String(e));
|
|
840
870
|
onError?.(state.$error, cursor);
|
|
@@ -856,7 +886,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
|
|
|
856
886
|
state.$error = e instanceof Error ? e : new Error(String(e));
|
|
857
887
|
settle({ outcome: "error", error: state.$error, next: { kind: "pause" } });
|
|
858
888
|
endRun("$error", { error: state.$error });
|
|
859
|
-
return { state, signal: "$error", cursor, paused: true };
|
|
889
|
+
return withCarrier({ state, signal: "$error", cursor, paused: true });
|
|
860
890
|
}
|
|
861
891
|
const routed = runSucceeded || step.run === void 0 ? { outcome: "ok", signal } : { outcome: "error", signal, error: state.$error };
|
|
862
892
|
const transition = step.transitions.find((t) => t.signal === signal);
|
|
@@ -869,7 +899,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
|
|
|
869
899
|
settle({ ...routed, next: { kind: "end" } });
|
|
870
900
|
onComplete?.(state, signal);
|
|
871
901
|
endRun(signal);
|
|
872
|
-
return { state, signal, cursor: null, paused: false };
|
|
902
|
+
return withCarrier({ state, signal, cursor: null, paused: false });
|
|
873
903
|
}
|
|
874
904
|
settle({ ...routed, next: { kind: "step", name: transition.target.name } });
|
|
875
905
|
cursor = transition.target.name;
|
|
@@ -880,7 +910,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
|
|
|
880
910
|
} else {
|
|
881
911
|
settle({ outcome: "error", error: state.$error, next: { kind: "pause" } });
|
|
882
912
|
endRun("$error", { error: state.$error });
|
|
883
|
-
return { state, signal: "$error", cursor, paused: true };
|
|
913
|
+
return withCarrier({ state, signal: "$error", cursor, paused: true });
|
|
884
914
|
}
|
|
885
915
|
} else {
|
|
886
916
|
const next = step.next ?? implicitNextMap.get(cursor) ?? null;
|
|
@@ -1249,6 +1279,13 @@ async function querySessionPhase(store, agentId, sessionId) {
|
|
|
1249
1279
|
const loaded = await store.load(agentId, sessionId);
|
|
1250
1280
|
return storedSessionToPhase(loaded);
|
|
1251
1281
|
}
|
|
1282
|
+
function runRefsOf(loaded) {
|
|
1283
|
+
if (loaded === null || loaded.phase !== "paused") return {};
|
|
1284
|
+
if (loaded.step !== void 0) {
|
|
1285
|
+
return { resumedFrom: { runId: loaded.runId, ...loaded.carrier !== void 0 ? { carrier: loaded.carrier } : {} } };
|
|
1286
|
+
}
|
|
1287
|
+
return loaded.branchedFrom !== void 0 ? { branchedFrom: loaded.branchedFrom } : {};
|
|
1288
|
+
}
|
|
1252
1289
|
async function runWithSession(store, agentId, sessionId, runId, graph, initialStateArg, schema, ctx, options) {
|
|
1253
1290
|
const leaseRef = options?.leaseRef ?? { current: null };
|
|
1254
1291
|
const keepAlive = createKeepAliveFn(leaseRef, store, options?.claimTtlMs ?? 3e4);
|
|
@@ -1300,7 +1337,8 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
|
|
|
1300
1337
|
const result = await runLoop(graph, state, ctx, schema, composedShouldStop, loaded?.step, {
|
|
1301
1338
|
...options,
|
|
1302
1339
|
runId,
|
|
1303
|
-
...options?.parentRunId !== void 0 ? { parentRunId: options.parentRunId } : {}
|
|
1340
|
+
...options?.parentRunId !== void 0 ? { parentRunId: options.parentRunId } : {},
|
|
1341
|
+
...runRefsOf(loaded)
|
|
1304
1342
|
});
|
|
1305
1343
|
if (leaseExpired) {
|
|
1306
1344
|
const expiredError = new LeaseExpiredError(sessionId);
|
|
@@ -1324,7 +1362,8 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
|
|
|
1324
1362
|
finalState: result.state,
|
|
1325
1363
|
step: result.cursor,
|
|
1326
1364
|
...result.signal !== null ? { signal: result.signal } : {},
|
|
1327
|
-
...metadata !== void 0 ? { metadata } : {}
|
|
1365
|
+
...metadata !== void 0 ? { metadata } : {},
|
|
1366
|
+
...result.carrier !== void 0 ? { carrier: result.carrier } : {}
|
|
1328
1367
|
};
|
|
1329
1368
|
await store.save(agentId, sessionId, saved);
|
|
1330
1369
|
} else {
|
|
@@ -1339,7 +1378,8 @@ async function runWithSession(store, agentId, sessionId, runId, graph, initialSt
|
|
|
1339
1378
|
initialState: initialStateSnapshot,
|
|
1340
1379
|
finalState: result.state,
|
|
1341
1380
|
...result.signal !== null ? { signal: result.signal } : {},
|
|
1342
|
-
...metadata !== void 0 ? { metadata } : {}
|
|
1381
|
+
...metadata !== void 0 ? { metadata } : {},
|
|
1382
|
+
...result.carrier !== void 0 ? { carrier: result.carrier } : {}
|
|
1343
1383
|
};
|
|
1344
1384
|
await store.save(agentId, sessionId, saved);
|
|
1345
1385
|
}
|
|
@@ -1416,7 +1456,10 @@ async function injectInterruptResponse(store, agentId, sessionId, interruptId, r
|
|
|
1416
1456
|
initialState: loaded.initialState,
|
|
1417
1457
|
finalState: result.state,
|
|
1418
1458
|
...result.cursor !== void 0 ? { step: result.cursor } : {},
|
|
1419
|
-
...loaded.signal !== void 0 ? { signal: loaded.signal } : {}
|
|
1459
|
+
...loaded.signal !== void 0 ? { signal: loaded.signal } : {},
|
|
1460
|
+
...loaded.metadata !== void 0 ? { metadata: loaded.metadata } : {},
|
|
1461
|
+
...loaded.carrier !== void 0 ? { carrier: loaded.carrier } : {},
|
|
1462
|
+
...loaded.branchedFrom !== void 0 ? { branchedFrom: loaded.branchedFrom } : {}
|
|
1420
1463
|
};
|
|
1421
1464
|
await store.save(agentId, sessionId, updated);
|
|
1422
1465
|
}
|
|
@@ -1661,7 +1704,8 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1661
1704
|
const reservedRunKeys = /* @__PURE__ */ new Set(["sessionId", "signal", "events", "listeners", "observer", "onObserverError", "claimOptions", "parentRunId"]);
|
|
1662
1705
|
const inFlightSessions = /* @__PURE__ */ new Set();
|
|
1663
1706
|
const interruptPendingSessions = /* @__PURE__ */ new Set();
|
|
1664
|
-
const makeAgentResumeHandle = (resp, sId, iId,
|
|
1707
|
+
const makeAgentResumeHandle = (resp, sId, iId, resumeResources = {}) => {
|
|
1708
|
+
const { onStoreError, observer, onObserverError, telemetrySlots, parentRunId } = resumeResources;
|
|
1665
1709
|
let _stopped = false;
|
|
1666
1710
|
const abortController = new AbortController();
|
|
1667
1711
|
const flag = {
|
|
@@ -1686,14 +1730,14 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1686
1730
|
lease = await capturedStore.claim(id, sId, claimOptions);
|
|
1687
1731
|
} catch (claimError) {
|
|
1688
1732
|
const loadError = new StoreLoadError(claimError);
|
|
1689
|
-
|
|
1733
|
+
onStoreError?.(loadError, "claim");
|
|
1690
1734
|
const failState = initializeState(null, {}, agentInternals.stateSchema);
|
|
1691
1735
|
failState["$error"] = loadError;
|
|
1692
1736
|
return { state: failState, signal: "$error" };
|
|
1693
1737
|
}
|
|
1694
1738
|
if (lease === null) {
|
|
1695
1739
|
const busyError = new SessionBusyError(sId);
|
|
1696
|
-
|
|
1740
|
+
onStoreError?.(busyError, "claim");
|
|
1697
1741
|
const failState = initializeState(null, {}, agentInternals.stateSchema);
|
|
1698
1742
|
failState["$error"] = busyError;
|
|
1699
1743
|
return { state: failState, signal: "$error" };
|
|
@@ -1707,7 +1751,7 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1707
1751
|
} catch (injectError) {
|
|
1708
1752
|
if (injectError instanceof NoInterruptError) throw injectError;
|
|
1709
1753
|
const storeError = new StoreLoadError(injectError);
|
|
1710
|
-
|
|
1754
|
+
onStoreError?.(storeError, "claim");
|
|
1711
1755
|
const failState = initializeState(null, {}, agentInternals.stateSchema);
|
|
1712
1756
|
failState["$error"] = storeError;
|
|
1713
1757
|
return { state: failState, signal: "$error" };
|
|
@@ -1717,7 +1761,8 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1717
1761
|
agentId: id,
|
|
1718
1762
|
sessionId: sId,
|
|
1719
1763
|
runId: rId,
|
|
1720
|
-
signal: abortController.signal
|
|
1764
|
+
signal: abortController.signal,
|
|
1765
|
+
...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {}
|
|
1721
1766
|
};
|
|
1722
1767
|
const r = await runWithSession(
|
|
1723
1768
|
capturedStore,
|
|
@@ -1733,11 +1778,13 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1733
1778
|
onBeforeStep: (n) => {
|
|
1734
1779
|
ref.current = n;
|
|
1735
1780
|
},
|
|
1736
|
-
...
|
|
1781
|
+
...onStoreError !== void 0 ? { onStoreError } : {},
|
|
1737
1782
|
...leaseRef !== void 0 ? { leaseRef, claimTtlMs: claimOptions.ttlMs } : {},
|
|
1738
1783
|
...observer !== void 0 ? { observer } : {},
|
|
1739
1784
|
...onObserverError !== void 0 ? { onObserverError } : {},
|
|
1740
|
-
...telemetrySlots !== void 0 && telemetrySlots.length > 0 ? { telemetrySlots } : {}
|
|
1785
|
+
...telemetrySlots !== void 0 && telemetrySlots.length > 0 ? { telemetrySlots } : {},
|
|
1786
|
+
...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {},
|
|
1787
|
+
...parentRunId !== void 0 ? { parentRunId } : {}
|
|
1741
1788
|
}
|
|
1742
1789
|
);
|
|
1743
1790
|
if (r.signal === "$interrupt") interruptPendingSessions.add(sId);
|
|
@@ -1765,7 +1812,8 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1765
1812
|
if (inFlightSessions.has(sId)) throw new SessionInFlightError(sId);
|
|
1766
1813
|
interruptPendingSessions.delete(sId);
|
|
1767
1814
|
inFlightSessions.add(sId);
|
|
1768
|
-
|
|
1815
|
+
const { parentRunId: _staleParentRunId, ...inherited } = resumeResources;
|
|
1816
|
+
return makeAgentResumeHandle(r, sId, i, inherited);
|
|
1769
1817
|
};
|
|
1770
1818
|
return createRunHandle(sId, rId, exec, flag, ref, resumeFn);
|
|
1771
1819
|
};
|
|
@@ -1901,7 +1949,7 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1901
1949
|
}
|
|
1902
1950
|
);
|
|
1903
1951
|
if (r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
|
|
1904
|
-
lastResult = { state: r.state, cursor: r.cursor };
|
|
1952
|
+
lastResult = { state: r.state, cursor: r.cursor, runId, ...r.carrier !== void 0 ? { carrier: r.carrier } : {} };
|
|
1905
1953
|
return { state: r.state, signal: r.signal };
|
|
1906
1954
|
} catch (error) {
|
|
1907
1955
|
if (error instanceof LeaseExpiredError) {
|
|
@@ -1974,11 +2022,12 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1974
2022
|
...Object.keys(listeners).length > 0 ? { listeners } : {},
|
|
1975
2023
|
...observer !== void 0 ? { observer } : {},
|
|
1976
2024
|
...onObserverError !== void 0 ? { onObserverError } : {},
|
|
1977
|
-
...telemetrySlots.length > 0 ? { telemetrySlots } : {}
|
|
2025
|
+
...telemetrySlots.length > 0 ? { telemetrySlots } : {},
|
|
2026
|
+
...agentOptions?.instanceId !== void 0 ? { instanceId: agentOptions.instanceId } : {}
|
|
1978
2027
|
}
|
|
1979
2028
|
);
|
|
1980
2029
|
if (r2.signal === "$interrupt") interruptPendingSessions.add(sessionId);
|
|
1981
|
-
lastResult = { state: r2.state, cursor: r2.cursor };
|
|
2030
|
+
lastResult = { state: r2.state, cursor: r2.cursor, runId: resumeRunId, ...r2.carrier !== void 0 ? { carrier: r2.carrier } : {} };
|
|
1982
2031
|
return { state: r2.state, signal: r2.signal };
|
|
1983
2032
|
}
|
|
1984
2033
|
const prev = lastResult;
|
|
@@ -1996,6 +2045,7 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
1996
2045
|
cursor,
|
|
1997
2046
|
{
|
|
1998
2047
|
runId: resumeRunId,
|
|
2048
|
+
resumedFrom: { runId: prev.runId, ...prev.carrier !== void 0 ? { carrier: prev.carrier } : {} },
|
|
1999
2049
|
onBeforeStep: (n, s) => {
|
|
2000
2050
|
resumeStepRef.current = n;
|
|
2001
2051
|
events.onBeforeStep?.(n, s);
|
|
@@ -2011,7 +2061,7 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
2011
2061
|
}
|
|
2012
2062
|
);
|
|
2013
2063
|
if (r.signal === "$interrupt") interruptPendingSessions.add(sessionId);
|
|
2014
|
-
lastResult = { state: r.state, cursor: r.cursor };
|
|
2064
|
+
lastResult = { state: r.state, cursor: r.cursor, runId: resumeRunId, ...r.carrier !== void 0 ? { carrier: r.carrier } : {} };
|
|
2015
2065
|
return { state: r.state, signal: r.signal };
|
|
2016
2066
|
} catch (error) {
|
|
2017
2067
|
if (error instanceof LeaseExpiredError) {
|
|
@@ -2034,11 +2084,17 @@ function createAgent(id, h, slots, agentOptions) {
|
|
|
2034
2084
|
interruptPendingSessions.delete(sessionId);
|
|
2035
2085
|
inFlightSessions.add(sessionId);
|
|
2036
2086
|
const events = extractRunEvents(resources ?? {});
|
|
2037
|
-
const resumeOpts = events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : void 0;
|
|
2038
2087
|
const observer = extractRunObserver(resources ?? {});
|
|
2039
2088
|
const onObserverError = extractRunErrorSink(resources ?? {});
|
|
2040
2089
|
const telemetrySlots = buildTelemetrySlots(agentInternals.resolvedProviders.entries());
|
|
2041
|
-
|
|
2090
|
+
const parentRunId = resources?.["parentRunId"];
|
|
2091
|
+
return makeAgentResumeHandle(response, sessionId, interruptId, {
|
|
2092
|
+
...events.onStoreError !== void 0 ? { onStoreError: events.onStoreError } : {},
|
|
2093
|
+
...observer !== void 0 ? { observer } : {},
|
|
2094
|
+
...onObserverError !== void 0 ? { onObserverError } : {},
|
|
2095
|
+
...telemetrySlots.length > 0 ? { telemetrySlots } : {},
|
|
2096
|
+
...typeof parentRunId === "string" ? { parentRunId } : {}
|
|
2097
|
+
});
|
|
2042
2098
|
},
|
|
2043
2099
|
status: (sessionId) => querySessionPhase(capturedStore, id, sessionId),
|
|
2044
2100
|
[_agentInternals]: agentInternals
|