@noetaris/harness 0.3.2 → 0.5.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 +8 -3
- package/dist/index.d.ts +269 -125
- package/dist/index.js +535 -151
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -124,17 +124,20 @@ h.provide('model', runtime()) // per-run — supplied at agent.run
|
|
|
124
124
|
|
|
125
125
|
### Session Store
|
|
126
126
|
|
|
127
|
-
`h.store()` adds persistence. The reserved `session` key is used by the framework to save and restore state across runs
|
|
127
|
+
`h.store()` adds persistence. The reserved `session` key is used by the framework to save and restore state across runs.
|
|
128
128
|
|
|
129
129
|
```ts
|
|
130
130
|
import { InMemorySessionStore } from '@noetaris/harness-store'
|
|
131
131
|
|
|
132
132
|
h.store({
|
|
133
|
-
session:
|
|
134
|
-
knowledge: new MyKnowledgeGraph(), // available as ctx.store.knowledge
|
|
133
|
+
session: new InMemorySessionStore(), // framework-managed lifecycle
|
|
135
134
|
})
|
|
136
135
|
```
|
|
137
136
|
|
|
137
|
+
> **Note:** Only the `session` key is processed today. Surfacing additional
|
|
138
|
+
> store keys to steps as `ctx.store.<name>` is planned but not yet wired up —
|
|
139
|
+
> non-session keys passed to `h.store()` are currently ignored.
|
|
140
|
+
|
|
138
141
|
The framework injects `ctx.sessionId` automatically on every run — no declaration in `Ctx` needed.
|
|
139
142
|
|
|
140
143
|
### Interrupts
|
|
@@ -187,6 +190,8 @@ const resumed = agent.resume(response, sessionId, interruptId)
|
|
|
187
190
|
- ESM only (`"type": "module"`)
|
|
188
191
|
- Zero runtime dependencies
|
|
189
192
|
|
|
193
|
+
See [LIMITATIONS.md](LIMITATIONS.md) for known limitations.
|
|
194
|
+
|
|
190
195
|
## Related Packages
|
|
191
196
|
|
|
192
197
|
- [`@noetaris/harness-store`](https://github.com/noetaris-lab/harness-store) — session store implementations (`InMemorySessionStore`, `LocalFileSessionStore`, etc.)
|
package/dist/index.d.ts
CHANGED
|
@@ -100,6 +100,189 @@ declare function isRequiredMarker(value: unknown): value is RequiredMarker;
|
|
|
100
100
|
*/
|
|
101
101
|
declare function isRuntimeMarker(value: unknown): value is RuntimeMarker;
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Identifies the agent and session for run-level observer callbacks.
|
|
105
|
+
*/
|
|
106
|
+
interface RunContext {
|
|
107
|
+
readonly agentId: string;
|
|
108
|
+
readonly sessionId: string;
|
|
109
|
+
/**
|
|
110
|
+
* The UUID for this specific run invocation. Always present in RunContext
|
|
111
|
+
* passed to observer methods.
|
|
112
|
+
*/
|
|
113
|
+
readonly runId: string;
|
|
114
|
+
/**
|
|
115
|
+
* Optional parent run UUID for cross-process trace correlation. Present
|
|
116
|
+
* when parentRunId was passed to agent.run() resources. Absent for top-level
|
|
117
|
+
* runs or when not provided by the caller.
|
|
118
|
+
*/
|
|
119
|
+
readonly parentRunId?: string;
|
|
120
|
+
/**
|
|
121
|
+
* The physical instance ID of the agent process, if configured via
|
|
122
|
+
* `createAgent()` options. Absent when `instanceId` was not provided.
|
|
123
|
+
*/
|
|
124
|
+
readonly instanceId?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Branch-name path (outermost to innermost), present only when this context describes a
|
|
127
|
+
* fork branch's own nested run rather than the top-level run. A branch is implemented as
|
|
128
|
+
* its own recursive runLoop call, so it fires onRunStart/onRunEnd on the same observer as
|
|
129
|
+
* the top-level run, under the same runId — this field is what makes those distinguishable
|
|
130
|
+
* from the real top-level pair. Absent for the top-level run.
|
|
131
|
+
*/
|
|
132
|
+
readonly branchPath?: readonly string[];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Identifies the agent, session, and current step for step-level observer callbacks.
|
|
136
|
+
*/
|
|
137
|
+
interface StepContext {
|
|
138
|
+
readonly agentId: string;
|
|
139
|
+
readonly sessionId: string;
|
|
140
|
+
readonly stepName: string;
|
|
141
|
+
/**
|
|
142
|
+
* Branch-name path (outermost to innermost), present only when this step runs inside a
|
|
143
|
+
* fork branch. Absent for a step at the top level of the loop (including the fork node
|
|
144
|
+
* itself, which is one atomic step from the outer run's point of view).
|
|
145
|
+
*/
|
|
146
|
+
readonly branchPath?: readonly string[];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Observability hook interface. All methods are optional — implement only
|
|
150
|
+
* the hooks you need.
|
|
151
|
+
*
|
|
152
|
+
* Pass an `Observer` implementation in `agent.run()` resources under the key
|
|
153
|
+
* `'observer'`, or bind it to an {@link ObserverAware} slot before running.
|
|
154
|
+
*
|
|
155
|
+
* LLM adapters emit `'llm.response'` events via `onEvent` carrying an
|
|
156
|
+
* `LLMUsageEvent` payload for token tracking.
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* const obs: Observer = {
|
|
161
|
+
* onRunStart: (ctx) => console.log('run started', ctx.sessionId),
|
|
162
|
+
* onEvent: (ctx, type, payload) => metrics.record(type, payload),
|
|
163
|
+
* }
|
|
164
|
+
* agent.run({}, { llm, observer: obs })
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
interface Observer {
|
|
168
|
+
/** Called once when a run begins, before the first step executes. */
|
|
169
|
+
onRunStart?: (ctx: RunContext) => void;
|
|
170
|
+
/** Called once when a run settles (completed or stopped). */
|
|
171
|
+
onRunEnd?: (ctx: RunContext, event: {
|
|
172
|
+
signal: string;
|
|
173
|
+
durationMs: number;
|
|
174
|
+
}) => void;
|
|
175
|
+
/** Called immediately before each step's `run` function is invoked. */
|
|
176
|
+
onStepStart?: (ctx: StepContext) => void;
|
|
177
|
+
/** Called after a step completes successfully. */
|
|
178
|
+
onStepEnd?: (ctx: StepContext, event: {
|
|
179
|
+
durationMs: number;
|
|
180
|
+
}) => void;
|
|
181
|
+
/** Called when a step's `run` function throws. */
|
|
182
|
+
onStepError?: (ctx: StepContext, event: {
|
|
183
|
+
error: unknown;
|
|
184
|
+
durationMs: number;
|
|
185
|
+
}) => void;
|
|
186
|
+
/** Called when a step issues a `ctx.interrupt()`. */
|
|
187
|
+
onInterrupt?: (ctx: StepContext, event: {
|
|
188
|
+
prompt: unknown;
|
|
189
|
+
interruptId: string;
|
|
190
|
+
}) => void;
|
|
191
|
+
/**
|
|
192
|
+
* Called for arbitrary named events emitted by step code via `ctx.emit()` or
|
|
193
|
+
* by LLM adapters (e.g. `'llm.response'`).
|
|
194
|
+
*/
|
|
195
|
+
onEvent?: (ctx: StepContext, type: string, payload: unknown) => void;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* The telemetry a resource needs to attribute an `invoke()` call to the correct
|
|
199
|
+
* run and step: the run's {@link Observer} and the current {@link StepContext}.
|
|
200
|
+
*
|
|
201
|
+
* The harness passes this per call rather than storing it on the resource instance,
|
|
202
|
+
* so concurrently-running fork branches that share one instance never race on it —
|
|
203
|
+
* each call carries its own context by value.
|
|
204
|
+
*/
|
|
205
|
+
interface TelemetryContext {
|
|
206
|
+
readonly observer: Observer;
|
|
207
|
+
readonly stepContext: StepContext;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Implemented by resources (e.g. LLM adapters) that emit per-step telemetry.
|
|
211
|
+
*
|
|
212
|
+
* The harness calls {@link ObserverAware.withTelemetry} at the start of each step for every
|
|
213
|
+
* slot that exposes it, and installs the returned value as that slot on the step's `ctx`.
|
|
214
|
+
* Step code then calls the returned view (e.g. `ctx.llm.invoke(...)`) with no telemetry
|
|
215
|
+
* arguments, and the view forwards the {@link TelemetryContext} into the call it wraps — so
|
|
216
|
+
* telemetry travels *with the call* rather than living in mutable instance state.
|
|
217
|
+
*
|
|
218
|
+
* The return is typed `unknown` because core exports no LLM-domain type; a concrete adapter
|
|
219
|
+
* narrows it (e.g. to its `LLM` view). The harness treats the returned value as an opaque
|
|
220
|
+
* slot, exactly as it treats every provided slot.
|
|
221
|
+
*/
|
|
222
|
+
interface ObserverAware {
|
|
223
|
+
/**
|
|
224
|
+
* Return a view of this resource scoped to the given telemetry. The harness installs
|
|
225
|
+
* the returned value as the resource's slot on the step's `ctx` before calling `step.run`.
|
|
226
|
+
* Called once per step (and once per step per branch), so each concurrent branch gets its
|
|
227
|
+
* own scoped view and there is no shared field to race on.
|
|
228
|
+
*/
|
|
229
|
+
withTelemetry(ctx: TelemetryContext): unknown;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Combine multiple {@link Observer} instances into one. Each hook on the
|
|
233
|
+
* composite forwards to all constituent observers in order.
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```ts
|
|
237
|
+
* const observer = composeObservers([otelObserver, metricsObserver])
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
/**
|
|
241
|
+
* Identifies which observer hook threw. Passed to an {@link ObserverErrorSink}.
|
|
242
|
+
*/
|
|
243
|
+
interface ObserverErrorContext {
|
|
244
|
+
/** The observer hook that threw, e.g. `'onStepStart'`, `'onRunEnd'`, `'onEvent'`. */
|
|
245
|
+
readonly hookName: string;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Opt-in sink for errors thrown by observer callbacks. Supplied under the
|
|
249
|
+
* `'onObserverError'` key in `agent.run()` / `agent.resume()` resources.
|
|
250
|
+
*
|
|
251
|
+
* Telemetry is a side-channel: an observer that throws must never crash a run.
|
|
252
|
+
* The harness routes every such throw here and continues. The sink is called at
|
|
253
|
+
* most once per failed hook invocation and MUST NOT rethrow into the run — if it
|
|
254
|
+
* does, the harness contains that too (see {@link safeInvoke}).
|
|
255
|
+
*/
|
|
256
|
+
type ObserverErrorSink = (error: unknown, ctx: ObserverErrorContext) => void;
|
|
257
|
+
declare function composeObservers(observers: Observer[], onObserverError?: ObserverErrorSink): Observer;
|
|
258
|
+
|
|
259
|
+
type Cursor = string | ForkCursor;
|
|
260
|
+
/**
|
|
261
|
+
* Persisted/returned shape of a paused fork. Always lists every branch of the fork, in
|
|
262
|
+
* declaration order (never completion order — see decision #8). A branch never appears
|
|
263
|
+
* with an 'errored' status: an uncaught branch error always escalates to whole-fork
|
|
264
|
+
* failure (decision #9) rather than sitting here as a persisted per-branch state.
|
|
265
|
+
*/
|
|
266
|
+
interface ForkCursor {
|
|
267
|
+
readonly kind: 'fork';
|
|
268
|
+
readonly fork: string;
|
|
269
|
+
readonly branches: readonly BranchCursor[];
|
|
270
|
+
}
|
|
271
|
+
type BranchCursor = {
|
|
272
|
+
readonly name: string;
|
|
273
|
+
readonly status: 'done';
|
|
274
|
+
readonly state: Record<string, unknown>;
|
|
275
|
+
readonly touchedKeys: readonly string[];
|
|
276
|
+
} | {
|
|
277
|
+
readonly name: string;
|
|
278
|
+
readonly status: 'paused';
|
|
279
|
+
readonly state: Record<string, unknown>;
|
|
280
|
+
readonly cursor: Cursor;
|
|
281
|
+
readonly touchedKeys: readonly string[];
|
|
282
|
+
};
|
|
283
|
+
/** Structural type guard distinguishing a ForkCursor from a plain step-name cursor. */
|
|
284
|
+
declare function isForkCursor(cursor: Cursor): cursor is ForkCursor;
|
|
285
|
+
|
|
103
286
|
/**
|
|
104
287
|
* Options passed to {@link SessionStore.claim}.
|
|
105
288
|
*
|
|
@@ -236,7 +419,11 @@ interface SessionStore {
|
|
|
236
419
|
* every run settles (either `'paused'` on an interrupt or `'completed'`).
|
|
237
420
|
*
|
|
238
421
|
* - `phase: 'paused'` — the run paused on an interrupt; `step` is the step that
|
|
239
|
-
* issued the interrupt and `signal` is `'$interrupt'`.
|
|
422
|
+
* issued the interrupt and `signal` is `'$interrupt'`. `step` is a plain step name,
|
|
423
|
+
* or (when the run paused inside a fork) a composite `ForkCursor` carrying every
|
|
424
|
+
* branch's own state/cursor — see `Cursor` in `loop-executor.ts` and
|
|
425
|
+
* HANDOVER-fork-join.md §4/Phase 5. A plain-string `step` from before fork/join
|
|
426
|
+
* existed is still a valid `Cursor`, so old rows load and resume unchanged.
|
|
240
427
|
* - `phase: 'completed'` — the loop exited normally; `signal` holds the exit
|
|
241
428
|
* signal (or is absent when the loop ended without emitting a signal).
|
|
242
429
|
*/
|
|
@@ -251,7 +438,7 @@ interface StoredRun {
|
|
|
251
438
|
readonly initialState: Record<string, unknown>;
|
|
252
439
|
readonly finalState: Record<string, unknown>;
|
|
253
440
|
readonly signal?: string;
|
|
254
|
-
readonly step?:
|
|
441
|
+
readonly step?: Cursor;
|
|
255
442
|
/**
|
|
256
443
|
* Operational metadata written by the framework.
|
|
257
444
|
*
|
|
@@ -268,7 +455,8 @@ interface StoredRun {
|
|
|
268
455
|
* - `'in-flight'` — a run is currently executing (in-process guard only; not
|
|
269
456
|
* detectable cross-process from the store alone).
|
|
270
457
|
* - `'paused'` — the last run settled on an interrupt; `step` identifies the
|
|
271
|
-
* step that issued the interrupt.
|
|
458
|
+
* step that issued the interrupt. When the run paused inside a fork, `step` is
|
|
459
|
+
* the fork's own name (not the richer per-branch detail — see `pendingInterrupts`).
|
|
272
460
|
* - `'completed'` — the last run exited the loop; `signal` is the exit signal.
|
|
273
461
|
*/
|
|
274
462
|
type SessionPhase = {
|
|
@@ -280,10 +468,28 @@ type SessionPhase = {
|
|
|
280
468
|
readonly phase: 'paused';
|
|
281
469
|
readonly signal?: string;
|
|
282
470
|
readonly step: string;
|
|
471
|
+
/**
|
|
472
|
+
* Every interrupt currently awaiting a response, one per paused branch when the run
|
|
473
|
+
* paused inside a fork (a single-element array for a plain, non-forked pause). Lets a
|
|
474
|
+
* caller discover and resolve each one — via `Agent.resume(response, sessionId, id)` —
|
|
475
|
+
* independently; the session stays `'paused'` until every entry is resolved.
|
|
476
|
+
*/
|
|
477
|
+
readonly pendingInterrupts: readonly PendingInterrupt[];
|
|
283
478
|
} | {
|
|
284
479
|
readonly phase: 'completed';
|
|
285
480
|
readonly signal?: string;
|
|
286
481
|
};
|
|
482
|
+
/** One outstanding `ctx.interrupt()` call awaiting a response. See {@link SessionPhase}. */
|
|
483
|
+
interface PendingInterrupt {
|
|
484
|
+
readonly interruptId: string;
|
|
485
|
+
readonly prompt: unknown;
|
|
486
|
+
/**
|
|
487
|
+
* The step that raised this interrupt. For a forked pause this is a `/`-joined
|
|
488
|
+
* breadcrumb (`"<fork>/<branch>/<step>"`, recursing further for nested forks) —
|
|
489
|
+
* same style as the validator's own fork/branch violation paths.
|
|
490
|
+
*/
|
|
491
|
+
readonly step: string;
|
|
492
|
+
}
|
|
287
493
|
|
|
288
494
|
/**
|
|
289
495
|
* Options for background renewal mode.
|
|
@@ -408,20 +614,47 @@ interface StepDef {
|
|
|
408
614
|
readonly next: string | undefined;
|
|
409
615
|
readonly errorAware: boolean;
|
|
410
616
|
}
|
|
617
|
+
/**
|
|
618
|
+
* A single `.branch(name, fn)` declaration inside a fork.
|
|
619
|
+
* definition is the branch's own, independently-captured LoopDefinition — built by running
|
|
620
|
+
* the branch's builder lambda against a fresh createLoopBuilder() instance. Inside a branch,
|
|
621
|
+
* `.end()` means "this branch is done," not "the run is done" (see LoopBuilder.branch).
|
|
622
|
+
*/
|
|
623
|
+
interface BranchDef {
|
|
624
|
+
readonly name: string;
|
|
625
|
+
readonly definition: LoopDefinition;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Compiled fork node stored in LoopDefinition.steps alongside StepDef nodes.
|
|
629
|
+
* A fork has no run/route of its own — it never emits a signal, so .on() is not supported on it.
|
|
630
|
+
* next: explicit .next(name) target for the step that runs once every branch has settled
|
|
631
|
+
* (the "join"), or undefined to fall through to the next declared top-level node.
|
|
632
|
+
* Discriminated from StepDef structurally (see isForkDef) rather than via a tag field, so
|
|
633
|
+
* existing StepDef-only code and hand-built StepDef literals are unaffected.
|
|
634
|
+
*/
|
|
635
|
+
interface ForkDef {
|
|
636
|
+
readonly name: string;
|
|
637
|
+
readonly branches: readonly BranchDef[];
|
|
638
|
+
readonly next: string | undefined;
|
|
639
|
+
}
|
|
640
|
+
/** A top-level node in a LoopDefinition: either an ordinary step or a fork. */
|
|
641
|
+
type LoopNode = StepDef | ForkDef;
|
|
642
|
+
/** Structural type guard distinguishing ForkDef from StepDef within a LoopNode. */
|
|
643
|
+
declare function isForkDef(node: LoopNode): node is ForkDef;
|
|
411
644
|
/**
|
|
412
645
|
* Complete loop topology captured from the builder lambda.
|
|
413
646
|
* Immutable snapshot — produced once, consumed by LoopValidator and (later) F6.
|
|
414
647
|
*
|
|
415
648
|
* startCalled: true if l.start() was invoked at least once.
|
|
416
|
-
* entryStep: name of the first .step() declared after .start() was called; undefined if
|
|
417
|
-
* was never called or no
|
|
418
|
-
* steps: all declared steps in declaration order.
|
|
649
|
+
* entryStep: name of the first .step()/.fork() declared after .start() was called; undefined if
|
|
650
|
+
* .start() was never called or no node was declared after it.
|
|
651
|
+
* steps: all declared top-level nodes (steps and forks) in declaration order.
|
|
419
652
|
* onError: the fallback step name from l.onError(), or undefined if not set.
|
|
420
653
|
*/
|
|
421
654
|
interface LoopDefinition {
|
|
422
655
|
readonly startCalled: boolean;
|
|
423
656
|
readonly entryStep: string | undefined;
|
|
424
|
-
readonly steps: readonly
|
|
657
|
+
readonly steps: readonly LoopNode[];
|
|
425
658
|
readonly onError: string | undefined;
|
|
426
659
|
}
|
|
427
660
|
/**
|
|
@@ -439,13 +672,40 @@ interface OnChain<S, Ctx> {
|
|
|
439
672
|
* All methods mutate internal state and return `this` for chaining.
|
|
440
673
|
*/
|
|
441
674
|
interface LoopBuilder<S, Ctx> {
|
|
442
|
-
/** Mark the loop as having a declared entry. The first .step() called after .start() is the entry. */
|
|
675
|
+
/** Mark the loop as having a declared entry. The first .step()/.fork() called after .start() is the entry. */
|
|
443
676
|
start(): LoopBuilder<S, Ctx>;
|
|
444
677
|
/** Declare a step. At least one of run or route must be set (validated later by LoopValidator). */
|
|
445
678
|
step(name: string, options: StepOptions<S, Ctx>): LoopBuilder<S, Ctx>;
|
|
679
|
+
/**
|
|
680
|
+
* Declare a fork: a named node that splits execution into 2+ concurrent branches, declared
|
|
681
|
+
* with .branch() immediately after. The step immediately following the fork automatically
|
|
682
|
+
* waits for every branch to settle (the "join") — no separate join primitive exists.
|
|
683
|
+
*
|
|
684
|
+
* Per-step telemetry is attributed correctly across concurrent branches: the harness scopes
|
|
685
|
+
* each branch's slots per step via {@link ObserverAware.withTelemetry}, so telemetry travels
|
|
686
|
+
* with the call rather than through shared instance state. See `ObserverAware`'s doc comment
|
|
687
|
+
* (`agent/observer.ts`).
|
|
688
|
+
*/
|
|
689
|
+
fork(name: string): LoopBuilder<S, Ctx>;
|
|
690
|
+
/**
|
|
691
|
+
* Declare one branch of the fork most recently opened with .fork(). Must be called immediately
|
|
692
|
+
* after .fork() or another .branch() on the same fork — throws otherwise.
|
|
693
|
+
* builderFn receives a fresh LoopBuilder scoped to this branch, using the same .step()/.on()/
|
|
694
|
+
* .next() vocabulary as the top level; inside it, .end() means "this branch is done," not
|
|
695
|
+
* "the run is done."
|
|
696
|
+
*/
|
|
697
|
+
branch(name: string, builderFn: (b: LoopBuilder<S, Ctx>) => void): LoopBuilder<S, Ctx>;
|
|
698
|
+
/**
|
|
699
|
+
* Declare the step that runs once every branch of the preceding fork has settled.
|
|
700
|
+
* Pure alias of .step(name, options) — not enforced by the validator, just a naming
|
|
701
|
+
* convenience so the DSL reads as fork/branch/join.
|
|
702
|
+
*/
|
|
703
|
+
join(name: string, options: StepOptions<S, Ctx>): LoopBuilder<S, Ctx>;
|
|
446
704
|
/**
|
|
447
705
|
* Begin a signal transition declaration. Must be called immediately after .step() or after
|
|
448
706
|
* a previous .on().to() or .on().end() chain (attaches to the most recently declared step).
|
|
707
|
+
* Throws if the most recently declared node is a fork — a fork has no run/route to emit
|
|
708
|
+
* a signal from.
|
|
449
709
|
*/
|
|
450
710
|
on(signal: string): OnChain<S, Ctx>;
|
|
451
711
|
/**
|
|
@@ -723,120 +983,4 @@ declare class LeaseExpiredError extends Error {
|
|
|
723
983
|
constructor(sessionId: string);
|
|
724
984
|
}
|
|
725
985
|
|
|
726
|
-
|
|
727
|
-
* Identifies the agent and session for run-level observer callbacks.
|
|
728
|
-
*/
|
|
729
|
-
interface RunContext {
|
|
730
|
-
readonly agentId: string;
|
|
731
|
-
readonly sessionId: string;
|
|
732
|
-
/**
|
|
733
|
-
* The UUID for this specific run invocation. Always present in RunContext
|
|
734
|
-
* passed to observer methods.
|
|
735
|
-
*/
|
|
736
|
-
readonly runId: string;
|
|
737
|
-
/**
|
|
738
|
-
* Optional parent run UUID for cross-process trace correlation. Present
|
|
739
|
-
* when parentRunId was passed to agent.run() resources. Absent for top-level
|
|
740
|
-
* runs or when not provided by the caller.
|
|
741
|
-
*/
|
|
742
|
-
readonly parentRunId?: string;
|
|
743
|
-
/**
|
|
744
|
-
* The physical instance ID of the agent process, if configured via
|
|
745
|
-
* `createAgent()` options. Absent when `instanceId` was not provided.
|
|
746
|
-
*/
|
|
747
|
-
readonly instanceId?: string;
|
|
748
|
-
}
|
|
749
|
-
/**
|
|
750
|
-
* Identifies the agent, session, and current step for step-level observer callbacks.
|
|
751
|
-
*/
|
|
752
|
-
interface StepContext {
|
|
753
|
-
readonly agentId: string;
|
|
754
|
-
readonly sessionId: string;
|
|
755
|
-
readonly stepName: string;
|
|
756
|
-
}
|
|
757
|
-
/**
|
|
758
|
-
* Observability hook interface. All methods are optional — implement only
|
|
759
|
-
* the hooks you need.
|
|
760
|
-
*
|
|
761
|
-
* Pass an `Observer` implementation in `agent.run()` resources under the key
|
|
762
|
-
* `'observer'`, or bind it to an {@link ObserverAware} slot before running.
|
|
763
|
-
*
|
|
764
|
-
* LLM adapters emit `'llm.response'` events via `onEvent` carrying an
|
|
765
|
-
* `LLMUsageEvent` payload for token tracking.
|
|
766
|
-
*
|
|
767
|
-
* @example
|
|
768
|
-
* ```ts
|
|
769
|
-
* const obs: Observer = {
|
|
770
|
-
* onRunStart: (ctx) => console.log('run started', ctx.sessionId),
|
|
771
|
-
* onEvent: (ctx, type, payload) => metrics.record(type, payload),
|
|
772
|
-
* }
|
|
773
|
-
* agent.run({}, { llm, observer: obs })
|
|
774
|
-
* ```
|
|
775
|
-
*/
|
|
776
|
-
interface Observer {
|
|
777
|
-
/** Called once when a run begins, before the first step executes. */
|
|
778
|
-
onRunStart?: (ctx: RunContext) => void;
|
|
779
|
-
/** Called once when a run settles (completed or stopped). */
|
|
780
|
-
onRunEnd?: (ctx: RunContext, event: {
|
|
781
|
-
signal: string;
|
|
782
|
-
durationMs: number;
|
|
783
|
-
}) => void;
|
|
784
|
-
/** Called immediately before each step's `run` function is invoked. */
|
|
785
|
-
onStepStart?: (ctx: StepContext) => void;
|
|
786
|
-
/** Called after a step completes successfully. */
|
|
787
|
-
onStepEnd?: (ctx: StepContext, event: {
|
|
788
|
-
durationMs: number;
|
|
789
|
-
}) => void;
|
|
790
|
-
/** Called when a step's `run` function throws. */
|
|
791
|
-
onStepError?: (ctx: StepContext, event: {
|
|
792
|
-
error: unknown;
|
|
793
|
-
durationMs: number;
|
|
794
|
-
}) => void;
|
|
795
|
-
/** Called when a step issues a `ctx.interrupt()`. */
|
|
796
|
-
onInterrupt?: (ctx: StepContext, event: {
|
|
797
|
-
prompt: unknown;
|
|
798
|
-
interruptId: string;
|
|
799
|
-
}) => void;
|
|
800
|
-
/**
|
|
801
|
-
* Called for arbitrary named events emitted by step code via `ctx.emit()` or
|
|
802
|
-
* by LLM adapters (e.g. `'llm.response'`).
|
|
803
|
-
*/
|
|
804
|
-
onEvent?: (ctx: StepContext, type: string, payload: unknown) => void;
|
|
805
|
-
}
|
|
806
|
-
/**
|
|
807
|
-
* Implemented by resources (e.g. LLM adapters) that accept an {@link Observer}
|
|
808
|
-
* at run time. The harness calls `bindObserver` on every slot value that
|
|
809
|
-
* implements this interface before the first step runs.
|
|
810
|
-
*
|
|
811
|
-
* Optionally, the harness calls `setStepContext` at the start of each step for
|
|
812
|
-
* every slot that exposes it. Adapters use this to attribute per-step telemetry
|
|
813
|
-
* (e.g. `observer.onEvent`) to the correct step without manual calls from step code.
|
|
814
|
-
*/
|
|
815
|
-
interface ObserverAware {
|
|
816
|
-
/**
|
|
817
|
-
* Receive the run's observer. The harness calls this once per `agent.run()`
|
|
818
|
-
* invocation before execution begins.
|
|
819
|
-
*/
|
|
820
|
-
bindObserver(observer: Observer): void;
|
|
821
|
-
/**
|
|
822
|
-
* Receive the current step context. The harness calls this at the start of
|
|
823
|
-
* each step for every slot that exposes this method, before calling `step.run`.
|
|
824
|
-
*
|
|
825
|
-
* Adapters that emit `observer.onEvent` in their `invoke()` method store the
|
|
826
|
-
* provided `StepContext` and use it as the first argument to `onEvent`, so
|
|
827
|
-
* events are attributed to the correct step automatically.
|
|
828
|
-
*/
|
|
829
|
-
setStepContext?(ctx: StepContext): void;
|
|
830
|
-
}
|
|
831
|
-
/**
|
|
832
|
-
* Combine multiple {@link Observer} instances into one. Each hook on the
|
|
833
|
-
* composite forwards to all constituent observers in order.
|
|
834
|
-
*
|
|
835
|
-
* @example
|
|
836
|
-
* ```ts
|
|
837
|
-
* const observer = composeObservers(otelObserver, metricsObserver)
|
|
838
|
-
* ```
|
|
839
|
-
*/
|
|
840
|
-
declare function composeObservers(...observers: Observer[]): Observer;
|
|
841
|
-
|
|
842
|
-
export { type Agent, type ClaimOptions, type DeepWithMarkers, type FieldDefinition, type FrameworkState, type Harness, type Lease, LeaseExpiredError, type LoopDefinition, LoopNotDefinedError, NoInterruptError, type Observer, type ObserverAware, 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 StepState, StoreLoadError, type StoredRun, type StoredRunMetadata, type TransitionTarget, composeObservers, createAgent, createHarness, field, isRequiredMarker, isRuntimeMarker, required, runtime };
|
|
986
|
+
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 StepState, StoreLoadError, type StoredRun, type StoredRunMetadata, type TelemetryContext, type TransitionTarget, composeObservers, createAgent, createHarness, field, isForkCursor, isForkDef, isRequiredMarker, isRuntimeMarker, required, runtime };
|