@noetaris/harness 0.8.0 → 0.10.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 +35 -3
- package/dist/index.d.ts +65 -1
- package/dist/index.js +286 -188
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -156,6 +156,14 @@ 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 }`, `parentRunId` (the caller's run id,
|
|
161
|
+
for a sub agent resumed from a step), and `claimOptions`. `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` and, when the store supports claims, claim the
|
|
165
|
+
session for the resumed run; a `resume()` on the returned handle reuses the same claim TTL.
|
|
166
|
+
|
|
159
167
|
**Resume replays the interrupted step from the top.** On resume, the step that called
|
|
160
168
|
`ctx.interrupt()` runs again from its first line; each `ctx.interrupt()` call it reaches
|
|
161
169
|
returns the stored response instead of pausing. State updates from the paused attempt are
|
|
@@ -263,6 +271,7 @@ throws on an adapter event never fails a step. Every hook is optional.
|
|
|
263
271
|
| `onEvent(ctx, type, payload)` | on `ctx.emit()` and adapter events such as `llm.response` |
|
|
264
272
|
| `onStepSettled(ctx, event)` | once per step, when its outcome and destination are final |
|
|
265
273
|
| `wrap(ctx, scope, fn)` | around a step's `run`, and around calls made with `ctx.within` |
|
|
274
|
+
| `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
275
|
|
|
267
276
|
Step hooks receive a `StepContext`: `agentId`, `sessionId`, `runId`, `stepName` and, inside a
|
|
268
277
|
fork branch, `branchPath`. `runId` is the same value as `RunContext.runId`. Steps inside a branch
|
|
@@ -314,6 +323,27 @@ await createAgent('demo', h, {}).run({}, { observer: logger })
|
|
|
314
323
|
// decide: ok end (signal done)
|
|
315
324
|
```
|
|
316
325
|
|
|
326
|
+
### Linking runs
|
|
327
|
+
|
|
328
|
+
Some runs continue an earlier one. Their `RunContext` says which, so an observer can link them:
|
|
329
|
+
|
|
330
|
+
| The run is | `RunContext` field |
|
|
331
|
+
|---|---|
|
|
332
|
+
| 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 |
|
|
333
|
+
| the first run of a session created by `SessionStore.branch()` | `branchedFrom: { sessionId, runId, carrier? }` — the run it was branched from, in the source session |
|
|
334
|
+
|
|
335
|
+
A run has at most one of the two, only on its top-level context (never on a fork branch's), and a
|
|
336
|
+
session that already completed runs nothing, so it has neither.
|
|
337
|
+
|
|
338
|
+
`carrier` is what the earlier run's observer returned from `carry(ctx)`: core calls `carry` once,
|
|
339
|
+
right after `onRunStart`, keeps its string values, and saves them as `StoredRun.carrier` (or on the
|
|
340
|
+
handle, for `run.resume()` without a store). It describes that run only — when the earlier run had
|
|
341
|
+
no observer with `carry`, `carrier` is absent rather than taken from an older run.
|
|
342
|
+
`@noetaris/harness-otel` uses this to link each run's root span to the one before it.
|
|
343
|
+
`composeObservers` merges the maps its observers return (on a shared key the later observer wins);
|
|
344
|
+
a `carry` that throws is reported to `onObserverError` with hook name `'carry'` and counts as
|
|
345
|
+
returning nothing.
|
|
346
|
+
|
|
317
347
|
### Running code inside a span
|
|
318
348
|
|
|
319
349
|
Hooks are notifications: they return before the step runs, so they cannot make a span the
|
|
@@ -381,20 +411,22 @@ The harness never trusts a wrapper. `fn` always runs exactly once, and the calle
|
|
|
381
411
|
| Export | Description |
|
|
382
412
|
|---|---|
|
|
383
413
|
| `createHarness<Ctx>()(schema)` | Creates a harness. Fixes `Ctx`, infers `State` from schema. |
|
|
384
|
-
| `createAgent(id, h, slots)` | Assigns the agent an ID and fills `required()` slots. Returns an `Agent`. |
|
|
414
|
+
| `createAgent(id, h, slots, options?)` | Assigns the agent an ID and fills `required()` slots. `options` (`AgentOptions`) takes `instanceId`, `stores` (for `required()` store slots) and `claimOptions` (this agent's default claim TTL; per-call `claimOptions` wins, then `HARNESS_CLAIM_TTL_MS`, then 30 000 ms). Returns an `Agent`. |
|
|
385
415
|
| `field<T>(opts)` | Declares a state field with a default and optional reduce function. |
|
|
386
416
|
| `required()` | Marks a provider slot as required at `createAgent()`. |
|
|
387
417
|
| `runtime()` | Marks a provider slot as required at `agent.run()`. |
|
|
388
418
|
| `composeObservers([a, b], onObserverError?)` | Merges multiple `Observer` instances into one fan-out observer; a throwing observer is isolated and reported to `onObserverError`. |
|
|
389
419
|
| `SessionStore` | Interface for session persistence backends. |
|
|
390
|
-
| `StoredRun` | Type for a persisted run snapshot. Includes `agentId`, `runId`, `sessionId`, `phase`, and
|
|
420
|
+
| `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). |
|
|
421
|
+
| `RunRef` | `{ runId, carrier? }` — points at an earlier run; used by `RunContext.resumedFrom` and `branchedFrom` (which adds `sessionId`). |
|
|
422
|
+
| `RunCarrier` | `Readonly<Record<string, string>>` — the opaque map an observer's `carry` returns. |
|
|
391
423
|
| `Observer` | Interface for telemetry hooks on run and step lifecycle events (see [Observers](#observers)). |
|
|
392
424
|
| `StepSettledEvent` | Payload of `Observer.onStepSettled` — outcome, duration, update, signal, next, error, interrupt, fork. |
|
|
393
425
|
| `StepSettledNext` | Where the run goes after a step settles: `step`, `end`, `pause`, or `throw`. |
|
|
394
426
|
| `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
427
|
| `TelemetryContext` | What `withTelemetry` receives: `observer` (the run's observer, fault-isolated), `stepContext`, and `within(type, payload, fn)` bound to that step. |
|
|
396
428
|
| `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
|
|
429
|
+
| `RunContext` | Context passed to run-level observer hooks — `agentId`, `sessionId`, `runId`, `parentRunId?`, `instanceId?`, `branchPath?`, `resumedFrom?`, `branchedFrom?` (see [Linking runs](#linking-runs)). |
|
|
398
430
|
| `StepContext` | Context passed to step-level observer hooks — `agentId`, `sessionId`, `runId`, `stepName`, `branchPath?`. |
|
|
399
431
|
| `NoInterruptError` | Thrown when `resume()` is called but the session is not paused on a matching interrupt. |
|
|
400
432
|
| `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
|
|
@@ -992,6 +1045,12 @@ interface AgentOptions {
|
|
|
992
1045
|
* Keyed by the store key used in the harness (e.g. `{ session: myStore }`).
|
|
993
1046
|
*/
|
|
994
1047
|
readonly stores?: Record<string, unknown>;
|
|
1048
|
+
/**
|
|
1049
|
+
* Default claim options for every run and resume of this agent, used when the call passes none.
|
|
1050
|
+
* Resolution: per-call `claimOptions` → this → `HARNESS_CLAIM_TTL_MS` → 30 000 ms.
|
|
1051
|
+
* `ttlMs` must be a finite number greater than 0; otherwise `createAgent` throws `InvalidClaimOptionsError`.
|
|
1052
|
+
*/
|
|
1053
|
+
readonly claimOptions?: ClaimOptions;
|
|
995
1054
|
}
|
|
996
1055
|
interface Agent {
|
|
997
1056
|
/** The agent's unique identifier, as provided to createAgent(). */
|
|
@@ -1006,7 +1065,12 @@ interface Agent {
|
|
|
1006
1065
|
*
|
|
1007
1066
|
* The optional fourth argument `resources` accepts:
|
|
1008
1067
|
* - `observer?: Observer` — structured telemetry for the resumed run (see Observability)
|
|
1068
|
+
* - `parentRunId?: string` — the caller's run id, as for `run()`; set on this resumed run's
|
|
1069
|
+
* `RunContext` only (a later `handle.resume()` on the returned handle does not inherit it)
|
|
1009
1070
|
* - `events?.onStoreError?` — raw store-error callback (unchanged from prior shape)
|
|
1071
|
+
* - `claimOptions?: ClaimOptions` — claim TTL for this resume, parsed like `agent.run()`'s; falls back
|
|
1072
|
+
* to `AgentOptions.claimOptions`, then `HARNESS_CLAIM_TTL_MS`, then 30 000 ms. A later `handle.resume()`
|
|
1073
|
+
* on the returned handle reuses it
|
|
1010
1074
|
*/
|
|
1011
1075
|
resume(response: unknown, sessionId: string, interruptId: string, resources?: Record<string, unknown>): RunHandle;
|
|
1012
1076
|
/**
|
|
@@ -1110,4 +1174,4 @@ declare class LeaseExpiredError extends Error {
|
|
|
1110
1174
|
constructor(sessionId: string);
|
|
1111
1175
|
}
|
|
1112
1176
|
|
|
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 };
|
|
1177
|
+
export { type Agent, type AgentOptions, 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 };
|