@dudousxd/nestjs-agent-core 0.15.4 → 0.16.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/dist/index.d.ts CHANGED
@@ -1,812 +1,8 @@
1
+ import { M as ModelMessage, a as ToolDefinition, b as ToolCallRequest, c as MessageUsage, E as ElicitationRequest, A as Actor, d as ThreadSummary, e as ThreadDetail, S as StoredMessage, f as ToolResult, g as MessageAttachment, h as ToolCallStatus, U as UsagePurpose, i as ToolSpec, Q as QuotaState, j as AgentRunInput, H as HumanReply, T as ToolHandler, k as HistoryPolicy, O as OutputProcessor, P as ProcessorContext, I as InputProcessor, l as ProcessedPrompt, m as ModelAnswer, n as PageContext, o as AgentDefinition, p as AgentDelegation, D as DetachedDelivery, q as AiToolCtx, r as PromptBuilder, s as PromptContributor, t as ToolTransientRetrySetting, u as AgentIntake, v as Decision, L as LlmStepEnvelope, w as ToolStepEnvelope } from './tool-CL9oEytW.js';
2
+ export { x as ASK_TOOL_DESCRIPTION, y as ASK_TOOL_NAME, z as AgentCatalogEntry, B as AgentHistoryWindow, C as AskToolInput, F as DEFAULT_INCREMENTAL_LOOKBACK_CHARS, G as DEFAULT_INTAKE_PREAMBLE, J as DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS, K as DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS, N as ElicitationOption, R as ElicitationOutcome, V as ElicitationQuestion, W as ElicitationReply, X as ElicitationResult, Y as HistoryPolicyContext, Z as HistorySelection, _ as HistorySummary, $ as IncrementalGating, a0 as InvokeWithTransientRetryOptions, a1 as MAX_ASK_QUESTIONS, a2 as MessageRole, a3 as OutputRejectedError, a4 as OutputVerdict, a5 as ProcessorFailedError, a6 as PromptContext, a7 as QuotaView, a8 as ToolKind, a9 as ToolStepCtx, aa as ToolTransientRetryNumbers, ab as ToolTransientRetryOptions, ac as askInputSchema, ad as askToolDefinition, ae as invokeWithTransientRetry, af as isTransientToolError, ag as normalizeElicitationReply, ah as renderElicitationAnswers, ai as resolveElicitation, aj as resolveToolTransientRetryNumbers, ak as settleElicitation } from './tool-CL9oEytW.js';
1
3
  import { StandardSchemaV1 } from '@standard-schema/spec';
2
4
  import { ChannelRegistry } from '@dudousxd/nestjs-diagnostics';
3
5
 
4
- /**
5
- * Asking the USER a structured question, and waiting for the answer.
6
- *
7
- * `awaitApproval` collects a yes/no about work already proposed; this collects the scope BEFORE the
8
- * work. Two surfaces produce it — a configured intake (`AgentLoopDeps.intake`) and the model-callable
9
- * `ask` tool (`AgentLoopDeps.ask`) — and they deliberately produce the SAME {@link
10
- * ElicitationRequest}, persist through the same tool-call row, and resume through the same
11
- * `tool:<runId>:<callId>` signal. A consumer cannot tell which one asked, and should not have to.
12
- */
13
-
14
- /** One choice a question offers. */
15
- interface ElicitationOption {
16
- /** Stable identifier submitted back. Never shown to the user. */
17
- value: string;
18
- /** What the user reads. */
19
- label: string;
20
- /**
21
- * A single character a UI may bind as a keyboard shortcut for this option. Advisory — nothing in
22
- * the library reads it, and a client is free to render its own.
23
- */
24
- hotkey?: string;
25
- }
26
- /** One question in a set. */
27
- interface ElicitationQuestion {
28
- /** Unique within its request; the key answers come back under. */
29
- id: string;
30
- prompt: string;
31
- options: ElicitationOption[];
32
- /** More than one option may be chosen. Omit → single choice. */
33
- multiple?: boolean;
34
- /**
35
- * The options already picked for the user. The claim this whole surface makes is that confirming
36
- * is enough, so a question with no defaults is a question the user must stop and think about —
37
- * which is the case the design is trying to avoid. Empty/omitted is allowed and means exactly
38
- * that: submitting without answering leaves this question unanswered.
39
- */
40
- defaults?: string[];
41
- /** Accept values that are not among `options` (a typed-in answer). Omit → options only. */
42
- allowFreeText?: boolean;
43
- }
44
- /**
45
- * A question set awaiting a human. Identical in shape whether an `@Agent`'s configured intake or
46
- * the model's `ask` tool authored it — `source` records which, for audit, not for control flow.
47
- *
48
- * `questions.length` is known when the request is written, which is what lets a client render
49
- * "Question 1 of 3" without guessing whether a fourth is coming.
50
- */
51
- interface ElicitationRequest {
52
- /** The tool-call id this request is persisted under, and the signal it is answered through. */
53
- id: string;
54
- source: 'intake' | 'ask';
55
- /** What the assistant says above the form. */
56
- preamble?: string;
57
- questions: ElicitationQuestion[];
58
- }
59
- /** What a human sent back for an {@link ElicitationRequest}. */
60
- interface ElicitationReply {
61
- /**
62
- * questionId → chosen values. A question whose id is ABSENT takes the request's own `defaults` —
63
- * that is what makes "just submit" mean "yes, your pre-picked answers". A present-but-empty array
64
- * is an explicit "none of these" and does NOT fall back.
65
- */
66
- answers: Record<string, string[]>;
67
- /**
68
- * The user declined to answer and told the agent to proceed on its own assumptions. Distinct from
69
- * confirming the defaults even though the resulting values are the same: one is a decision the
70
- * user made, the other is one they refused to make, and only the first is evidence of intent.
71
- */
72
- skipped?: boolean;
73
- /** Opaque ref of WHO answered, when it wasn't the run's own actor. */
74
- answeredByRef?: string;
75
- }
76
- /** A settled elicitation: what the agent proceeds on, and how it got there. */
77
- interface ElicitationOutcome {
78
- /** One entry per question, in request order — always present, so a caller never re-applies defaults. */
79
- answers: Record<string, string[]>;
80
- skipped: boolean;
81
- /** Question ids filled from the request's `defaults` rather than by the human. */
82
- defaulted: string[];
83
- }
84
- /**
85
- * Read whatever the human channel delivered as an {@link ElicitationReply}.
86
- *
87
- * A question set is persisted as an `action` tool call in `pending_approval` — that is what puts it
88
- * in the approvals inbox a deployment already has, instead of needing one of its own. The cost of
89
- * that choice is that the thing which comes back may be a {@link Decision} someone pressed
90
- * Approve/Reject on rather than a set of answers, and a `Decision` carries no `answers` at all.
91
- *
92
- * Approve means every question keeps its own pre-picked `defaults`, which is exactly what "just
93
- * submit" already means on this surface; Reject is the same declining-to-answer a skip is. Neither
94
- * reading is a guess — a yes/no channel cannot say more than that, and saying it here is what lets
95
- * one inbox settle both kinds of pending work.
96
- *
97
- * Returns the reply UNCHANGED when it already carries answers, so the common path allocates nothing
98
- * and a caller can identity-compare.
99
- */
100
- declare function normalizeElicitationReply(reply: ElicitationReply | Decision): ElicitationReply;
101
- /**
102
- * Settle a reply against the request it answers: fill every unanswered question from its own
103
- * `defaults`, drop submitted values that aren't on offer, and collapse a single-choice question to
104
- * one value.
105
- *
106
- * PURE, and deliberately so. Both of its inputs are already journaled by the time the loop calls it
107
- * — the request came from module config or from an `llm:<i>` checkpoint, the reply from the signal
108
- * checkpoint — so every process replaying the turn reaches the same values without a checkpoint of
109
- * its own. Resolving defaults in the HTTP layer instead would put them behind a store read that a
110
- * replay would have to repeat.
111
- */
112
- declare function resolveElicitation(request: ElicitationRequest, raw: ElicitationReply | Decision): ElicitationOutcome;
113
- /**
114
- * What a settled elicitation looks like to everyone downstream: the model reading it back as a tool
115
- * result, the thread reader rendering it, the auditor asking what the agent was told to do. One
116
- * shape for both surfaces — nothing here records which of them asked.
117
- */
118
- interface ElicitationResult extends ElicitationOutcome {
119
- /** The questions against the chosen LABELS, so a reader (and a model) can act on it. */
120
- summary: string;
121
- }
122
- /** {@link resolveElicitation} plus its human-readable rendering. Pure, for the same reason. */
123
- declare function settleElicitation(request: ElicitationRequest, reply: ElicitationReply | Decision): ElicitationResult;
124
- /**
125
- * The answers as the model reads them: the question's own prompt against the chosen options' LABELS,
126
- * not their opaque `value`s — a model shown `{"scope":["b"]}` has been told nothing.
127
- */
128
- declare function renderElicitationAnswers(request: ElicitationRequest, outcome: ElicitationOutcome): string;
129
- /** The reserved tool name the model calls to ask the user something. */
130
- declare const ASK_TOOL_NAME = "ask";
131
- /** What the model must supply when it calls `ask`. */
132
- interface AskToolInput {
133
- preamble?: string;
134
- questions: ElicitationQuestion[];
135
- }
136
- /** How many questions one `ask` may carry. A form the user has to scroll is a form they skip. */
137
- declare const MAX_ASK_QUESTIONS = 5;
138
- /**
139
- * The `ask` tool's input schema, hand-written rather than borrowed from a validation library: core
140
- * depends on no validator, and the schema has to carry a JSON Schema a provider can constrain
141
- * generation against. It publishes one through the Standard JSON Schema extension
142
- * (`~standard.jsonSchema.input`), which is the path the AI SDK adapter already recognises for
143
- * Valibot / ArkType / Zod 4.
144
- */
145
- declare const askInputSchema: StandardSchemaV1<unknown, AskToolInput>;
146
- /**
147
- * What the model is told the `ask` tool is for. Written to discourage the two failure modes that
148
- * make a clarifying question worse than a guess: asking about something the conversation already
149
- * settled, and asking without saying what you would have done.
150
- */
151
- declare const ASK_TOOL_DESCRIPTION = "Ask the user to settle the scope of the work before you do it. Use it when a reasonable person would produce a materially different result depending on the answer \u2014 not to confirm something the conversation already says. Every question must pre-pick the answer you would choose, so the user can confirm instead of deciding. The user may decline, in which case you proceed on those pre-picked answers.";
152
- /**
153
- * The `ask` tool as the model sees it. NOT a `ToolSpec` and never registered: `ask` has no handler,
154
- * because the loop settles it against a human instead of invoking anything. Keeping it out of the
155
- * `ToolRegistry` is also what keeps the kind decision off a process-local lookup — see
156
- * `claimToolCall`.
157
- */
158
- declare function askToolDefinition(): ToolDefinition;
159
- /** A question set an `@Agent` asks before it starts working. See `AgentLoopDeps.intake`. */
160
- interface AgentIntake {
161
- questions: ElicitationQuestion[];
162
- /** What the assistant says above the form. Omit → {@link DEFAULT_INTAKE_PREAMBLE}. */
163
- preamble?: string;
164
- /**
165
- * `'thread-start'` (default) asks once, on the first turn of a thread; `'every-turn'` asks before
166
- * every turn. Both are decided from what `load:thread` recorded about the thread when the turn
167
- * began, never from anything this process happens to know — by the time a replay reaches the
168
- * question, the thread already holds the assistant message the first attempt wrote.
169
- */
170
- when?: 'thread-start' | 'every-turn';
171
- }
172
- declare const DEFAULT_INTAKE_PREAMBLE = "A few questions before I start. I have pre-picked what I would choose, so confirming is enough.";
173
-
174
- /**
175
- * The ceiling on how much of a thread rides into a turn. Without one, `runAgentLoop` maps EVERY
176
- * message the store returns into the model's messages, so a long-lived thread grows until the
177
- * provider rejects the request — and every turn before that one pays for the whole transcript.
178
- * `@dudousxd/nestjs-agent-core` ships `windowHistory` as the built-in; anything satisfying this SPI
179
- * works. Wire it as `AgentLoopDeps.historyPolicy`, or via `AgentModule.forRoot({ history })` /
180
- * `@Agent({ history })`.
181
- */
182
-
183
- /** Whose history this is — enough for a policy to size the window per agent or per actor. */
184
- interface HistoryPolicyContext {
185
- threadId: string;
186
- actor: Actor;
187
- /** The agent running this turn. Undefined → the default agent. */
188
- agentName?: string;
189
- }
190
- /** How a policy split the thread: what rides into the turn, and what the ceiling left out. */
191
- interface HistorySelection {
192
- /** Sent to the model, oldest-first. */
193
- keep: ModelMessage[];
194
- /** Left out, oldest-first. Folded into a leading summary when the policy implements `summarize`. */
195
- drop: ModelMessage[];
196
- }
197
- /** A stand-in for the messages a window left out, plus what producing it cost. */
198
- interface HistorySummary {
199
- /** Prose the loop folds into the window as a leading `system` message. */
200
- text: string;
201
- /**
202
- * What the summarizer spent, when it called a model. Recorded as a `history_summary` usage row, so
203
- * a ceiling on context cost cannot itself become spend nothing accounts for. Omit for a summarizer
204
- * that calls no model (a rollup of tool names, a digest the app already stored).
205
- */
206
- usage?: MessageUsage;
207
- /** Accounting label for the model that produced it; falls back to `AgentLoopDeps.modelId`. */
208
- modelId?: string;
209
- }
210
- interface HistoryPolicy {
211
- /**
212
- * The most messages {@link select} can ever keep, where the ceiling can be stated as a row count.
213
- *
214
- * A hint the loop hands to the store, which then reads that many of the thread's newest rows
215
- * rather than its whole transcript (see `ThreadTurnReader`). Declaring it is a PROMISE about
216
- * `select`: that it keeps at most this many messages, and that they are the NEWEST ones — so a
217
- * window of this size is indistinguishable, to `select`, from the full transcript. A policy that
218
- * can keep more than this, or can keep something older than the newest `maxMessages`, must omit it
219
- * rather than select over rows the store was never asked for.
220
- *
221
- * Omit where the ceiling is not a row count at all — a token budget alone cannot name one, since
222
- * one message can be four tokens or forty thousand. Omitting costs only the bound on the READ; the
223
- * prompt is identical either way.
224
- */
225
- readonly maxMessages?: number;
226
- /**
227
- * Decide what the model sees.
228
- *
229
- * MUST be a pure function of `messages`. The loop calls it INSIDE the `load:thread` checkpoint and
230
- * records its result there, so the ceiling bounds the journal as well as the prompt: what the
231
- * checkpoint holds is the selection rather than the store's whole `ThreadDetail`, on a payload
232
- * every replay re-reads. It still adds no position of its own — a policy cannot change the name or
233
- * position of a single existing checkpoint.
234
- *
235
- * Read a clock, a feature flag or a database here and the resumed run windows differently from the
236
- * one that suspended: the model gets a different prompt, and any step whose existence depends on
237
- * the split lands at a position the history has no room for. Anything non-deterministic belongs in
238
- * {@link summarize}, which has a checkpoint of its own.
239
- *
240
- * The newest message must always be in `keep` — dropping it leaves the turn with nothing to answer.
241
- */
242
- select(messages: ModelMessage[], ctx: HistoryPolicyContext): HistorySelection;
243
- /**
244
- * Fold the dropped messages into prose the model reads in their place, prepended to the window as
245
- * a `system` message. Optional — without it, dropped messages are simply gone, and `load:thread`
246
- * does not record them either: a summarizer is the only thing that ever reads them back.
247
- *
248
- * Runs inside the loop's `history:summarize` checkpoint, so it may call a model or hit the
249
- * network: the first attempt's result is journaled and every replay reads it back instead of
250
- * re-summarizing. It runs once per RUN (not per model step), and only when `select` actually
251
- * dropped something.
252
- */
253
- summarize?(dropped: ModelMessage[], ctx: HistoryPolicyContext): Promise<HistorySummary>;
254
- }
255
- /**
256
- * The window an agent asks for declaratively — `AgentModule.forRoot({ history })` and
257
- * `@Agent({ history })`. Plain data, so it can live in a decorator's metadata; the NestJS layer
258
- * turns it into a `windowHistory` policy. A consumer needing anything the window can't express
259
- * supplies a {@link HistoryPolicy} instead.
260
- */
261
- interface AgentHistoryWindow {
262
- /** Keep at most this many of the newest messages. */
263
- maxMessages?: number;
264
- /** Keep the newest messages whose estimated tokens fit this budget. */
265
- maxTokens?: number;
266
- /** Fold what the window left out into a leading summary — one extra model call per run. */
267
- summarize?: boolean;
268
- }
269
-
270
- /**
271
- * Transient tool-error classification + the retry loop that wraps a tool's own invocation. A
272
- * classified-transient error (a DB deadlock, a lock-wait timeout, a serialization failure) means
273
- * the server rolled the tool's work back — retrying THAT class is safe, unlike a tool's general
274
- * business failure, which stays a one-shot outcome (no durable step retries: a tool may not be
275
- * idempotent). See `runAgentLoop`'s `tool:<call.id>` step body and `AgentRunSteps.tool` — both wrap
276
- * `registry.invoke(...)` with {@link invokeWithTransientRetry} so a retry never becomes a new
277
- * checkpoint; history still shows exactly one step per tool call.
278
- */
279
- /**
280
- * Default transient-tool-error classifier: true for a recognized MySQL/Postgres/SQLite
281
- * lock-contention shape (by driver `code`/`errno`/`sqlState`, or a matching message), checked on
282
- * the error itself and one level of `cause` (drivers commonly wrap the original error). A plain
283
- * `Error` with none of these markers — any other business failure — is `false`.
284
- */
285
- declare function isTransientToolError(error: unknown): boolean;
286
- /** Total attempts (initial try + retries) when `toolTransientRetry` doesn't set `attempts`. */
287
- declare const DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS = 2;
288
- /** Backoff base in ms — the wait between attempt N and N+1 is `backoffMs * N`. */
289
- declare const DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS = 150;
290
- /** The host-configurable half of the policy — everything except the (non-wire-safe) `classify` fn. */
291
- interface ToolTransientRetryOptions {
292
- /** Total attempts (initial try + retries). Defaults to {@link DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS}. */
293
- attempts?: number;
294
- /** Backoff base in ms. Defaults to {@link DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS}. */
295
- backoffMs?: number;
296
- /** Overrides the default classifier — widen or narrow which errors are treated as transient. */
297
- classify?: (error: unknown) => boolean;
298
- }
299
- /** `false` disables transient retry entirely — a tool's own thrown error surfaces immediately. */
300
- type ToolTransientRetrySetting = ToolTransientRetryOptions | false;
301
- /** Just the wire-safe (numeric) half of a resolved policy — what a dispatched envelope carries. */
302
- interface ToolTransientRetryNumbers {
303
- attempts: number;
304
- backoffMs: number;
305
- }
306
- /**
307
- * Resolves the numeric half of `toolTransientRetry` for the dispatched wire envelope: `false` when
308
- * explicitly disabled, else concrete `{ attempts, backoffMs }` (defaults filled in) — never
309
- * `undefined`, so the dispatched handler always gets a definite answer instead of re-deriving its
310
- * own default. The `classify` function never rides this — it isn't wire-safe; the dispatched
311
- * handler resolves its own `classify` from its local module options (see `AgentRunSteps.tool`).
312
- */
313
- declare function resolveToolTransientRetryNumbers(setting: ToolTransientRetrySetting | undefined): ToolTransientRetryNumbers | false;
314
- interface InvokeWithTransientRetryOptions {
315
- /**
316
- * Widens what counts as a control-flow signal (durable suspend / continue-as-new) so a retry never
317
- * swallows one — same rule the loop's tool catch applies. An ADDITION to the built-in marker check
318
- * ({@link isControlFlowSignal}), which already covers every signal the durable runtimes raise;
319
- * supply this only for a runner whose signals carry no marker.
320
- */
321
- isControlFlowError?: (error: unknown) => boolean;
322
- /**
323
- * Called before each wait-and-retry, with the 1-based ordinal of the attempt that just failed and
324
- * the error it threw. The call site uses this to emit the `tool.retry` diagnostics point event —
325
- * `invokeWithTransientRetry` itself carries no tool identity (name/callId), only the thunk.
326
- */
327
- onRetry?: (attempt: number, error: unknown) => void;
328
- }
329
- /**
330
- * Retries `fn` in place — never a new durable step/checkpoint, just repeated attempts inside
331
- * whichever step body already wraps this call. `setting: false` runs `fn` once, unwrapped (no
332
- * classify/backoff bookkeeping at all). Otherwise: try; on a thrown error, rethrow immediately if
333
- * it's a recognized control-flow signal, else if the (possibly custom) classifier calls it
334
- * transient AND attempts remain, wait `backoffMs * attemptNumber` and retry; otherwise rethrow the
335
- * error as-is.
336
- */
337
- declare function invokeWithTransientRetry<T>(fn: () => Promise<T>, setting: ToolTransientRetrySetting, options?: InvokeWithTransientRetryOptions): Promise<T>;
338
-
339
- /** Who is driving the turn. Roles + tenant come from the host app (nestjs-context/authz). */
340
- interface Actor {
341
- id: string;
342
- /** The caller's roles. Tool authorization is a set intersection against a tool's `roles`. */
343
- roles?: string[];
344
- tenantRef?: string;
345
- }
346
- type ToolKind = 'read' | 'action' | 'agent' | 'ask' | 'skill' | 'memory';
347
- /**
348
- * One agent->agent edge on {@link AgentDefinition.delegatesTo}. A bare name is the awaited
349
- * delegation that has always existed; the object form is how an author says this one runs in the
350
- * background.
351
- */
352
- type AgentDelegation = string | {
353
- agent: string;
354
- detached?: boolean;
355
- };
356
- /**
357
- * Where a detached sub-agent run posts its answer: the thread that delegated it, and the
358
- * `agent`-kind tool call that started it. Carried on the child run's own {@link AgentRunInput},
359
- * because by the time the child finishes the parent turn is over and nothing is holding the
360
- * address.
361
- */
362
- interface DetachedDelivery {
363
- threadId: string;
364
- toolCallId: string;
365
- }
366
- /**
367
- * Declared shape of a tool.
368
- * - `read` auto-executes.
369
- * - `action` never auto-executes — requires HITL approval.
370
- * - `agent` delegates to another named agent (durable: a child workflow; inline: a nested loop),
371
- * handled at the loop level — NOT via a handler. Carries `targetAgent`.
372
- * - `ask` puts a question set to the user and waits for the answers (see `elicitation.ts`).
373
- * Handled at the loop level and never registered, so no `ToolSpec` carries this kind:
374
- * the only tool that has it is the built-in `ask`, whose definition the loop supplies.
375
- * - `skill` reads one of the procedures offered in the turn's `<skills>` catalog (see `skills.ts`).
376
- * Auto-executes like a read and performs nothing, but is served by the LOOP from the
377
- * catalog the journal holds rather than by a registered handler — so, like `ask`, no
378
- * `ToolSpec` carries this kind.
379
- * - `memory` records one fact about the actor for later turns (see `memory.ts`). Served by the LOOP
380
- * and never registered, like `skill`. It WRITES, but auto-executes rather than asking
381
- * for approval: an agent may only ever write the scope of the actor it is running for,
382
- * so the blast radius of a bad one is the prompt of the person who was talking, and the
383
- * remedy is the read-back that lets them delete it.
384
- */
385
- interface ToolSpec {
386
- name: string;
387
- kind: ToolKind;
388
- description: string;
389
- /**
390
- * Input schema as a [Standard Schema](https://standardschema.dev) — validation-agnostic, so
391
- * Zod, Valibot, or ArkType all work. The loop validates input via `~standard.validate` before
392
- * running the handler, and providers convert it to the model's tool-parameter JSON schema.
393
- */
394
- inputSchema: StandardSchemaV1;
395
- /** For `kind: 'agent'` — the name of the agent to delegate to. */
396
- targetAgent?: string;
397
- /**
398
- * For `kind: 'agent'` — start the delegation and let the calling turn END, instead of holding it
399
- * open until the delegate answers. The call's result is a {@link DetachedDelegationReceipt}, and
400
- * the answer arrives later as its own message in the same thread (see
401
- * {@link AgentRunInput.deliverTo}).
402
- *
403
- * Authored per EDGE, never chosen by the model: a model that can decide to detach can decide to
404
- * detach the one thing the user is sitting there waiting for, and it has no way to know which that
405
- * is. The person wiring `A -> B` does.
406
- *
407
- * Settled into the call's `persist:toolcall` checkpoint alongside `targetAgent`, so every replay
408
- * reads the branch back rather than re-deciding it against a registry that may have changed.
409
- */
410
- detached?: boolean;
411
- /** Roles allowed to invoke. Undefined → defaults applied by RolesPolicy (e.g. ADMIN-only). */
412
- roles?: string[];
413
- /**
414
- * Whether the tool exists in this deployment. `false` (or a predicate returning `false`) drops it
415
- * before the role filter, so it is never offered to the model and cannot be invoked. Undefined →
416
- * enabled.
417
- *
418
- * A predicate is re-evaluated every turn, so a flag flipped at runtime takes effect on the next
419
- * message with nothing re-registered. For availability that depends on injected services, put
420
- * `isEnabled()` on the handler instead — a spec is data, a handler is a provider.
421
- */
422
- enabled?: boolean | (() => boolean | Promise<boolean>);
423
- /**
424
- * An authorization ability name (e.g. 'cache.purge'). Consumed by an ability-aware RolesPolicy
425
- * such as the `@dudousxd/nestjs-agent-authz` Gate adapter. Apps that don't use authz ignore it
426
- * and rely on `roles` instead — both live on the same SPI, so neither is required.
427
- */
428
- ability?: string;
429
- }
430
- /** What the model is told a tool looks like (no handler, no host types). */
431
- interface ToolDefinition {
432
- name: string;
433
- kind: ToolKind;
434
- description: string;
435
- inputSchema: StandardSchemaV1;
436
- }
437
- /** A tool call the model asked for during a turn. */
438
- interface ToolCallRequest {
439
- id: string;
440
- name: string;
441
- input: unknown;
442
- /**
443
- * The tool's declared kind (`ToolSpec.kind`), stamped where the tool was OFFERED — inside the llm
444
- * checkpoint, by the process that built the definition list the model chose from. It travels with
445
- * the call from there, so thread-read consumers know a call's kind without hardcoding a tool-name
446
- * allowlist, and the approval branch does not depend on which process replays the turn.
447
- * Undefined only for a call that predates the stamp, or one no registry could resolve
448
- * (defensively treated as `read` wherever a definite value is required).
449
- */
450
- kind?: ToolKind;
451
- }
452
- /** Result of running a tool. */
453
- interface ToolResult {
454
- /**
455
- * A person declined this action, so the tool never ran. Set INSTEAD of a failure, and read by
456
- * every consumer that has to tell the two apart — the stream frame, the replayed transcript. The
457
- * `error` field still carries what the MODEL is told, because that is the channel a model reads a
458
- * tool's outcome on; this flag is what everything else reads.
459
- */
460
- denied?: true;
461
- id: string;
462
- name: string;
463
- output: unknown;
464
- error?: string;
465
- }
466
- interface MessageUsage {
467
- /**
468
- * Total input (prompt) tokens for the turn — the whole input side, cached and uncached alike.
469
- * `cacheWriteTokens` + `cacheReadTokens` are subsets of this count, not additions to it, so
470
- * token totals and quota never change when a breakdown is present.
471
- */
472
- inputTokens: number;
473
- /** Total output (completion) tokens for the turn; `reasoningTokens` is a subset of this. */
474
- outputTokens: number;
475
- /**
476
- * How many of `inputTokens` were written to the prompt cache this turn (billed at a premium,
477
- * ~1.25× base input). Undefined when the provider doesn't report caching. Refines the cost
478
- * estimate only — priced by the pricing row's cache-write rate (falling back to the input rate).
479
- */
480
- cacheWriteTokens?: number;
481
- /**
482
- * How many of `inputTokens` were served from the prompt cache this turn (billed at a discount,
483
- * ~0.1× base input). Undefined when the provider doesn't report caching.
484
- */
485
- cacheReadTokens?: number;
486
- /**
487
- * How many of `outputTokens` the model spent on reasoning/thinking. Observability only — reasoning
488
- * tokens are billed at the output rate, so they don't change the cost estimate. Undefined for
489
- * non-reasoning models or providers that don't report it.
490
- */
491
- reasoningTokens?: number;
492
- /**
493
- * This turn's USD cost: the provider's own reported figure when it has one, else an estimate from
494
- * the bound `AgentPricingStore` (cached once per run — see `AgentLoopDeps.pricingStore`), else
495
- * `null` when no pricing store is bound or the model has no price row. Never `0` for an unpriced
496
- * model — a real $0 turn and "we don't know" must stay distinguishable.
497
- */
498
- costUsd?: number | null;
499
- }
500
- /**
501
- * What a usage row was spent ON. `chat` is a model step of the turn itself; `follow_ups` is the
502
- * extra call that proposes follow-up questions; `history_summary` is the extra call a
503
- * {@link import('./spi/history-policy.js').HistoryPolicy} makes to fold windowed-out messages into a
504
- * summary — so bounding context cost never becomes spend nothing accounts for; `structured_output`
505
- * is the formatting pass that restates a finished answer as `AgentLoopDeps.outputSchema` requires.
506
- */
507
- type UsagePurpose = 'chat' | 'follow_ups' | 'history_summary' | 'structured_output';
508
- interface QuotaState {
509
- usedTokens: number;
510
- limitTokens: number;
511
- withinLimit: boolean;
512
- }
513
- /**
514
- * The read-model the quota-today endpoint returns to a client — a superset of {@link QuotaState}
515
- * for rendering a usage badge. `limitTokens` is `null` when no quota is configured (unlimited, so
516
- * `withinLimit` is always true); `costUsd` is the day's summed provider-reported USD spend (`0`
517
- * when only tokens were reported).
518
- */
519
- interface QuotaView {
520
- usedTokens: number;
521
- limitTokens: number | null;
522
- withinLimit: boolean;
523
- costUsd: number;
524
- }
525
- /**
526
- * Anything a human sends back into a parked run: a {@link Decision} on an action tool, or an
527
- * `ElicitationReply` answering a question set. Both travel the same `tool:<runId>:<toolCallId>`
528
- * signal, so the runner that delivers them does not need to know which it is carrying.
529
- */
530
- type HumanReply = Decision | ElicitationReply;
531
- /** A human decision on a pending action tool call. */
532
- interface Decision {
533
- approved: boolean;
534
- reason?: string;
535
- /**
536
- * Opaque ref of WHO decided (e.g. a console admin). When absent, the run's own actor decided
537
- * (the chat flow).
538
- */
539
- executedByRef?: string;
540
- }
541
- type MessageRole = 'user' | 'assistant' | 'system';
542
- /**
543
- * A file a user attached to a message so a vision-capable model sees it natively (an image, a PDF).
544
- * The lib stays provider-agnostic: it passes {@link MessageAttachment.url} straight through as the
545
- * model's image/file part data — making that URL reachable by the provider (presigned S3, a proxy)
546
- * is the consumer's job. The lib never fetches bytes or talks to a store.
547
- */
548
- interface MessageAttachment {
549
- /** Stable id of the stored media object in the consumer's media store. Provenance + replay key. */
550
- mediaId: string;
551
- /** A URL the model provider can fetch the bytes from at turn time. */
552
- url: string;
553
- /** MIME type — routes the part: `image/*` → image part, otherwise → file part. */
554
- contentType: string;
555
- /** Original filename, for display and the file part's filename. */
556
- name: string;
557
- }
558
- /** A neutral chat message exchanged with the model. */
559
- interface ModelMessage {
560
- role: MessageRole;
561
- content: string;
562
- toolCalls?: ToolCallRequest[];
563
- toolResults?: ToolResult[];
564
- /** User-message attachments (image/PDF), rendered as native model content parts by the adapter. */
565
- attachments?: MessageAttachment[];
566
- }
567
- interface PageContext {
568
- kind?: string;
569
- [key: string]: unknown;
570
- }
571
- /**
572
- * Inputs a {@link PromptBuilder} or {@link PromptContributor} may use to compose the system prompt
573
- * for a turn. Resolved once per turn from stable inputs (actor / agent / pageContext) so it stays
574
- * replay-safe.
575
- */
576
- interface PromptContext {
577
- actor: Actor;
578
- /** The selected agent's name. */
579
- agentName: string;
580
- pageContext?: PageContext;
581
- }
582
- /**
583
- * An agent's base system prompt. Return a string (optionally async) built from the turn's context —
584
- * e.g. injecting the actor, the current page, or a data-shape description. Set on an `@Agent` class
585
- * via a `@SystemPrompt()` method (or a flat string).
586
- */
587
- type PromptBuilder = (ctx: PromptContext) => string | Promise<string>;
588
- /**
589
- * A cross-agent system-prompt contributor. Returns an ordered section to APPEND to the composed
590
- * prompt (after the agent's base), or `null` to contribute nothing this turn — so conditional
591
- * sections (base-scope, a mentions legend, schema hints) stay clean when they don't apply.
592
- * Registered app-wide via `@SystemPromptContributor()`; the loop runs every contributor in order.
593
- */
594
- type PromptContributor = (ctx: PromptContext) => string | null | Promise<string | null>;
595
- /** Everything needed to run one agent turn. */
596
- interface AgentRunInput {
597
- threadId: string;
598
- actor: Actor;
599
- /** The latest user message text. */
600
- userText: string;
601
- /** Files attached to the latest user message (image/PDF). Persisted with it and sent to the model. */
602
- attachments?: MessageAttachment[];
603
- pageContext?: PageContext;
604
- /** YYYY-MM-DD stamped by the runner so quota/day stays deterministic under durable replay. */
605
- day?: string;
606
- /** Which named agent runs this turn. Omitted → the default/single agent. */
607
- agentName?: string;
608
- /**
609
- * How many agent→agent delegations deep this run already is (0 for a top-level turn). The runner
610
- * increments it for each child run; the loop refuses to delegate past its depth ceiling.
611
- */
612
- delegationDepth?: number;
613
- /**
614
- * The named agents already on this delegation chain, root first — what {@link delegationDepth}
615
- * counts, spelled out. The runner appends its own agent's name for each child it starts.
616
- *
617
- * A count can only say a chain is LONG. This says whether it is going in circles, and how often:
618
- * an agent that appears here is one the chain has already passed through, so a delegation back to
619
- * it is a cycle by inspection rather than by proxy. A run whose runner does not supply it falls
620
- * back to the depth ceiling alone.
621
- */
622
- delegationPath?: readonly string[];
623
- /**
624
- * When set, this run streams into ANOTHER run's sink instead of its own. A sub-agent run carries
625
- * its top-level ancestor's runId here so its tokens (and its pending action-tool frames) land in
626
- * the live stream the human is already watching — the only way a human can see, and therefore
627
- * approve, a sub-agent's HITL action. Propagated unchanged down the delegation chain.
628
- */
629
- sinkRunId?: string;
630
- /**
631
- * Set on a DETACHED sub-agent run: the thread and tool call this run answers into when it
632
- * finishes. Its presence is also what makes a run detached from the inside — it has no ancestor
633
- * sink to stream into, so nothing else distinguishes it from a top-level turn.
634
- */
635
- deliverTo?: DetachedDelivery;
636
- /**
637
- * The run that started this one (a delegation's parent). Recorded with the run so a governance
638
- * surface can roll a delegation's cost up to the turn that asked for it; a detached child is
639
- * otherwise a row with nothing pointing at it.
640
- */
641
- parentRunId?: string;
642
- /**
643
- * Re-run the last exchange instead of adding a new message: the loop truncates everything after
644
- * the thread's last user message and re-answers it (no `userText` is appended). Used by a
645
- * "regenerate" button. `userText` is ignored when set.
646
- */
647
- regenerate?: boolean;
648
- }
649
- /**
650
- * A named agent: its prompt, the tools it may use, and who it can hand off to. This is the
651
- * internal record the loop and `AgentDepsFactory` consume; in an app it is authored as an
652
- * `@Agent`-decorated class and populated into the `AgentRegistry` by discovery (name, base prompt
653
- * from `@SystemPrompt`, tool allow-list, handoff targets). An orchestrator hands off to others via
654
- * `ctx.handoff(OtherAgent)`. Model/store/sink/governance are shared from the module.
655
- */
656
- interface AgentDefinition {
657
- name: string;
658
- /** Human-readable summary from `@Agent({ description })`. Surfaced by the `GET agents` catalog. */
659
- description?: string;
660
- /** Base prompt for this agent. A flat string, or a {@link PromptBuilder} resolved per turn. */
661
- systemPrompt?: string | PromptBuilder;
662
- /** Allow-list of tool names this agent may use (subset of all registered tools). */
663
- tools?: string[];
664
- /**
665
- * Other agents this agent may hand off to (auto-registered as `agent`-kind tools). A bare name
666
- * is the awaited form; `{ agent, detached: true }` starts the delegate and lets this agent's turn
667
- * finish without its answer — see {@link ToolSpec.detached}.
668
- */
669
- delegatesTo?: AgentDelegation[];
670
- modelId?: string;
671
- maxSteps?: number;
672
- /**
673
- * How deep delegation may nest below this agent. Undefined → {@link MAX_DELEGATION_DEPTH}.
674
- *
675
- * Bounds the CHAIN, not the fan-out: how many agents a turn delegates to is the model's.
676
- */
677
- maxDelegationDepth?: number;
678
- /**
679
- * How many times one agent may appear on a single delegation chain.
680
- * Undefined → {@link DEFAULT_MAX_AGENT_APPEARANCES}.
681
- */
682
- maxAgentAppearances?: number;
683
- /**
684
- * This agent's own ceiling on how much of a thread rides into its turn, overriding the
685
- * module-wide one. A persona that reasons over a long back-and-forth and one that answers a single
686
- * question from a page context want very different windows.
687
- */
688
- history?: AgentHistoryWindow;
689
- /**
690
- * Constrain this agent's final answer to a schema. A live schema INSTANCE, so it is resolved from
691
- * DI on whichever process runs the turn and never travels on `AgentRunInput` — a Standard Schema
692
- * cannot survive the JSON hop into a durable workflow, which is why there is no per-request
693
- * override on the HTTP surface.
694
- */
695
- outputSchema?: StandardSchemaV1;
696
- /**
697
- * Extra model calls allowed to fix an answer that failed {@link outputSchema}. Undefined → 1.
698
- */
699
- outputRepairAttempts?: number;
700
- /**
701
- * Questions this agent puts to the user BEFORE it starts working. Authored, so the turn pays no
702
- * model call to produce them and a client knows the total up front. Undefined → no intake.
703
- */
704
- intake?: AgentIntake;
705
- /**
706
- * Whether this agent is offered the built-in `ask` tool. Undefined → the module-wide setting.
707
- */
708
- ask?: boolean;
709
- }
710
- /**
711
- * The read-model the `GET agents` endpoint returns to a client — the safe public subset of an
712
- * {@link AgentDefinition} so a host can render a persona picker instead of hardcoding one.
713
- */
714
- interface AgentCatalogEntry {
715
- name: string;
716
- description: string;
717
- /** Whether this is the agent a turn uses when the caller names none. Omitted when not the default. */
718
- isDefault?: boolean;
719
- }
720
- interface ThreadSummary {
721
- id: string;
722
- title: string;
723
- transient: boolean;
724
- createdAt: string;
725
- updatedAt: string;
726
- lastMessagePreview?: string;
727
- /**
728
- * The agent a `chat()` call on this thread uses when the caller doesn't name one explicitly.
729
- * Optional — undefined for a store that doesn't implement `AgentStore.updateThread` (the only
730
- * way to set it). The REST/service read-model normalizes this to `null` when absent.
731
- */
732
- defaultAgent?: string | null;
733
- /**
734
- * The runId of a currently-running turn on this thread, or `null` if none is running. Optional —
735
- * undefined for a store that doesn't implement `AgentStore.activeRunForThread`. The REST/service
736
- * read-model normalizes this to `null` when absent, so a client can always do `?? null`.
737
- */
738
- activeRunId?: string | null;
739
- }
740
- interface StoredMessage {
741
- id: string;
742
- role: MessageRole;
743
- content: string;
744
- /** Which agent produced this message (assistant messages) — provenance for replay / UI / telescope. */
745
- agentName?: string;
746
- toolCalls?: ToolCallRequest[];
747
- toolResults?: ToolResult[];
748
- /** Files the user attached to this message (image/PDF). Persisted with the message, replayed as-is. */
749
- attachments?: MessageAttachment[];
750
- followUps?: string[];
751
- usage?: MessageUsage;
752
- /** The run (turn) that produced this message; absent on a row written before this was recorded. */
753
- runId?: string;
754
- createdAt: string;
755
- }
756
- interface ThreadDetail extends ThreadSummary {
757
- messages: StoredMessage[];
758
- activeStreamId?: string;
759
- }
760
- type ToolCallStatus = 'auto_executed' | 'pending_approval' | 'executed' | 'rejected' | 'failed';
761
- /**
762
- * Serializable input for a dispatched model-turn step. Carries only data — the serving worker
763
- * re-resolves the model/sink/registry from its own DI via AGENT_DEPS_FACTORY.forAgent(agentName).
764
- */
765
- interface LlmStepEnvelope {
766
- /** Undefined = default agent (same semantics as {@link AgentRunInput.agentName}). */
767
- agentName?: string;
768
- system: string;
769
- messages: ModelMessage[];
770
- /** The turn's actor — the handler re-derives tool definitions from it (definitionsFor). */
771
- actor: Actor;
772
- /**
773
- * Hold this call's stream frames rather than writing them to the run's sink, and return them on
774
- * the result. Set by the loop when an output processor has to see the whole answer before the
775
- * subscriber does — the dispatched handler streams to a worker-side sink the loop cannot
776
- * interpose on, so the instruction has to ride the envelope. Absent → stream live, as before.
777
- */
778
- bufferOutput?: boolean;
779
- }
780
- /**
781
- * The serializable subset of `AiToolCtx` — everything except `host` (re-attached handler-side
782
- * from DI).
783
- */
784
- interface ToolStepCtx {
785
- actor: Actor;
786
- threadId: string;
787
- runId: string;
788
- requestId: string;
789
- agentName?: string;
790
- pageContext?: PageContext;
791
- }
792
- /** Serializable input for a dispatched tool-execution step. */
793
- interface ToolStepEnvelope {
794
- toolName: string;
795
- input: unknown;
796
- ctx: ToolStepCtx;
797
- /** Applied INSIDE the handler (`withToolTimeout`) — never as a durable step `timeoutMs`. */
798
- timeoutMs?: number;
799
- /**
800
- * The numeric half of `toolTransientRetry` (resolved by the loop from `AgentLoopDeps`, always a
801
- * definite value — `false` when disabled, else concrete `{ attempts, backoffMs }` with defaults
802
- * already filled in) — never `undefined`, so the dispatched handler gets the SAME policy the
803
- * loop would have used locally. The `classify` function is deliberately absent: it isn't
804
- * wire-safe, so the handler resolves its own from its local module options (see
805
- * `AgentRunSteps.tool`) instead of trying to serialize a function.
806
- */
807
- transientRetry: ToolTransientRetryNumbers | false;
808
- }
809
-
810
6
  /**
811
7
  * Public, cross-lib-discoverable DI tokens.
812
8
  *
@@ -879,54 +75,6 @@ declare const AGENT_MEMORY: unique symbol;
879
75
  */
880
76
  declare const AGENT_SKILL_SOURCES: unique symbol;
881
77
 
882
- /**
883
- * Per-invocation context handed to a tool handler. Host-supplied bits are optional. Identity lives
884
- * on {@link AiToolCtx.actor} — read `ctx.actor.id` / `ctx.actor.tenantRef` (single source of truth;
885
- * no denormalized copies).
886
- */
887
- interface AiToolCtx {
888
- actor: Actor;
889
- threadId: string;
890
- runId: string;
891
- requestId: string;
892
- /** The name of the agent running this turn — provenance a tool can scope on (e.g. capability sets). */
893
- agentName?: string;
894
- pageContext?: PageContext;
895
- /** Optional host handle (e.g. an ORM EntityManager) the app threads through options. */
896
- host?: unknown;
897
- }
898
- /** A tool implementation. `I` is the parsed (Zod-validated) input. */
899
- interface ToolHandler<I = unknown> {
900
- execute(input: I, ctx: AiToolCtx): Promise<unknown>;
901
- /**
902
- * Whether this tool exists in this deployment at all — evaluated per turn, BEFORE the roles
903
- * policy, so a `false` here means the model is never shown the tool rather than being shown one
904
- * it will be refused. Omit → always enabled.
905
- *
906
- * This is the seam for a feature flag or a licensing tier: the handler is an ordinary provider,
907
- * so it can read injected config (`this.config.featureX`) that a decorator, evaluated at import
908
- * time, cannot. Answering "does this capability exist here?"; `roles`/`RolesPolicy` answers the
909
- * separate question "may THIS actor use it?", and both still run.
910
- *
911
- * Prefer this over conditionally registering the provider: registration happens while the
912
- * `@Module` metadata is built, which in most apps is before configuration is loaded.
913
- */
914
- isEnabled?(): boolean | Promise<boolean>;
915
- /**
916
- * Whether THIS actor may use the tool, decided per turn. Omit → the role gate alone decides.
917
- *
918
- * The three existing gates all answer the question somewhere else: `roles` is static data,
919
- * `RolesPolicy` is one app-wide rule for every tool, and an agent's `tools` allow-list is fixed
920
- * when the agent is declared. This one lives on the tool and runs with DI, so it can ask the
921
- * questions only the tool knows to ask — is this user's org on the plan that includes it, does
922
- * this actor own the base being queried, is the per-user override in the DB set today.
923
- *
924
- * Runs AFTER {@link isEnabled} and the `RolesPolicy`, and all of them must pass. Applied both
925
- * when the turn's tool list is built (a denied actor is never shown it) and again on invoke.
926
- */
927
- canUse?(actor: Actor): boolean | Promise<boolean>;
928
- }
929
-
930
78
  /**
931
79
  * The "data plane": live token transport, decoupled from the durable control plane.
932
80
  *
@@ -1486,163 +634,6 @@ interface Retriever {
1486
634
  retrieve(query: string, options?: RetrieveOptions): Promise<Passage[]>;
1487
635
  }
1488
636
 
1489
- /**
1490
- * The two seams that see a turn's traffic to and from the model: an {@link InputProcessor} rewrites
1491
- * the prompt on its way out, an {@link OutputProcessor} inspects the answer on its way back and may
1492
- * redact, replace or refuse it. Wire them as `AgentLoopDeps.inputProcessors` /
1493
- * `outputProcessors`, or via `AgentModule.forRoot({ inputProcessors, outputProcessors })`.
1494
- *
1495
- * WHAT THESE ARE NOT: a second way to decide what enters the context. `HistoryPolicy` owns
1496
- * SELECTION — which of the thread's messages ride into the turn, and what stands in for the rest.
1497
- * Processors run on whatever selection produced and own TRANSFORMATION — what those messages say.
1498
- * The distinction is load-bearing rather than stylistic: selection is contractually pure and is what
1499
- * the `load:thread` checkpoint records, so it bounds the journal as well as the prompt, while a
1500
- * processor is allowed to call a model, runs in a checkpoint of its own per step, and rewrites a
1501
- * DERIVED prompt that never becomes the thread's own memory of what was said. Splitting the same
1502
- * decision across both means neither can be reasoned about alone, and the cheap one stops being the
1503
- * whole answer to "why did this turn cost that much".
1504
- */
1505
-
1506
- /** Which turn, and which model step of it, a processor is looking at. */
1507
- interface ProcessorContext {
1508
- threadId: string;
1509
- actor: Actor;
1510
- /** The agent running this turn. Undefined → the default agent. */
1511
- agentName?: string;
1512
- /** 0-based model step within the run — the same index the `llm:<step>` checkpoint carries. */
1513
- step: number;
1514
- }
1515
- /** Everything the model is about to be sent, as the previous processor in the chain left it. */
1516
- interface ProcessedPrompt {
1517
- /** The composed system prompt (agent base + contributors + any injected retrieval block). */
1518
- system: string;
1519
- /** The turn's messages, oldest-first, already through the history ceiling. */
1520
- messages: ModelMessage[];
1521
- }
1522
- /**
1523
- * Rewrites the prompt before each model call of a turn — masking identifiers, stamping a policy
1524
- * preamble, collapsing an oversized tool result. Runs on EVERY step, not once per run, because the
1525
- * transcript grows between steps: a redactor that only saw the opening prompt would wave through
1526
- * whatever a tool result carried back.
1527
- *
1528
- * Runs inside the loop's `process:input:<step>` checkpoint and its result is journaled, so a
1529
- * processor may call a model or hit the network — a resumed run reads back the prompt the suspended
1530
- * attempt built rather than composing a different one.
1531
- */
1532
- interface InputProcessor {
1533
- /** Identifies this processor in a failure. Keep it stable — it is user-visible on an error. */
1534
- readonly name: string;
1535
- process(prompt: ProcessedPrompt, ctx: ProcessorContext): ProcessedPrompt | Promise<ProcessedPrompt>;
1536
- }
1537
- /** One model step's answer, as the previous processor in the chain left it. */
1538
- interface ModelAnswer {
1539
- /** The assembled assistant text for this step. */
1540
- text: string;
1541
- /** The tool calls the same step asked for. Read-only context — a processor cannot change them. */
1542
- toolCalls: readonly ToolCallRequest[];
1543
- }
1544
- /**
1545
- * What an {@link OutputProcessor} decided. `pass` hands the text to the next processor unchanged;
1546
- * `replace` hands it on rewritten (a redaction is a replacement); `reject` ends the chain AND the
1547
- * run — the text is never streamed, never persisted, and the caller gets an
1548
- * {@link OutputRejectedError} rather than an answer.
1549
- */
1550
- type OutputVerdict = {
1551
- action: 'pass';
1552
- } | {
1553
- action: 'replace';
1554
- text: string;
1555
- } | {
1556
- action: 'reject';
1557
- reason: string;
1558
- };
1559
- /**
1560
- * Characters an incremental gate keeps holding at the end of the transformed answer, when a
1561
- * processor declares {@link IncrementalGating} without naming its own window. Wide enough for the
1562
- * patterns a redactor is usually written against — an SSN, an email address, a card number — and
1563
- * deliberately not wider: the window IS the answer's minimum latency tail, since those characters
1564
- * are only released once the whole-answer pass runs.
1565
- */
1566
- declare const DEFAULT_INCREMENTAL_LOOKBACK_CHARS = 64;
1567
- /**
1568
- * A processor's statement that its verdict on a PREFIX of an answer is worth acting on, which is
1569
- * what lets the loop release that prefix to the reader instead of holding the whole answer.
1570
- *
1571
- * Declaring it is a promise about every prefix `P` of the final answer, and the loop takes it at
1572
- * face value on the streaming path:
1573
- *
1574
- * 1. REJECTION IS PREFIX-DECIDABLE. The refusal this processor would return for the whole answer is
1575
- * already returned for the first prefix that contains the reason. A refusal that only emerges
1576
- * from the complete answer still fails the run, but by then the reader has seen a prefix — there
1577
- * is no un-sending bytes, and that is the cost of opting in.
1578
- * 2. REPLACEMENT IS PREFIX-STABLE UP TO THE WINDOW. Once a character of `process(P)`'s output is
1579
- * more than {@link lookbackChars} from the end of that output, it never changes as `P` grows.
1580
- *
1581
- * A broken second promise is caught, not tolerated: the whole-answer pass stays authoritative, and
1582
- * the loop raises a `ProcessorFailedError` when its result is not an extension of what the chain
1583
- * already released. So a window too short for a pattern fails loudly rather than streaming the text
1584
- * it was supposed to redact.
1585
- */
1586
- interface IncrementalGating {
1587
- /**
1588
- * How many characters of this processor's own output stay held back. Undefined →
1589
- * {@link DEFAULT_INCREMENTAL_LOOKBACK_CHARS}. Set it to the length of the longest pattern the
1590
- * processor can act on — anything shorter is a run that fails on the pattern it was written for.
1591
- */
1592
- readonly lookbackChars?: number;
1593
- }
1594
- /**
1595
- * Inspects each model step's answer before anything downstream sees it — before it reaches the live
1596
- * stream, before it is persisted, before it becomes the next step's context.
1597
- *
1598
- * Gating an answer and streaming it as it is generated are mutually exclusive, so registering ANY
1599
- * output processor switches the turn's model call off the run's sink: nothing reaches the
1600
- * subscriber until this chain has ruled on it — see `AgentLoopDeps.outputProcessors`. How much of
1601
- * that costs the reader is what {@link incremental} decides.
1602
- *
1603
- * Runs inside the loop's `process:output:<step>` checkpoint (which also performs the release), so a
1604
- * processor may call a model — a moderation pass is the motivating case — and a replay reads the
1605
- * verdict back instead of re-deciding it.
1606
- */
1607
- interface OutputProcessor {
1608
- /** Identifies this processor in a rejection or a failure. User-visible; keep it stable. */
1609
- readonly name: string;
1610
- /**
1611
- * Opt this processor into gating a PREFIX, so the turn keeps streaming — see
1612
- * {@link IncrementalGating} for what declaring it promises. Undefined means the whole answer,
1613
- * which is what a processor written against the complete text needs and therefore the only safe
1614
- * default; a chain is incremental only when EVERY member of it declares this, so a neighbour can
1615
- * never downgrade what another author was given.
1616
- */
1617
- readonly incremental?: IncrementalGating;
1618
- process(answer: ModelAnswer, ctx: ProcessorContext): OutputVerdict | Promise<OutputVerdict>;
1619
- }
1620
- /**
1621
- * The run ended because an output processor refused the answer — NOT because the model failed. The
1622
- * two must stay distinguishable: a model failure is worth retrying and worth paging someone about,
1623
- * a refusal is the control doing its job. Both runners map this to the `output_rejected` stream
1624
- * error code.
1625
- */
1626
- declare class OutputRejectedError extends Error {
1627
- /** {@link OutputProcessor.name} of the processor that refused. */
1628
- readonly processor: string;
1629
- /** The reason it gave, verbatim. */
1630
- readonly reason: string;
1631
- constructor(processor: string, reason: string);
1632
- }
1633
- /**
1634
- * A processor threw. Wrapped so it can never be mistaken for the model call failing — the loop's
1635
- * only other source of failure at that point in the turn — and so the failure names the processor
1636
- * that produced it instead of surfacing a bare `TypeError` from someone else's code.
1637
- */
1638
- declare class ProcessorFailedError extends Error {
1639
- /** Which seam it was on, so a reader knows whether the prompt or the answer was in flight. */
1640
- readonly phase: 'input' | 'output';
1641
- /** The processor's `name`. */
1642
- readonly processor: string;
1643
- constructor(phase: 'input' | 'output', processor: string, cause: unknown);
1644
- }
1645
-
1646
637
  /**
1647
638
  * Turns text into embedding vectors — the sibling of {@link import('./model-provider.js').ModelProvider}
1648
639
  * for the retrieval side. Batched (`texts` → one vector each, same order) so ingestion can embed many
@@ -4211,4 +3202,4 @@ type AgentDiagnosticKey = `agent:${AgentDiagnosticEvent}`;
4211
3202
  */
4212
3203
  declare function agentDiagnosticKey(event: AgentDiagnosticEvent): AgentDiagnosticKey;
4213
3204
 
4214
- export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_ATTACHMENT_STAGING, AGENT_DEPS_FACTORY, AGENT_DIAGNOSTIC_EVENTS, AGENT_DURABLE_RUNNER, AGENT_EMBEDDING_PROVIDER, AGENT_GOVERNANCE_QUERIES, AGENT_MEMORY, AGENT_MODEL, AGENT_OPTIONS, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_QUOTA_STORE, AGENT_REGISTRY, AGENT_RETRIEVER, AGENT_ROLES_POLICY, AGENT_RUNNER, AGENT_SINK, AGENT_SKILLS, AGENT_SKILL_SOURCES, AGENT_SPAN_EVENTS, AGENT_STORE, AGENT_TOOL_REGISTRY, ASK_TOOL_DESCRIPTION, ASK_TOOL_NAME, type Actor, type ActorDirectory, type ActorResolver, type ActorSpendRow, type AgentApprovalPort, type AgentCatalogEntry, type AgentDefinition, type AgentDelegated, type AgentDelegation, type AgentDiagnosticEvent, type AgentDiagnosticKey, type AgentFollowUpsSpan, type AgentGovernanceQueries, type AgentHistoryWindow, type AgentIntake, type AgentLlmTurnSpan, type AgentLoopDeps, type AgentLoopHooks, type AgentLoopResult, type AgentMemoryResolved, type AgentMemoryWritten, type AgentMessageEvent, type AgentPricingStore, type AgentQuotaExceeded, AgentRegistry, type AgentRetrievalSpan, type AgentRetrieved, type AgentRunFailed, type AgentRunFinished, type AgentRunInput, type AgentRunStarted, type AgentRunner, type AgentSkillsResolved, type AgentSpanEvent, type AgentStore, AgentStreamError, type AgentStreamEvent, type AgentStructuredOutputSpan, type AgentToolCallEvent, type AgentToolExecutionSpan, type AgentToolRetry, type AiToolCtx, type AppendMessageInput, type ApprovalWhere, type AskToolInput, type AttachmentRef, type AttachmentStagingStore, type BufferedModelTurnResult, type BuildMemoryBlockInput, type CostUsage, type CreateThreadInput, type CurrentModelPrice, DEFAULT_HISTORY_SUMMARY_INSTRUCTION, DEFAULT_INCREMENTAL_LOOKBACK_CHARS, DEFAULT_INTAKE_PREAMBLE, DEFAULT_MAX_FACT_CHARS, DEFAULT_MAX_MEMORIES, DEFAULT_MAX_SKILLS, DEFAULT_REFUSAL_REASON, DEFAULT_STRUCTURED_OUTPUT_INSTRUCTION, DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS, DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS, type Decision, DefaultRolesPolicy, type DetachedDelegationOutcome, type DetachedDelegationReceipt, type DetachedDelivery, type DetailThreadRef, type ElicitationOption, type ElicitationOutcome, type ElicitationQuestion, type ElicitationReply, type ElicitationRequest, type ElicitationResult, type EmbeddingProvider, type ForgetMemoryInput, type FrameBuffer, GLOBAL_SCOPE, type GovernancePage, type GovernancePageQuery, type GovernanceRange, type GovernanceRunDetail, type GovernanceThreadDetail, type GovernanceThreadDetailQuery, type GovernanceUsageInput, type HistoryPolicy, type HistoryPolicyContext, type HistorySelection, type HistorySummary, type HumanReply, type IncrementalGate, type IncrementalGating, type InputProcessor, type InvokeWithTransientRetryOptions, type ListMemoriesInput, type ListSkillsInput, type ListStagedAttachmentsInput, type LlmStepEnvelope, type LoadSkillInput, MAX_ASK_QUESTIONS, type MemoryAuthor, type MemoryConfig, type MemoryDigest, type MemoryDigestEntry, type MemoryFact, type MemoryForgetRequest, type MemoryOrigin, type MemoryProvider, type MemoryRecord, type MemoryVerdict, type MemoryWriteOutcome, type MemoryWriteRequest, type MessageAttachment, type MessageRole, type MessageUsage, type ModelAnswer, type ModelMessage, type ModelPrice, type ModelPriceInput, type ModelProvider, type ModelSpendRow, type ModelTurnArgs, type ModelTurnResult, type OfferMemoriesInput, type OutputGateMode, type OutputGateResult, type OutputProcessor, OutputRejectedError, type OutputVerdict, type OverriddenMemory, type PageContext, type Passage, type PendingApprovalRow, type ProcessedPrompt, type ProcessorContext, ProcessorFailedError, type PromptBuilder, type PromptContext, type PromptContributor, QuotaExceededError, type QuotaState, type QuotaStore, type QuotaView, REMEMBER_TOOL_DESCRIPTION, REMEMBER_TOOL_NAME, type RecentRunRow, type RecordRunEndInput, type RecordRunStartInput, type RecordToolCallInput, type RecordUsageInput, type RememberToolInput, type RerankOptions, type Reranker, type ResolveAttachmentInput, type ResolveMemoryDigestInput, type ResolvedDelegation, type RetrieveOptions, type Retriever, type RolesPolicy, type RunAgentBreakdownRow, RunCancelledError, type RunErrorBreakdownRow, type RunMetrics, type RunToolCallRow, type RunTrendPoint, type RunWhere, SKILL_TOOL_DESCRIPTION, SKILL_TOOL_NAME, type ScopeContext, type ScopeResolver, type SearchMemoriesInput, type SettledTask, type SinkWriter, type Skill, type SkillAuthor, type SkillCatalogEntry, type SkillContext, type SkillLoadOutcome, type SkillOffer, type SkillProvider, type SkillSummary, type SkillToolInput, type SkillWriteRequest, type SkillWriteVerdict, type SkillsConfig, type StageAttachmentInput, type StagedAttachment, type StoreMemoryInput, type StoredMessage, type StreamError, type StructuredOutcome, StructuredOutputError, THREAD_DETAIL_CONTENT_CHARS, type ThreadActivityRow, type ThreadDetail, type ThreadMessageRow, type ThreadMeta, type ThreadSpendRow, type ThreadSummary, type ThreadTurnPage, type ThreadTurnQuery, type ThreadTurnReader, type ThreadUsageRollup, type ThreadWhere, type TokenStreamSink, type ToolCallActivityRow, type ToolCallRequest, type ToolCallStatus, type ToolCallWhere, type ToolDefinition, ToolDisabledError, ToolForbiddenError, type ToolHandler, ToolInputInvalidError, type ToolKind, type ToolKindDeps, ToolNotFoundError, ToolRegistry, type ToolResult, type ToolSpec, type ToolStatRow, type ToolStepCtx, type ToolStepEnvelope, type ToolTransientRetryNumbers, type ToolTransientRetryOptions, type ToolTransientRetrySetting, type UpdateThreadInput, type UpdateToolCallInput, type UsagePurpose, type UsageTrendPoint, type WindowHistoryOptions, type WithMemoryToolInput, type WriteMemoryInput, actorScope, agentDiagnosticKey, agentFailureCode, askInputSchema, askToolDefinition, bucketByActor, bucketByModel, bucketByThread, bucketUsageTrend, buildMemoryBlock, buildSkillsBlock, canActorUseTool, compositeSkillProvider, createFrameBuffer, createIncrementalGate, dayBoundsUtc, decodeStreamEvent, defaultScopeResolver, detachedDelivered, detachedStarted, detachedUnsettled, encodeStreamEvent, estimateCost, estimateMessageTokens, extractJson, filterToolsByAllowList, filterToolsByCanUse, filterToolsByEnabled, filterToolsByRole, gateFollowUps, gateTail, invokeWithTransientRetry, isControlFlowSignal, isReplayIntegrityError, isToolEnabled, isTransientToolError, loadSkill, memoryForgetVerdict, memoryWriteVerdict, normalizeDelegation, normalizeElicitationReply, offerMemories, offerSkills, publishAgentDelegated, publishAgentMemoryResolved, publishAgentMemoryWritten, publishAgentMessage, publishAgentQuotaExceeded, publishAgentRetrieved, publishAgentRunFailed, publishAgentRunFinished, publishAgentRunStarted, publishAgentSkillsResolved, publishAgentToolCall, publishAgentToolRetry, releaseGatedFrames, rememberInputSchema, rememberToolDefinition, renderElicitationAnswers, repairInstruction, resolveElicitation, resolveGateLookback, resolveMemoryDigest, resolveOutputGateMode, resolveSkillCatalog, resolveToolTransientRetryNumbers, rollupThreadUsage, runAgentLoop, runInputProcessors, runOutputProcessors, seedModelPrices, settleAll, settleElicitation, settleUnsettledDelegation, skillInputSchema, skillToolDefinition, skillWriteVerdict, stampToolKinds, staticSkillProvider, summarizeWithModel, tenantScope, traceLlmTurn, traceToolExecution, truncateDetailContent, validateStructured, windowHistory, withAskTool, withMemoryTool, withSkillTool, withToolTimeout, writeMemory };
3205
+ export { AGENT_ACTOR_DIRECTORY, AGENT_ACTOR_RESOLVER, AGENT_APPROVAL_PORT, AGENT_ATTACHMENT_STAGING, AGENT_DEPS_FACTORY, AGENT_DIAGNOSTIC_EVENTS, AGENT_DURABLE_RUNNER, AGENT_EMBEDDING_PROVIDER, AGENT_GOVERNANCE_QUERIES, AGENT_MEMORY, AGENT_MODEL, AGENT_OPTIONS, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_QUOTA_STORE, AGENT_REGISTRY, AGENT_RETRIEVER, AGENT_ROLES_POLICY, AGENT_RUNNER, AGENT_SINK, AGENT_SKILLS, AGENT_SKILL_SOURCES, AGENT_SPAN_EVENTS, AGENT_STORE, AGENT_TOOL_REGISTRY, Actor, type ActorDirectory, type ActorResolver, type ActorSpendRow, type AgentApprovalPort, AgentDefinition, type AgentDelegated, AgentDelegation, type AgentDiagnosticEvent, type AgentDiagnosticKey, type AgentFollowUpsSpan, type AgentGovernanceQueries, AgentIntake, type AgentLlmTurnSpan, type AgentLoopDeps, type AgentLoopHooks, type AgentLoopResult, type AgentMemoryResolved, type AgentMemoryWritten, type AgentMessageEvent, type AgentPricingStore, type AgentQuotaExceeded, AgentRegistry, type AgentRetrievalSpan, type AgentRetrieved, type AgentRunFailed, type AgentRunFinished, AgentRunInput, type AgentRunStarted, type AgentRunner, type AgentSkillsResolved, type AgentSpanEvent, type AgentStore, AgentStreamError, type AgentStreamEvent, type AgentStructuredOutputSpan, type AgentToolCallEvent, type AgentToolExecutionSpan, type AgentToolRetry, AiToolCtx, type AppendMessageInput, type ApprovalWhere, type AttachmentRef, type AttachmentStagingStore, type BufferedModelTurnResult, type BuildMemoryBlockInput, type CostUsage, type CreateThreadInput, type CurrentModelPrice, DEFAULT_HISTORY_SUMMARY_INSTRUCTION, DEFAULT_MAX_FACT_CHARS, DEFAULT_MAX_MEMORIES, DEFAULT_MAX_SKILLS, DEFAULT_REFUSAL_REASON, DEFAULT_STRUCTURED_OUTPUT_INSTRUCTION, Decision, DefaultRolesPolicy, type DetachedDelegationOutcome, type DetachedDelegationReceipt, DetachedDelivery, type DetailThreadRef, ElicitationRequest, type EmbeddingProvider, type ForgetMemoryInput, type FrameBuffer, GLOBAL_SCOPE, type GovernancePage, type GovernancePageQuery, type GovernanceRange, type GovernanceRunDetail, type GovernanceThreadDetail, type GovernanceThreadDetailQuery, type GovernanceUsageInput, HistoryPolicy, HumanReply, type IncrementalGate, InputProcessor, type ListMemoriesInput, type ListSkillsInput, type ListStagedAttachmentsInput, LlmStepEnvelope, type LoadSkillInput, type MemoryAuthor, type MemoryConfig, type MemoryDigest, type MemoryDigestEntry, type MemoryFact, type MemoryForgetRequest, type MemoryOrigin, type MemoryProvider, type MemoryRecord, type MemoryVerdict, type MemoryWriteOutcome, type MemoryWriteRequest, MessageAttachment, MessageUsage, ModelAnswer, ModelMessage, type ModelPrice, type ModelPriceInput, type ModelProvider, type ModelSpendRow, type ModelTurnArgs, type ModelTurnResult, type OfferMemoriesInput, type OutputGateMode, type OutputGateResult, OutputProcessor, type OverriddenMemory, PageContext, type Passage, type PendingApprovalRow, ProcessedPrompt, ProcessorContext, PromptBuilder, PromptContributor, QuotaExceededError, QuotaState, type QuotaStore, REMEMBER_TOOL_DESCRIPTION, REMEMBER_TOOL_NAME, type RecentRunRow, type RecordRunEndInput, type RecordRunStartInput, type RecordToolCallInput, type RecordUsageInput, type RememberToolInput, type RerankOptions, type Reranker, type ResolveAttachmentInput, type ResolveMemoryDigestInput, type ResolvedDelegation, type RetrieveOptions, type Retriever, type RolesPolicy, type RunAgentBreakdownRow, RunCancelledError, type RunErrorBreakdownRow, type RunMetrics, type RunToolCallRow, type RunTrendPoint, type RunWhere, SKILL_TOOL_DESCRIPTION, SKILL_TOOL_NAME, type ScopeContext, type ScopeResolver, type SearchMemoriesInput, type SettledTask, type SinkWriter, type Skill, type SkillAuthor, type SkillCatalogEntry, type SkillContext, type SkillLoadOutcome, type SkillOffer, type SkillProvider, type SkillSummary, type SkillToolInput, type SkillWriteRequest, type SkillWriteVerdict, type SkillsConfig, type StageAttachmentInput, type StagedAttachment, type StoreMemoryInput, StoredMessage, type StreamError, type StructuredOutcome, StructuredOutputError, THREAD_DETAIL_CONTENT_CHARS, type ThreadActivityRow, ThreadDetail, type ThreadMessageRow, type ThreadMeta, type ThreadSpendRow, ThreadSummary, type ThreadTurnPage, type ThreadTurnQuery, type ThreadTurnReader, type ThreadUsageRollup, type ThreadWhere, type TokenStreamSink, type ToolCallActivityRow, ToolCallRequest, ToolCallStatus, type ToolCallWhere, ToolDefinition, ToolDisabledError, ToolForbiddenError, ToolHandler, ToolInputInvalidError, type ToolKindDeps, ToolNotFoundError, ToolRegistry, ToolResult, ToolSpec, type ToolStatRow, ToolStepEnvelope, ToolTransientRetrySetting, type UpdateThreadInput, type UpdateToolCallInput, UsagePurpose, type UsageTrendPoint, type WindowHistoryOptions, type WithMemoryToolInput, type WriteMemoryInput, actorScope, agentDiagnosticKey, agentFailureCode, bucketByActor, bucketByModel, bucketByThread, bucketUsageTrend, buildMemoryBlock, buildSkillsBlock, canActorUseTool, compositeSkillProvider, createFrameBuffer, createIncrementalGate, dayBoundsUtc, decodeStreamEvent, defaultScopeResolver, detachedDelivered, detachedStarted, detachedUnsettled, encodeStreamEvent, estimateCost, estimateMessageTokens, extractJson, filterToolsByAllowList, filterToolsByCanUse, filterToolsByEnabled, filterToolsByRole, gateFollowUps, gateTail, isControlFlowSignal, isReplayIntegrityError, isToolEnabled, loadSkill, memoryForgetVerdict, memoryWriteVerdict, normalizeDelegation, offerMemories, offerSkills, publishAgentDelegated, publishAgentMemoryResolved, publishAgentMemoryWritten, publishAgentMessage, publishAgentQuotaExceeded, publishAgentRetrieved, publishAgentRunFailed, publishAgentRunFinished, publishAgentRunStarted, publishAgentSkillsResolved, publishAgentToolCall, publishAgentToolRetry, releaseGatedFrames, rememberInputSchema, rememberToolDefinition, repairInstruction, resolveGateLookback, resolveMemoryDigest, resolveOutputGateMode, resolveSkillCatalog, rollupThreadUsage, runAgentLoop, runInputProcessors, runOutputProcessors, seedModelPrices, settleAll, settleUnsettledDelegation, skillInputSchema, skillToolDefinition, skillWriteVerdict, stampToolKinds, staticSkillProvider, summarizeWithModel, tenantScope, traceLlmTurn, traceToolExecution, truncateDetailContent, validateStructured, windowHistory, withAskTool, withMemoryTool, withSkillTool, withToolTimeout, writeMemory };