@noetaris/harness 0.11.0 → 0.12.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 +98 -2
- package/dist/index.d.ts +216 -4
- package/dist/index.js +897 -246
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -299,14 +299,99 @@ h.loop(l =>
|
|
|
299
299
|
If a fork fails fast while a sub agent is paused, that sub agent's own session stays
|
|
300
300
|
paused in its store — clean it up or reuse it the next time the step runs.
|
|
301
301
|
|
|
302
|
+
### Debugging a run
|
|
303
|
+
|
|
304
|
+
`agent.debug(initialState, resources)` starts a run held before its first step and returns a
|
|
305
|
+
`DebugSession`. Each command runs part of the run and resolves with the next **stop**: where the
|
|
306
|
+
run is held, the step that just settled, and a copy of the state. It takes the same arguments as
|
|
307
|
+
`agent.run()` and drives the whole run chain — an interrupt is answered inside the session.
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
const dbg = agent.debug({}, { sessionId: 's1' })
|
|
311
|
+
|
|
312
|
+
let stop = await dbg.current // held before the first step: kind 'step', reason 'start'
|
|
313
|
+
stop = await dbg.next() // run one step; stop.last is the step that settled
|
|
314
|
+
if (stop.kind === 'step') console.log(stop.last?.name, stop.last?.update?.values)
|
|
315
|
+
|
|
316
|
+
dbg.setBreakpoint('review', (state) => (state.score as number) < 0.5)
|
|
317
|
+
stop = await dbg.continue() // runs until a breakpoint, an interrupt or the end
|
|
318
|
+
|
|
319
|
+
if (stop.kind === 'step') dbg.setState({ score: 0.9 }) // edit state before 'review' runs
|
|
320
|
+
stop = await dbg.continue()
|
|
321
|
+
if (stop.kind === 'interrupt') stop = await dbg.answer(true) // resume; held before the re-run
|
|
322
|
+
|
|
323
|
+
stop = await dbg.continue() // { kind: 'end', outcome: { state, signal } }
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
The session is local to this process. While it lasts, `run()`, `resume()` and `debug()` on its
|
|
327
|
+
session throw `SessionInDebugError` here; see [LIMITATIONS.md](./LIMITATIONS.md) for other processes.
|
|
328
|
+
|
|
329
|
+
**Stops** (`DebugStop`):
|
|
330
|
+
|
|
331
|
+
| `kind` | Fields | When |
|
|
332
|
+
|---|---|---|
|
|
333
|
+
| `step` | `reason`, `next` (the step about to run), `last` (the step that just settled: `name`, `outcome`, `update`, `signal`, `next`, `error`, `before`), `state`, `frame` | held at a step boundary. `reason` is `start`, `step`, `breakpoint`, `resume` (after `answer`), `fork-entry` or `branch-end` |
|
|
334
|
+
| `interrupt` | `interruptId`, `prompt`, `pending` (every interrupt the run waits on), `last`, `state`, `frame` | a step called `ctx.interrupt()` |
|
|
335
|
+
| `end` | `outcome` (`{ state, signal }`), `last` | the run completed, paused on an error, or was stopped |
|
|
336
|
+
|
|
337
|
+
`state`, `last.before` and `last.update` are `Snapshot`s: `{ values, shared }`. Each top-level key
|
|
338
|
+
is copied with `structuredClone`; a key that can't be cloned (a function, say) keeps its reference
|
|
339
|
+
and is listed in `shared`.
|
|
340
|
+
|
|
341
|
+
**Commands** (`DebugSession`):
|
|
342
|
+
|
|
343
|
+
| Command | Does |
|
|
344
|
+
|---|---|
|
|
345
|
+
| `current` | promise of the latest stop (the start stop at first) |
|
|
346
|
+
| `next()` | run exactly one step (a fork counts as one step) |
|
|
347
|
+
| `continue()` | run until a breakpoint hits, an interrupt, or the end |
|
|
348
|
+
| `setBreakpoint(step, when?)`, `clearBreakpoint(step)`, `breakpoints` | stop before `step` when `when(state)` is truthy (always without `when`); one breakpoint per step |
|
|
349
|
+
| `setState(patch)` | while held at a `step` stop: overwrite keys before the next step (no reducers; values are copied; `$` keys are refused). Observers get event `debug.setState` with `{ patch }` |
|
|
350
|
+
| `answer(response, interruptId?)` | at an `interrupt` stop: resume through the normal resume path and hold before the resumed step |
|
|
351
|
+
| `stop()` | end the session at any time; resolves with the `end` stop. At an interrupt stop the interrupt stays pending as saved |
|
|
352
|
+
| `done` | promise of the last run's outcome, as `await handle` gives it |
|
|
353
|
+
|
|
354
|
+
One command at a time: a second `next()`, `continue()` or `answer()` while one runs rejects with
|
|
355
|
+
`DebugCommandError` (`reason: 'busy'`). Other reasons: `not-held`, `not-a-fork`, `reserved-key`,
|
|
356
|
+
`ended`.
|
|
357
|
+
|
|
358
|
+
While the run is held, the session renews its claim on the session store (when the store supports
|
|
359
|
+
claims), so the lease doesn't expire during a pause. Aborting the `signal` resource ends the
|
|
360
|
+
session in any state; so does `stop()`. Step durations leave out the time held.
|
|
361
|
+
|
|
362
|
+
#### Fork branches
|
|
363
|
+
|
|
364
|
+
`next()` steps over a fork. To step inside it, call `stepInto()` when the next step is a fork:
|
|
365
|
+
every branch starts and is held at its first step, and the first branch is focused. Branches are
|
|
366
|
+
**frames**, named by their branch path (`[]` is the top level).
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
stop = await dbg.stepInto() // { frame: ['x'], reason: 'fork-entry', next: 'sx' }
|
|
370
|
+
dbg.frames // [{ frame: [], status: 'in-fork' }, { frame: ['x'], status: 'held' }, …]
|
|
371
|
+
stop = dbg.focus(['y']) // switch to another held branch
|
|
372
|
+
dbg.setState({ note: 'edited in y' }) // counts as written by branch y, so the join keeps it
|
|
373
|
+
stop = await dbg.continueAll() // release every branch; resolves with the first stop any frame makes
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
`next()` and `continue()` drive only the focused frame; other branches wait. When the driven frame
|
|
377
|
+
ends, focus moves to the next held frame (`reason: 'branch-end'`, with `ended`). Breakpoints hit in
|
|
378
|
+
any frame. If a branch fails and the fork fails fast, held siblings are released and end.
|
|
379
|
+
|
|
302
380
|
### Observers
|
|
303
381
|
|
|
304
382
|
An `Observer` receives telemetry hooks for a run. Pass it as `observer` in the resources
|
|
305
|
-
of `agent.run()` or `agent.resume()
|
|
383
|
+
of `agent.run()` or `agent.resume()`, or once for every run of an agent in
|
|
384
|
+
`createAgent(id, h, slots, { observer })`. Combine several with `composeObservers([a, b])`;
|
|
306
385
|
a hook that throws is reported to `onObserverError` (or `console.error`) and never stops
|
|
307
386
|
the run. The observer that LLM adapters call is isolated the same way, so an observer that
|
|
308
387
|
throws on an adapter event never fails a step. Every hook is optional.
|
|
309
388
|
|
|
389
|
+
An agent's observer serves `agent.run()`, `handle.resume()` and `agent.resume()`. A per-call
|
|
390
|
+
`observer` is composed on top of it, never replacing it: the agent's hooks fire first, its `wrap` is
|
|
391
|
+
outermost, and on a shared `carry` key the per-call value wins. Passing the agent's own observer per
|
|
392
|
+
call uses it once. Hook errors go to the per-call `onObserverError`, else the agent's
|
|
393
|
+
`onObserverError` option, else `console.error`.
|
|
394
|
+
|
|
310
395
|
| Hook | Fires |
|
|
311
396
|
|---|---|
|
|
312
397
|
| `onRunStart(ctx)` | once when a run starts |
|
|
@@ -462,7 +547,7 @@ The harness never trusts a wrapper. `fn` always runs exactly once, and the calle
|
|
|
462
547
|
| Export | Description |
|
|
463
548
|
|---|---|
|
|
464
549
|
| `createHarness<Ctx>()(schema)` | Creates a harness. Fixes `Ctx`, infers `State` from schema. |
|
|
465
|
-
| `createAgent(id, h, slots, options?)` | Assigns the agent an ID and fills `required()` slots. `options` (`AgentOptions`) takes `instanceId`, `stores` (for `required()` store slots)
|
|
550
|
+
| `createAgent(id, h, slots, options?)` | Assigns the agent an ID and fills `required()` slots. `options` (`AgentOptions`) takes `instanceId`, `stores` (for `required()` store slots), `claimOptions` (this agent's default claim TTL; per-call `claimOptions` wins, then `HARNESS_CLAIM_TTL_MS`, then 30 000 ms), `observer` (for every run of the agent; see [Observers](#observers)) and `onObserverError` (the default sink for hook errors). Returns an `Agent`. |
|
|
466
551
|
| `field<T>(opts)` | Declares a state field with a default and optional reduce function. |
|
|
467
552
|
| `required()` | Marks a provider slot as required at `createAgent()`. |
|
|
468
553
|
| `runtime()` | Marks a provider slot as required at `agent.run()`. |
|
|
@@ -484,6 +569,15 @@ The harness never trusts a wrapper. `fn` always runs exactly once, and the calle
|
|
|
484
569
|
| `SessionInFlightError` | Thrown when a session is already running. |
|
|
485
570
|
| `SessionPendingInterruptError` | `agent.run()` on a session paused on a pending interrupt — use `agent.resume()` instead. With a session store, the run resolves with `signal: '$error'` and this error in `state.$error`; without one, `agent.run()` throws it. |
|
|
486
571
|
| `StoreLoadError` | Thrown when the session store fails to load state. |
|
|
572
|
+
| `InvalidAgentOptionError` | Thrown by `createAgent()` when `observer` is not an object or `onObserverError` is not a function; `key` and `value` name it. |
|
|
573
|
+
| `InvalidClaimOptionsError` | Thrown by `createAgent()` when `claimOptions.ttlMs` is not a finite number greater than 0. |
|
|
574
|
+
| `DebugSession` | Returned by `agent.debug()`: a run driven step by step (see [Debugging a run](#debugging-a-run)). |
|
|
575
|
+
| `DebugStop` | Where a debug session stopped: `step`, `interrupt` or `end`. |
|
|
576
|
+
| `SettledStep`, `Snapshot` | The step that just settled at a stop, and a per-key copy of a state or update. |
|
|
577
|
+
| `DebugFrame`, `EndedFrame`, `PendingInterrupt` | A fork branch held by a debug session, a frame that ended, an interrupt the run waits on. |
|
|
578
|
+
| `DebugBreakpoint` | `{ step, when? }`. |
|
|
579
|
+
| `DebugCommandError` | A debug command that can't run now; `reason` is `busy`, `not-held`, `not-a-fork`, `reserved-key` or `ended`. |
|
|
580
|
+
| `SessionInDebugError` | `run()`, `resume()` or `debug()` on a session that is being debugged in this process. |
|
|
487
581
|
|
|
488
582
|
## Design Principles
|
|
489
583
|
|
|
@@ -508,6 +602,8 @@ See [LIMITATIONS.md](LIMITATIONS.md) for known limitations.
|
|
|
508
602
|
- [`@noetaris/harness-openai`](https://github.com/noetaris-lab/harness-openai) — OpenAI adapter
|
|
509
603
|
- [`@noetaris/harness-google`](https://github.com/noetaris-lab/harness-google) — Google Gemini adapter
|
|
510
604
|
- [`@noetaris/harness-otel`](https://github.com/noetaris-lab/harness-otel) — OpenTelemetry observer bridge
|
|
605
|
+
- [`@noetaris/harness-recorder`](https://github.com/noetaris-lab/harness-recorder) — records runs step by step and plays a finished session back
|
|
606
|
+
- [`@noetaris/harness-testing`](https://github.com/noetaris-lab/harness-testing) — `runStep`, `runRoute`, `MockObserver` for tests
|
|
511
607
|
- [`@noetaris/harness-otel-genai`](https://github.com/noetaris-lab/harness-otel-genai) — GenAI semantic-conventions extension for the bridge
|
|
512
608
|
|
|
513
609
|
## License
|
package/dist/index.d.ts
CHANGED
|
@@ -677,13 +677,13 @@ type SessionPhase = {
|
|
|
677
677
|
* caller discover and resolve each one — via `Agent.resume(response, sessionId, id)` —
|
|
678
678
|
* independently; the session stays `'paused'` until every entry is resolved.
|
|
679
679
|
*/
|
|
680
|
-
readonly pendingInterrupts: readonly PendingInterrupt[];
|
|
680
|
+
readonly pendingInterrupts: readonly PendingInterrupt$1[];
|
|
681
681
|
} | {
|
|
682
682
|
readonly phase: 'completed';
|
|
683
683
|
readonly signal?: string;
|
|
684
684
|
};
|
|
685
685
|
/** One outstanding `ctx.interrupt()` call awaiting a response. See {@link SessionPhase}. */
|
|
686
|
-
interface PendingInterrupt {
|
|
686
|
+
interface PendingInterrupt$1 {
|
|
687
687
|
readonly interruptId: string;
|
|
688
688
|
readonly prompt: unknown;
|
|
689
689
|
/**
|
|
@@ -1061,6 +1061,169 @@ interface RunHandle extends PromiseLike<RunOutcome> {
|
|
|
1061
1061
|
readonly currentStep: string | null;
|
|
1062
1062
|
}
|
|
1063
1063
|
|
|
1064
|
+
/**
|
|
1065
|
+
* A copy of a state (or update) object taken when a debug stop was made. Each top-level key is
|
|
1066
|
+
* copied with `structuredClone`; a key whose value can't be cloned (a function, for example) keeps
|
|
1067
|
+
* its reference and is listed in `shared`, so its value may have changed since.
|
|
1068
|
+
*/
|
|
1069
|
+
interface Snapshot {
|
|
1070
|
+
readonly values: Readonly<Record<string, unknown>>;
|
|
1071
|
+
readonly shared: readonly string[];
|
|
1072
|
+
}
|
|
1073
|
+
/** A step that has settled, as a debug stop reports it: what `onStepSettled` knows, plus state. */
|
|
1074
|
+
interface SettledStep {
|
|
1075
|
+
readonly name: string;
|
|
1076
|
+
readonly outcome: StepSettledEvent['outcome'];
|
|
1077
|
+
/** The step's return value, copied; absent when it did not return one (see {@link StepSettledEvent}). */
|
|
1078
|
+
readonly update?: Snapshot;
|
|
1079
|
+
readonly signal?: string;
|
|
1080
|
+
readonly next: StepSettledNext;
|
|
1081
|
+
readonly error?: Error;
|
|
1082
|
+
/** The state just before this step ran. The state after it is the stop's own `state` (or `outcome.state`). */
|
|
1083
|
+
readonly before: Snapshot;
|
|
1084
|
+
readonly durationMs: number;
|
|
1085
|
+
readonly fork?: StepSettledEvent['fork'];
|
|
1086
|
+
}
|
|
1087
|
+
/** A branch frame that ended, as a `branch-end` stop reports it. */
|
|
1088
|
+
interface EndedFrame {
|
|
1089
|
+
readonly frame: readonly string[];
|
|
1090
|
+
readonly outcome: 'done' | 'interrupt' | 'error' | 'stopped';
|
|
1091
|
+
readonly last?: SettledStep;
|
|
1092
|
+
}
|
|
1093
|
+
/** An interrupt the paused run is waiting on, and the frame that raised it. */
|
|
1094
|
+
interface PendingInterrupt {
|
|
1095
|
+
readonly interruptId: string;
|
|
1096
|
+
readonly prompt: unknown;
|
|
1097
|
+
readonly frame: readonly string[];
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Where a debug session stopped. `frame` is the frame the stop belongs to: `[]` for the top-level run,
|
|
1101
|
+
* the branch path for a fork branch.
|
|
1102
|
+
* - `step`: held at a step boundary of `frame`, before `next` runs. `last` is the step that just settled
|
|
1103
|
+
* in that frame (absent on its first stop). `reason` says why it stopped here; `branch-end` means the
|
|
1104
|
+
* frame the command was driving ended (see `ended`) and focus moved here.
|
|
1105
|
+
* - `interrupt`: the run paused on `ctx.interrupt()`; `pending` lists every interrupt it waits on
|
|
1106
|
+
* (depth-first, declaration order), the first one also in `interruptId` / `prompt` / `frame`.
|
|
1107
|
+
* Answer with {@link DebugSession.answer}.
|
|
1108
|
+
* - `end`: the session is over — the run completed, paused on an error, or was stopped.
|
|
1109
|
+
*/
|
|
1110
|
+
type DebugStop = {
|
|
1111
|
+
readonly kind: 'step';
|
|
1112
|
+
readonly frame: readonly string[];
|
|
1113
|
+
readonly reason: 'start' | 'step' | 'breakpoint' | 'resume' | 'fork-entry' | 'branch-end';
|
|
1114
|
+
readonly runId: string;
|
|
1115
|
+
readonly next: string;
|
|
1116
|
+
readonly last?: SettledStep;
|
|
1117
|
+
readonly state: Snapshot;
|
|
1118
|
+
/** What a breakpoint condition threw, when that is why the session stopped here. */
|
|
1119
|
+
readonly conditionError?: unknown;
|
|
1120
|
+
/** With `reason: 'branch-end'`: the frame that ended. */
|
|
1121
|
+
readonly ended?: EndedFrame;
|
|
1122
|
+
} | {
|
|
1123
|
+
readonly kind: 'interrupt';
|
|
1124
|
+
readonly frame: readonly string[];
|
|
1125
|
+
readonly runId: string;
|
|
1126
|
+
readonly last: SettledStep;
|
|
1127
|
+
readonly interruptId: string;
|
|
1128
|
+
readonly prompt: unknown;
|
|
1129
|
+
readonly pending: readonly PendingInterrupt[];
|
|
1130
|
+
readonly state: Snapshot;
|
|
1131
|
+
} | {
|
|
1132
|
+
readonly kind: 'end';
|
|
1133
|
+
readonly runId: string;
|
|
1134
|
+
readonly last?: SettledStep;
|
|
1135
|
+
readonly outcome: RunOutcome;
|
|
1136
|
+
};
|
|
1137
|
+
/** A frame of a debug session, as {@link DebugSession.frames} lists it. */
|
|
1138
|
+
interface DebugFrame {
|
|
1139
|
+
readonly frame: readonly string[];
|
|
1140
|
+
/** `in-fork`: waiting for a fork it started to join. `ended`: a branch that has finished (listed until the join). */
|
|
1141
|
+
readonly status: 'held' | 'running' | 'in-fork' | 'ended';
|
|
1142
|
+
/** The step it is held before, when `held`. */
|
|
1143
|
+
readonly next?: string;
|
|
1144
|
+
}
|
|
1145
|
+
/** A breakpoint: stop before `step` runs, when `when` (if given) returns a truthy value. */
|
|
1146
|
+
interface DebugBreakpoint {
|
|
1147
|
+
readonly step: string;
|
|
1148
|
+
/** Gets the live state of the frame about to run the step, not a copy: it must not change it. */
|
|
1149
|
+
readonly when?: (state: Readonly<Record<string, unknown>>) => unknown;
|
|
1150
|
+
}
|
|
1151
|
+
/** Thrown (or rejected) by a {@link DebugSession} command that can't run in the session's current state. */
|
|
1152
|
+
declare class DebugCommandError extends Error {
|
|
1153
|
+
/**
|
|
1154
|
+
* - `busy`: another command is still running
|
|
1155
|
+
* - `not-held`: the focused frame is not held at a step stop (or, for `answer`, the session is not at
|
|
1156
|
+
* an interrupt stop; for `focus`, the frame is not held)
|
|
1157
|
+
* - `not-a-fork`: `stepInto` where the next step is not a fork node
|
|
1158
|
+
* - `reserved-key`: `setState` was given a framework key (`$…`)
|
|
1159
|
+
* - `ended`: the session is over
|
|
1160
|
+
*/
|
|
1161
|
+
readonly reason: 'busy' | 'not-held' | 'not-a-fork' | 'reserved-key' | 'ended';
|
|
1162
|
+
/** The command that was refused. */
|
|
1163
|
+
readonly command: string;
|
|
1164
|
+
constructor(reason: DebugCommandError['reason'], command: string, detail?: string);
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* A run of an agent driven step by step, returned by `agent.debug()`. The session starts held before
|
|
1168
|
+
* the first step; each command returns the next stop. It spans the whole run chain: a run that pauses
|
|
1169
|
+
* on an interrupt is answered with {@link answer} and stepping continues in the resumed run.
|
|
1170
|
+
*
|
|
1171
|
+
* Fork branches are frames. Stepping into a fork ({@link stepInto}) holds every branch at its first
|
|
1172
|
+
* step; only the focused frame runs, the others wait until focused or released with
|
|
1173
|
+
* {@link continueAll}. When the frame a command drives ends, focus moves to the next held frame.
|
|
1174
|
+
*
|
|
1175
|
+
* One command at a time: `next()`, `continue()`, `stepInto()`, `continueAll()` and `answer()` reject
|
|
1176
|
+
* with a `busy` {@link DebugCommandError} while another is running. `stop()` is valid at any time.
|
|
1177
|
+
*
|
|
1178
|
+
* While a frame is held, the session renews the run's claim (if the store can extend claims). Call
|
|
1179
|
+
* `stop()`, or abort the `signal` resource, to end a session you no longer drive: a held session
|
|
1180
|
+
* otherwise keeps the session fenced in this process.
|
|
1181
|
+
*/
|
|
1182
|
+
interface DebugSession {
|
|
1183
|
+
readonly sessionId: string;
|
|
1184
|
+
/** The id of the current run; changes when an interrupt is answered. */
|
|
1185
|
+
readonly runId: string;
|
|
1186
|
+
/**
|
|
1187
|
+
* The focused frame's latest stop. At first, the start stop (`reason: 'start'`, `next` = the entry
|
|
1188
|
+
* step). While no frame is held, a promise of the next stop.
|
|
1189
|
+
*/
|
|
1190
|
+
readonly current: Promise<DebugStop>;
|
|
1191
|
+
/** The final outcome of the last run, as `await handle` would give it. */
|
|
1192
|
+
readonly done: Promise<RunOutcome>;
|
|
1193
|
+
readonly breakpoints: ReadonlyArray<DebugBreakpoint>;
|
|
1194
|
+
/** The frames of the current run, depth-first in declaration order. */
|
|
1195
|
+
readonly frames: ReadonlyArray<DebugFrame>;
|
|
1196
|
+
/** Focus a held frame; returns its current stop. */
|
|
1197
|
+
focus(frame: readonly string[]): DebugStop;
|
|
1198
|
+
/** Run exactly one step of the focused frame (a whole fork, stepping over it). Ignores breakpoints at its own boundary. */
|
|
1199
|
+
next(): Promise<DebugStop>;
|
|
1200
|
+
/** At a fork: start every branch and hold each at its first step; focus the first branch. */
|
|
1201
|
+
stepInto(): Promise<DebugStop>;
|
|
1202
|
+
/** Run the focused frame until a breakpoint hits or the frame ends; other frames stay held. */
|
|
1203
|
+
continue(): Promise<DebugStop>;
|
|
1204
|
+
/** Release every held frame; resolves with the first stop any frame makes. */
|
|
1205
|
+
continueAll(): Promise<DebugStop>;
|
|
1206
|
+
/** At an `interrupt` stop: answer it (by default the stop's own interrupt) and hold before the resumed run's first step. */
|
|
1207
|
+
answer(response: unknown, interruptId?: string): Promise<DebugStop>;
|
|
1208
|
+
/**
|
|
1209
|
+
* While the focused frame is held at a `step` stop: set state keys of that frame, overwriting them as
|
|
1210
|
+
* given (no reducers). Values are copied. In a branch the keys count as written by the branch, so the
|
|
1211
|
+
* join keeps them (a key also written by a sibling then needs a reducer, as for any write). Reported to
|
|
1212
|
+
* the observer as event `debug.setState` with `{ patch }` before the frame's next step.
|
|
1213
|
+
*/
|
|
1214
|
+
setState(patch: Record<string, unknown>): Snapshot;
|
|
1215
|
+
/** Stop before `step` when `when` returns a truthy value (always, without `when`). Replaces that step's breakpoint. */
|
|
1216
|
+
setBreakpoint(step: string, when?: DebugBreakpoint['when']): void;
|
|
1217
|
+
clearBreakpoint(step: string): void;
|
|
1218
|
+
/**
|
|
1219
|
+
* End the session. Held frames end (stopped) where they are; running steps are stopped like
|
|
1220
|
+
* `handle.stop()` and their frames end at the next boundary. At an interrupt stop: the session ends
|
|
1221
|
+
* and the interrupt stays pending as saved. Resolves with the `end` stop — the same one a pending
|
|
1222
|
+
* command resolves with, and again on every later call.
|
|
1223
|
+
*/
|
|
1224
|
+
stop(): Promise<DebugStop>;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1064
1227
|
/** Options passed to createAgent(). */
|
|
1065
1228
|
interface AgentOptions {
|
|
1066
1229
|
/**
|
|
@@ -1080,6 +1243,19 @@ interface AgentOptions {
|
|
|
1080
1243
|
* `ttlMs` must be a finite number greater than 0; otherwise `createAgent` throws `InvalidClaimOptionsError`.
|
|
1081
1244
|
*/
|
|
1082
1245
|
readonly claimOptions?: ClaimOptions;
|
|
1246
|
+
/**
|
|
1247
|
+
* Observer for every run of this agent: `agent.run()`, `handle.resume()` and `agent.resume()`.
|
|
1248
|
+
* A per-call `observer` is composed on top of it (this one first: its hooks fire first, its `wrap`
|
|
1249
|
+
* is outermost, and on a shared `carry` key the per-call value wins); it never replaces it. The
|
|
1250
|
+
* same object passed per call is used once. One object serves every run, concurrent runs included.
|
|
1251
|
+
* Must be an object; otherwise `createAgent` throws `InvalidAgentOptionError`.
|
|
1252
|
+
*/
|
|
1253
|
+
readonly observer?: Observer;
|
|
1254
|
+
/**
|
|
1255
|
+
* Default sink for observer-hook errors, used when a call passes no `onObserverError`.
|
|
1256
|
+
* Must be a function; otherwise `createAgent` throws `InvalidAgentOptionError`.
|
|
1257
|
+
*/
|
|
1258
|
+
readonly onObserverError?: ObserverErrorSink;
|
|
1083
1259
|
}
|
|
1084
1260
|
interface Agent {
|
|
1085
1261
|
/** The agent's unique identifier, as provided to createAgent(). */
|
|
@@ -1101,7 +1277,8 @@ interface Agent {
|
|
|
1101
1277
|
* Returns a RunHandle synchronously; the execution promise performs the resume.
|
|
1102
1278
|
*
|
|
1103
1279
|
* The optional fourth argument `resources` accepts:
|
|
1104
|
-
* - `observer?: Observer` — structured telemetry for the resumed run (see Observability)
|
|
1280
|
+
* - `observer?: Observer` — structured telemetry for the resumed run, composed on top of `AgentOptions.observer` (see Observability)
|
|
1281
|
+
* - `onObserverError?: ObserverErrorSink` — sink for observer-hook errors; falls back to `AgentOptions.onObserverError`
|
|
1105
1282
|
* - `parentRunId?: string` — the caller's run id, as for `run()`; set on this resumed run's
|
|
1106
1283
|
* `RunContext` only (a later `handle.resume()` on the returned handle does not inherit it)
|
|
1107
1284
|
* - `events?.onStoreError?` — raw store-error callback (unchanged from prior shape)
|
|
@@ -1110,11 +1287,35 @@ interface Agent {
|
|
|
1110
1287
|
* on the returned handle reuses it
|
|
1111
1288
|
*/
|
|
1112
1289
|
resume(response: unknown, sessionId: string, interruptId: string, resources?: Record<string, unknown>): RunHandle;
|
|
1290
|
+
/**
|
|
1291
|
+
* Start a run held before its first step, to drive it step by step. Takes the same `initialState`
|
|
1292
|
+
* and `resources` as {@link run} and returns a {@link DebugSession}, whose `current` resolves to
|
|
1293
|
+
* the start stop. While the session lasts, `run()`, `resume()` and `debug()` on its session throw
|
|
1294
|
+
* `SessionInDebugError` (in this process only). The `signal` resource, when given, ends the session
|
|
1295
|
+
* when aborted, in any state.
|
|
1296
|
+
*
|
|
1297
|
+
* Step durations exclude the time held; a run's duration (`onRunEnd`) includes it.
|
|
1298
|
+
*/
|
|
1299
|
+
debug(initialState: Record<string, unknown>, resources: Record<string, unknown>): DebugSession;
|
|
1113
1300
|
/**
|
|
1114
1301
|
* Query the session store for the current phase of a session.
|
|
1115
1302
|
*/
|
|
1116
1303
|
status(sessionId: string): Promise<SessionPhase>;
|
|
1117
1304
|
}
|
|
1305
|
+
/** Thrown by {@link createAgent} when `agentOptions.claimOptions.ttlMs` is not a finite number greater than 0. */
|
|
1306
|
+
declare class InvalidClaimOptionsError extends Error {
|
|
1307
|
+
/** The rejected `ttlMs` value, as given. */
|
|
1308
|
+
readonly ttlMs: unknown;
|
|
1309
|
+
constructor(ttlMs: unknown);
|
|
1310
|
+
}
|
|
1311
|
+
/** Thrown by {@link createAgent} when `agentOptions.observer` is not an object or `agentOptions.onObserverError` is not a function. */
|
|
1312
|
+
declare class InvalidAgentOptionError extends Error {
|
|
1313
|
+
/** The option that was rejected. */
|
|
1314
|
+
readonly key: 'observer' | 'onObserverError';
|
|
1315
|
+
/** The rejected value, as given. */
|
|
1316
|
+
readonly value: unknown;
|
|
1317
|
+
constructor(key: 'observer' | 'onObserverError', value: unknown);
|
|
1318
|
+
}
|
|
1118
1319
|
/** Thrown by `agent.run()` when `sessionId` or `requestId` is given but is not a non-empty string. */
|
|
1119
1320
|
declare class InvalidRunResourceError extends Error {
|
|
1120
1321
|
/** The resource key that was rejected. */
|
|
@@ -1160,6 +1361,17 @@ declare class SessionInFlightError extends Error {
|
|
|
1160
1361
|
readonly sessionId: string;
|
|
1161
1362
|
constructor(sessionId: string);
|
|
1162
1363
|
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Thrown by {@link Agent.run}, {@link Agent.resume} or {@link Agent.debug} when the given session is
|
|
1366
|
+
* being debugged by a debug session of the same agent instance. The fence is in-process only.
|
|
1367
|
+
*
|
|
1368
|
+
* Stop the debug session (`stop()`) or let it end before running the session normally.
|
|
1369
|
+
*/
|
|
1370
|
+
declare class SessionInDebugError extends Error {
|
|
1371
|
+
/** The session being debugged. */
|
|
1372
|
+
readonly sessionId: string;
|
|
1373
|
+
constructor(sessionId: string);
|
|
1374
|
+
}
|
|
1163
1375
|
/**
|
|
1164
1376
|
* Thrown by {@link Agent.run} when the session is paused on an unanswered
|
|
1165
1377
|
* interrupt. Call {@link Agent.resume} (or `handle.resume()`) instead.
|
|
@@ -1219,4 +1431,4 @@ declare class NoInterruptError extends Error {
|
|
|
1219
1431
|
constructor();
|
|
1220
1432
|
}
|
|
1221
1433
|
|
|
1222
|
-
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, InvalidRunResourceError, 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 };
|
|
1434
|
+
export { type Agent, type AgentOptions, type BranchCursor, type BranchDef, type ClaimOptions, type Cursor, type DebugBreakpoint, DebugCommandError, type DebugFrame, type DebugSession, type DebugStop, type DeepWithMarkers, type EndedFrame, type FieldDefinition, type ForkCursor, type ForkDef, type FrameworkState, type Harness, InvalidAgentOptionError, InvalidClaimOptionsError, InvalidRunResourceError, type Lease, LeaseExpiredError, type LoopDefinition, type LoopNode, LoopNotDefinedError, NoInterruptError, type Observer, type ObserverAware, type ObserverErrorContext, type ObserverErrorSink, type PendingInterrupt, REQUIRED_TAG, RUNTIME_TAG, type RequiredMarker, type RouteFn, type RunCarrier, type RunContext, type RunFn, type RunRef, type RuntimeMarker, SessionBusyError, SessionInDebugError, SessionInFlightError, SessionPendingInterruptError, type SessionStore, type SettledStep, type SignalTransition, type Snapshot, 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 };
|