@dudousxd/nestjs-agent-core 0.25.0 → 0.26.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.
@@ -0,0 +1,160 @@
1
+ import { M as ModelMessage, A as Actor, d as ToolCallRequest } from './tool-BXKG2xSm.cjs';
2
+
3
+ /**
4
+ * The two seams that see a turn's traffic to and from the model: an {@link InputProcessor} rewrites
5
+ * the prompt on its way out, an {@link OutputProcessor} inspects the answer on its way back and may
6
+ * redact, replace or refuse it. Wire them as `AgentLoopDeps.inputProcessors` /
7
+ * `outputProcessors`, or via `AgentModule.forRoot({ inputProcessors, outputProcessors })`.
8
+ *
9
+ * WHAT THESE ARE NOT: a second way to decide what enters the context. `HistoryPolicy` owns
10
+ * SELECTION — which of the thread's messages ride into the turn, and what stands in for the rest.
11
+ * Processors run on whatever selection produced and own TRANSFORMATION — what those messages say.
12
+ * The distinction is load-bearing rather than stylistic: selection is contractually pure and is what
13
+ * the `load:thread` checkpoint records, so it bounds the journal as well as the prompt, while a
14
+ * processor is allowed to call a model, runs in a checkpoint of its own per step, and rewrites a
15
+ * DERIVED prompt that never becomes the thread's own memory of what was said. Splitting the same
16
+ * decision across both means neither can be reasoned about alone, and the cheap one stops being the
17
+ * whole answer to "why did this turn cost that much".
18
+ */
19
+
20
+ /** Which turn, and which model step of it, a processor is looking at. */
21
+ interface ProcessorContext {
22
+ threadId: string;
23
+ actor: Actor;
24
+ /** The agent running this turn. Undefined → the default agent. */
25
+ agentName?: string;
26
+ /** 0-based model step within the run — the same index the `llm:<step>` checkpoint carries. */
27
+ step: number;
28
+ }
29
+ /** Everything the model is about to be sent, as the previous processor in the chain left it. */
30
+ interface ProcessedPrompt {
31
+ /** The composed system prompt (agent base + contributors + any injected retrieval block). */
32
+ system: string;
33
+ /** The turn's messages, oldest-first, already through the history ceiling. */
34
+ messages: ModelMessage[];
35
+ }
36
+ /**
37
+ * Rewrites the prompt before each model call of a turn — masking identifiers, stamping a policy
38
+ * preamble, collapsing an oversized tool result. Runs on EVERY step, not once per run, because the
39
+ * transcript grows between steps: a redactor that only saw the opening prompt would wave through
40
+ * whatever a tool result carried back.
41
+ *
42
+ * Runs inside the loop's `process:input:<step>` checkpoint and its result is journaled, so a
43
+ * processor may call a model or hit the network — a resumed run reads back the prompt the suspended
44
+ * attempt built rather than composing a different one.
45
+ */
46
+ interface InputProcessor {
47
+ /** Identifies this processor in a failure. Keep it stable — it is user-visible on an error. */
48
+ readonly name: string;
49
+ process(prompt: ProcessedPrompt, ctx: ProcessorContext): ProcessedPrompt | Promise<ProcessedPrompt>;
50
+ }
51
+ /** One model step's answer, as the previous processor in the chain left it. */
52
+ interface ModelAnswer {
53
+ /** The assembled assistant text for this step. */
54
+ text: string;
55
+ /** The tool calls the same step asked for. Read-only context — a processor cannot change them. */
56
+ toolCalls: readonly ToolCallRequest[];
57
+ }
58
+ /**
59
+ * What an {@link OutputProcessor} decided. `pass` hands the text to the next processor unchanged;
60
+ * `replace` hands it on rewritten (a redaction is a replacement); `reject` ends the chain AND the
61
+ * run — the text is never streamed, never persisted, and the caller gets an
62
+ * {@link OutputRejectedError} rather than an answer.
63
+ */
64
+ type OutputVerdict = {
65
+ action: 'pass';
66
+ } | {
67
+ action: 'replace';
68
+ text: string;
69
+ } | {
70
+ action: 'reject';
71
+ reason: string;
72
+ };
73
+ /**
74
+ * Characters an incremental gate keeps holding at the end of the transformed answer, when a
75
+ * processor declares {@link IncrementalGating} without naming its own window. Wide enough for the
76
+ * patterns a redactor is usually written against — an SSN, an email address, a card number — and
77
+ * deliberately not wider: the window IS the answer's minimum latency tail, since those characters
78
+ * are only released once the whole-answer pass runs.
79
+ */
80
+ declare const DEFAULT_INCREMENTAL_LOOKBACK_CHARS = 64;
81
+ /**
82
+ * A processor's statement that its verdict on a PREFIX of an answer is worth acting on, which is
83
+ * what lets the loop release that prefix to the reader instead of holding the whole answer.
84
+ *
85
+ * Declaring it is a promise about every prefix `P` of the final answer, and the loop takes it at
86
+ * face value on the streaming path:
87
+ *
88
+ * 1. REJECTION IS PREFIX-DECIDABLE. The refusal this processor would return for the whole answer is
89
+ * already returned for the first prefix that contains the reason. A refusal that only emerges
90
+ * from the complete answer still fails the run, but by then the reader has seen a prefix — there
91
+ * is no un-sending bytes, and that is the cost of opting in.
92
+ * 2. REPLACEMENT IS PREFIX-STABLE UP TO THE WINDOW. Once a character of `process(P)`'s output is
93
+ * more than {@link lookbackChars} from the end of that output, it never changes as `P` grows.
94
+ *
95
+ * A broken second promise is caught, not tolerated: the whole-answer pass stays authoritative, and
96
+ * the loop raises a `ProcessorFailedError` when its result is not an extension of what the chain
97
+ * already released. So a window too short for a pattern fails loudly rather than streaming the text
98
+ * it was supposed to redact.
99
+ */
100
+ interface IncrementalGating {
101
+ /**
102
+ * How many characters of this processor's own output stay held back. Undefined →
103
+ * {@link DEFAULT_INCREMENTAL_LOOKBACK_CHARS}. Set it to the length of the longest pattern the
104
+ * processor can act on — anything shorter is a run that fails on the pattern it was written for.
105
+ */
106
+ readonly lookbackChars?: number;
107
+ }
108
+ /**
109
+ * Inspects each model step's answer before anything downstream sees it — before it reaches the live
110
+ * stream, before it is persisted, before it becomes the next step's context.
111
+ *
112
+ * Gating an answer and streaming it as it is generated are mutually exclusive, so registering ANY
113
+ * output processor switches the turn's model call off the run's sink: nothing reaches the
114
+ * subscriber until this chain has ruled on it — see `AgentLoopDeps.outputProcessors`. How much of
115
+ * that costs the reader is what {@link incremental} decides.
116
+ *
117
+ * Runs inside the loop's `process:output:<step>` checkpoint (which also performs the release), so a
118
+ * processor may call a model — a moderation pass is the motivating case — and a replay reads the
119
+ * verdict back instead of re-deciding it.
120
+ */
121
+ interface OutputProcessor {
122
+ /** Identifies this processor in a rejection or a failure. User-visible; keep it stable. */
123
+ readonly name: string;
124
+ /**
125
+ * Opt this processor into gating a PREFIX, so the turn keeps streaming — see
126
+ * {@link IncrementalGating} for what declaring it promises. Undefined means the whole answer,
127
+ * which is what a processor written against the complete text needs and therefore the only safe
128
+ * default; a chain is incremental only when EVERY member of it declares this, so a neighbour can
129
+ * never downgrade what another author was given.
130
+ */
131
+ readonly incremental?: IncrementalGating;
132
+ process(answer: ModelAnswer, ctx: ProcessorContext): OutputVerdict | Promise<OutputVerdict>;
133
+ }
134
+ /**
135
+ * The run ended because an output processor refused the answer — NOT because the model failed. The
136
+ * two must stay distinguishable: a model failure is worth retrying and worth paging someone about,
137
+ * a refusal is the control doing its job. Both runners map this to the `output_rejected` stream
138
+ * error code.
139
+ */
140
+ declare class OutputRejectedError extends Error {
141
+ /** {@link OutputProcessor.name} of the processor that refused. */
142
+ readonly processor: string;
143
+ /** The reason it gave, verbatim. */
144
+ readonly reason: string;
145
+ constructor(processor: string, reason: string);
146
+ }
147
+ /**
148
+ * A processor threw. Wrapped so it can never be mistaken for the model call failing — the loop's
149
+ * only other source of failure at that point in the turn — and so the failure names the processor
150
+ * that produced it instead of surfacing a bare `TypeError` from someone else's code.
151
+ */
152
+ declare class ProcessorFailedError extends Error {
153
+ /** Which seam it was on, so a reader knows whether the prompt or the answer was in flight. */
154
+ readonly phase: 'input' | 'output';
155
+ /** The processor's `name`. */
156
+ readonly processor: string;
157
+ constructor(phase: 'input' | 'output', processor: string, cause: unknown);
158
+ }
159
+
160
+ export { DEFAULT_INCREMENTAL_LOOKBACK_CHARS as D, type InputProcessor as I, type ModelAnswer as M, type OutputProcessor as O, type ProcessorContext as P, type ProcessedPrompt as a, type IncrementalGating as b, OutputRejectedError as c, type OutputVerdict as d, ProcessorFailedError as e };
@@ -0,0 +1,160 @@
1
+ import { M as ModelMessage, A as Actor, d as ToolCallRequest } from './tool-BXKG2xSm.js';
2
+
3
+ /**
4
+ * The two seams that see a turn's traffic to and from the model: an {@link InputProcessor} rewrites
5
+ * the prompt on its way out, an {@link OutputProcessor} inspects the answer on its way back and may
6
+ * redact, replace or refuse it. Wire them as `AgentLoopDeps.inputProcessors` /
7
+ * `outputProcessors`, or via `AgentModule.forRoot({ inputProcessors, outputProcessors })`.
8
+ *
9
+ * WHAT THESE ARE NOT: a second way to decide what enters the context. `HistoryPolicy` owns
10
+ * SELECTION — which of the thread's messages ride into the turn, and what stands in for the rest.
11
+ * Processors run on whatever selection produced and own TRANSFORMATION — what those messages say.
12
+ * The distinction is load-bearing rather than stylistic: selection is contractually pure and is what
13
+ * the `load:thread` checkpoint records, so it bounds the journal as well as the prompt, while a
14
+ * processor is allowed to call a model, runs in a checkpoint of its own per step, and rewrites a
15
+ * DERIVED prompt that never becomes the thread's own memory of what was said. Splitting the same
16
+ * decision across both means neither can be reasoned about alone, and the cheap one stops being the
17
+ * whole answer to "why did this turn cost that much".
18
+ */
19
+
20
+ /** Which turn, and which model step of it, a processor is looking at. */
21
+ interface ProcessorContext {
22
+ threadId: string;
23
+ actor: Actor;
24
+ /** The agent running this turn. Undefined → the default agent. */
25
+ agentName?: string;
26
+ /** 0-based model step within the run — the same index the `llm:<step>` checkpoint carries. */
27
+ step: number;
28
+ }
29
+ /** Everything the model is about to be sent, as the previous processor in the chain left it. */
30
+ interface ProcessedPrompt {
31
+ /** The composed system prompt (agent base + contributors + any injected retrieval block). */
32
+ system: string;
33
+ /** The turn's messages, oldest-first, already through the history ceiling. */
34
+ messages: ModelMessage[];
35
+ }
36
+ /**
37
+ * Rewrites the prompt before each model call of a turn — masking identifiers, stamping a policy
38
+ * preamble, collapsing an oversized tool result. Runs on EVERY step, not once per run, because the
39
+ * transcript grows between steps: a redactor that only saw the opening prompt would wave through
40
+ * whatever a tool result carried back.
41
+ *
42
+ * Runs inside the loop's `process:input:<step>` checkpoint and its result is journaled, so a
43
+ * processor may call a model or hit the network — a resumed run reads back the prompt the suspended
44
+ * attempt built rather than composing a different one.
45
+ */
46
+ interface InputProcessor {
47
+ /** Identifies this processor in a failure. Keep it stable — it is user-visible on an error. */
48
+ readonly name: string;
49
+ process(prompt: ProcessedPrompt, ctx: ProcessorContext): ProcessedPrompt | Promise<ProcessedPrompt>;
50
+ }
51
+ /** One model step's answer, as the previous processor in the chain left it. */
52
+ interface ModelAnswer {
53
+ /** The assembled assistant text for this step. */
54
+ text: string;
55
+ /** The tool calls the same step asked for. Read-only context — a processor cannot change them. */
56
+ toolCalls: readonly ToolCallRequest[];
57
+ }
58
+ /**
59
+ * What an {@link OutputProcessor} decided. `pass` hands the text to the next processor unchanged;
60
+ * `replace` hands it on rewritten (a redaction is a replacement); `reject` ends the chain AND the
61
+ * run — the text is never streamed, never persisted, and the caller gets an
62
+ * {@link OutputRejectedError} rather than an answer.
63
+ */
64
+ type OutputVerdict = {
65
+ action: 'pass';
66
+ } | {
67
+ action: 'replace';
68
+ text: string;
69
+ } | {
70
+ action: 'reject';
71
+ reason: string;
72
+ };
73
+ /**
74
+ * Characters an incremental gate keeps holding at the end of the transformed answer, when a
75
+ * processor declares {@link IncrementalGating} without naming its own window. Wide enough for the
76
+ * patterns a redactor is usually written against — an SSN, an email address, a card number — and
77
+ * deliberately not wider: the window IS the answer's minimum latency tail, since those characters
78
+ * are only released once the whole-answer pass runs.
79
+ */
80
+ declare const DEFAULT_INCREMENTAL_LOOKBACK_CHARS = 64;
81
+ /**
82
+ * A processor's statement that its verdict on a PREFIX of an answer is worth acting on, which is
83
+ * what lets the loop release that prefix to the reader instead of holding the whole answer.
84
+ *
85
+ * Declaring it is a promise about every prefix `P` of the final answer, and the loop takes it at
86
+ * face value on the streaming path:
87
+ *
88
+ * 1. REJECTION IS PREFIX-DECIDABLE. The refusal this processor would return for the whole answer is
89
+ * already returned for the first prefix that contains the reason. A refusal that only emerges
90
+ * from the complete answer still fails the run, but by then the reader has seen a prefix — there
91
+ * is no un-sending bytes, and that is the cost of opting in.
92
+ * 2. REPLACEMENT IS PREFIX-STABLE UP TO THE WINDOW. Once a character of `process(P)`'s output is
93
+ * more than {@link lookbackChars} from the end of that output, it never changes as `P` grows.
94
+ *
95
+ * A broken second promise is caught, not tolerated: the whole-answer pass stays authoritative, and
96
+ * the loop raises a `ProcessorFailedError` when its result is not an extension of what the chain
97
+ * already released. So a window too short for a pattern fails loudly rather than streaming the text
98
+ * it was supposed to redact.
99
+ */
100
+ interface IncrementalGating {
101
+ /**
102
+ * How many characters of this processor's own output stay held back. Undefined →
103
+ * {@link DEFAULT_INCREMENTAL_LOOKBACK_CHARS}. Set it to the length of the longest pattern the
104
+ * processor can act on — anything shorter is a run that fails on the pattern it was written for.
105
+ */
106
+ readonly lookbackChars?: number;
107
+ }
108
+ /**
109
+ * Inspects each model step's answer before anything downstream sees it — before it reaches the live
110
+ * stream, before it is persisted, before it becomes the next step's context.
111
+ *
112
+ * Gating an answer and streaming it as it is generated are mutually exclusive, so registering ANY
113
+ * output processor switches the turn's model call off the run's sink: nothing reaches the
114
+ * subscriber until this chain has ruled on it — see `AgentLoopDeps.outputProcessors`. How much of
115
+ * that costs the reader is what {@link incremental} decides.
116
+ *
117
+ * Runs inside the loop's `process:output:<step>` checkpoint (which also performs the release), so a
118
+ * processor may call a model — a moderation pass is the motivating case — and a replay reads the
119
+ * verdict back instead of re-deciding it.
120
+ */
121
+ interface OutputProcessor {
122
+ /** Identifies this processor in a rejection or a failure. User-visible; keep it stable. */
123
+ readonly name: string;
124
+ /**
125
+ * Opt this processor into gating a PREFIX, so the turn keeps streaming — see
126
+ * {@link IncrementalGating} for what declaring it promises. Undefined means the whole answer,
127
+ * which is what a processor written against the complete text needs and therefore the only safe
128
+ * default; a chain is incremental only when EVERY member of it declares this, so a neighbour can
129
+ * never downgrade what another author was given.
130
+ */
131
+ readonly incremental?: IncrementalGating;
132
+ process(answer: ModelAnswer, ctx: ProcessorContext): OutputVerdict | Promise<OutputVerdict>;
133
+ }
134
+ /**
135
+ * The run ended because an output processor refused the answer — NOT because the model failed. The
136
+ * two must stay distinguishable: a model failure is worth retrying and worth paging someone about,
137
+ * a refusal is the control doing its job. Both runners map this to the `output_rejected` stream
138
+ * error code.
139
+ */
140
+ declare class OutputRejectedError extends Error {
141
+ /** {@link OutputProcessor.name} of the processor that refused. */
142
+ readonly processor: string;
143
+ /** The reason it gave, verbatim. */
144
+ readonly reason: string;
145
+ constructor(processor: string, reason: string);
146
+ }
147
+ /**
148
+ * A processor threw. Wrapped so it can never be mistaken for the model call failing — the loop's
149
+ * only other source of failure at that point in the turn — and so the failure names the processor
150
+ * that produced it instead of surfacing a bare `TypeError` from someone else's code.
151
+ */
152
+ declare class ProcessorFailedError extends Error {
153
+ /** Which seam it was on, so a reader knows whether the prompt or the answer was in flight. */
154
+ readonly phase: 'input' | 'output';
155
+ /** The processor's `name`. */
156
+ readonly processor: string;
157
+ constructor(phase: 'input' | 'output', processor: string, cause: unknown);
158
+ }
159
+
160
+ export { DEFAULT_INCREMENTAL_LOOKBACK_CHARS as D, type InputProcessor as I, type ModelAnswer as M, type OutputProcessor as O, type ProcessorContext as P, type ProcessedPrompt as a, type IncrementalGating as b, OutputRejectedError as c, type OutputVerdict as d, ProcessorFailedError as e };
@@ -1266,6 +1266,11 @@ interface LlmStepEnvelope {
1266
1266
  messages: ModelMessage[];
1267
1267
  /** The turn's actor — the handler re-derives tool definitions from it (definitionsFor). */
1268
1268
  actor: Actor;
1269
+ /**
1270
+ * The turn's thread — with {@link actor}, what a tool's per-turn `describe` is scoped on. Optional
1271
+ * so an envelope from a loop that predates it still parses.
1272
+ */
1273
+ threadId?: string;
1269
1274
  /**
1270
1275
  * Hold this call's stream frames rather than writing them to the run's sink, and return them on
1271
1276
  * the result. Set by the loop when an output processor has to see the whole answer before the
@@ -1312,163 +1317,6 @@ interface ToolStepEnvelope {
1312
1317
  collectUi?: boolean;
1313
1318
  }
1314
1319
 
1315
- /**
1316
- * The two seams that see a turn's traffic to and from the model: an {@link InputProcessor} rewrites
1317
- * the prompt on its way out, an {@link OutputProcessor} inspects the answer on its way back and may
1318
- * redact, replace or refuse it. Wire them as `AgentLoopDeps.inputProcessors` /
1319
- * `outputProcessors`, or via `AgentModule.forRoot({ inputProcessors, outputProcessors })`.
1320
- *
1321
- * WHAT THESE ARE NOT: a second way to decide what enters the context. `HistoryPolicy` owns
1322
- * SELECTION — which of the thread's messages ride into the turn, and what stands in for the rest.
1323
- * Processors run on whatever selection produced and own TRANSFORMATION — what those messages say.
1324
- * The distinction is load-bearing rather than stylistic: selection is contractually pure and is what
1325
- * the `load:thread` checkpoint records, so it bounds the journal as well as the prompt, while a
1326
- * processor is allowed to call a model, runs in a checkpoint of its own per step, and rewrites a
1327
- * DERIVED prompt that never becomes the thread's own memory of what was said. Splitting the same
1328
- * decision across both means neither can be reasoned about alone, and the cheap one stops being the
1329
- * whole answer to "why did this turn cost that much".
1330
- */
1331
-
1332
- /** Which turn, and which model step of it, a processor is looking at. */
1333
- interface ProcessorContext {
1334
- threadId: string;
1335
- actor: Actor;
1336
- /** The agent running this turn. Undefined → the default agent. */
1337
- agentName?: string;
1338
- /** 0-based model step within the run — the same index the `llm:<step>` checkpoint carries. */
1339
- step: number;
1340
- }
1341
- /** Everything the model is about to be sent, as the previous processor in the chain left it. */
1342
- interface ProcessedPrompt {
1343
- /** The composed system prompt (agent base + contributors + any injected retrieval block). */
1344
- system: string;
1345
- /** The turn's messages, oldest-first, already through the history ceiling. */
1346
- messages: ModelMessage[];
1347
- }
1348
- /**
1349
- * Rewrites the prompt before each model call of a turn — masking identifiers, stamping a policy
1350
- * preamble, collapsing an oversized tool result. Runs on EVERY step, not once per run, because the
1351
- * transcript grows between steps: a redactor that only saw the opening prompt would wave through
1352
- * whatever a tool result carried back.
1353
- *
1354
- * Runs inside the loop's `process:input:<step>` checkpoint and its result is journaled, so a
1355
- * processor may call a model or hit the network — a resumed run reads back the prompt the suspended
1356
- * attempt built rather than composing a different one.
1357
- */
1358
- interface InputProcessor {
1359
- /** Identifies this processor in a failure. Keep it stable — it is user-visible on an error. */
1360
- readonly name: string;
1361
- process(prompt: ProcessedPrompt, ctx: ProcessorContext): ProcessedPrompt | Promise<ProcessedPrompt>;
1362
- }
1363
- /** One model step's answer, as the previous processor in the chain left it. */
1364
- interface ModelAnswer {
1365
- /** The assembled assistant text for this step. */
1366
- text: string;
1367
- /** The tool calls the same step asked for. Read-only context — a processor cannot change them. */
1368
- toolCalls: readonly ToolCallRequest[];
1369
- }
1370
- /**
1371
- * What an {@link OutputProcessor} decided. `pass` hands the text to the next processor unchanged;
1372
- * `replace` hands it on rewritten (a redaction is a replacement); `reject` ends the chain AND the
1373
- * run — the text is never streamed, never persisted, and the caller gets an
1374
- * {@link OutputRejectedError} rather than an answer.
1375
- */
1376
- type OutputVerdict = {
1377
- action: 'pass';
1378
- } | {
1379
- action: 'replace';
1380
- text: string;
1381
- } | {
1382
- action: 'reject';
1383
- reason: string;
1384
- };
1385
- /**
1386
- * Characters an incremental gate keeps holding at the end of the transformed answer, when a
1387
- * processor declares {@link IncrementalGating} without naming its own window. Wide enough for the
1388
- * patterns a redactor is usually written against — an SSN, an email address, a card number — and
1389
- * deliberately not wider: the window IS the answer's minimum latency tail, since those characters
1390
- * are only released once the whole-answer pass runs.
1391
- */
1392
- declare const DEFAULT_INCREMENTAL_LOOKBACK_CHARS = 64;
1393
- /**
1394
- * A processor's statement that its verdict on a PREFIX of an answer is worth acting on, which is
1395
- * what lets the loop release that prefix to the reader instead of holding the whole answer.
1396
- *
1397
- * Declaring it is a promise about every prefix `P` of the final answer, and the loop takes it at
1398
- * face value on the streaming path:
1399
- *
1400
- * 1. REJECTION IS PREFIX-DECIDABLE. The refusal this processor would return for the whole answer is
1401
- * already returned for the first prefix that contains the reason. A refusal that only emerges
1402
- * from the complete answer still fails the run, but by then the reader has seen a prefix — there
1403
- * is no un-sending bytes, and that is the cost of opting in.
1404
- * 2. REPLACEMENT IS PREFIX-STABLE UP TO THE WINDOW. Once a character of `process(P)`'s output is
1405
- * more than {@link lookbackChars} from the end of that output, it never changes as `P` grows.
1406
- *
1407
- * A broken second promise is caught, not tolerated: the whole-answer pass stays authoritative, and
1408
- * the loop raises a `ProcessorFailedError` when its result is not an extension of what the chain
1409
- * already released. So a window too short for a pattern fails loudly rather than streaming the text
1410
- * it was supposed to redact.
1411
- */
1412
- interface IncrementalGating {
1413
- /**
1414
- * How many characters of this processor's own output stay held back. Undefined →
1415
- * {@link DEFAULT_INCREMENTAL_LOOKBACK_CHARS}. Set it to the length of the longest pattern the
1416
- * processor can act on — anything shorter is a run that fails on the pattern it was written for.
1417
- */
1418
- readonly lookbackChars?: number;
1419
- }
1420
- /**
1421
- * Inspects each model step's answer before anything downstream sees it — before it reaches the live
1422
- * stream, before it is persisted, before it becomes the next step's context.
1423
- *
1424
- * Gating an answer and streaming it as it is generated are mutually exclusive, so registering ANY
1425
- * output processor switches the turn's model call off the run's sink: nothing reaches the
1426
- * subscriber until this chain has ruled on it — see `AgentLoopDeps.outputProcessors`. How much of
1427
- * that costs the reader is what {@link incremental} decides.
1428
- *
1429
- * Runs inside the loop's `process:output:<step>` checkpoint (which also performs the release), so a
1430
- * processor may call a model — a moderation pass is the motivating case — and a replay reads the
1431
- * verdict back instead of re-deciding it.
1432
- */
1433
- interface OutputProcessor {
1434
- /** Identifies this processor in a rejection or a failure. User-visible; keep it stable. */
1435
- readonly name: string;
1436
- /**
1437
- * Opt this processor into gating a PREFIX, so the turn keeps streaming — see
1438
- * {@link IncrementalGating} for what declaring it promises. Undefined means the whole answer,
1439
- * which is what a processor written against the complete text needs and therefore the only safe
1440
- * default; a chain is incremental only when EVERY member of it declares this, so a neighbour can
1441
- * never downgrade what another author was given.
1442
- */
1443
- readonly incremental?: IncrementalGating;
1444
- process(answer: ModelAnswer, ctx: ProcessorContext): OutputVerdict | Promise<OutputVerdict>;
1445
- }
1446
- /**
1447
- * The run ended because an output processor refused the answer — NOT because the model failed. The
1448
- * two must stay distinguishable: a model failure is worth retrying and worth paging someone about,
1449
- * a refusal is the control doing its job. Both runners map this to the `output_rejected` stream
1450
- * error code.
1451
- */
1452
- declare class OutputRejectedError extends Error {
1453
- /** {@link OutputProcessor.name} of the processor that refused. */
1454
- readonly processor: string;
1455
- /** The reason it gave, verbatim. */
1456
- readonly reason: string;
1457
- constructor(processor: string, reason: string);
1458
- }
1459
- /**
1460
- * A processor threw. Wrapped so it can never be mistaken for the model call failing — the loop's
1461
- * only other source of failure at that point in the turn — and so the failure names the processor
1462
- * that produced it instead of surfacing a bare `TypeError` from someone else's code.
1463
- */
1464
- declare class ProcessorFailedError extends Error {
1465
- /** Which seam it was on, so a reader knows whether the prompt or the answer was in flight. */
1466
- readonly phase: 'input' | 'output';
1467
- /** The processor's `name`. */
1468
- readonly processor: string;
1469
- constructor(phase: 'input' | 'output', processor: string, cause: unknown);
1470
- }
1471
-
1472
1320
  /**
1473
1321
  * Per-invocation context handed to a tool handler. Host-supplied bits are optional. Identity lives
1474
1322
  * on {@link AiToolCtx.actor} — read `ctx.actor.id` / `ctx.actor.tenantRef` (single source of truth;
@@ -1494,12 +1342,13 @@ interface AiToolCtx {
1494
1342
  * JSON; it is snapshotted when pushed.
1495
1343
  *
1496
1344
  * Replay-safe under the durable runner: the pushed components ride the tool step's journaled
1497
- * result, so a replay neither streams nor persists them again. Present whenever the agent loop
1498
- * runs the tool (inline, durable, dispatched); absent on surfaces with no conversation to push
1499
- * into (the MCP server), hence optional — call it as `ctx.emitUi?.(…)` in a tool that is also
1500
- * served there.
1345
+ * result, so a replay neither streams nor persists them again.
1346
+ *
1347
+ * Always present. On a surface with no conversation to push into (the MCP server, a direct
1348
+ * `registry.invoke` without one) it is a no-op that still resolves to an id, so a tool calls
1349
+ * `ctx.emitUi(…)` unconditionally.
1501
1350
  */
1502
- emitUi?(component: string, props: Record<string, unknown>, options?: {
1351
+ emitUi(component: string, props: Record<string, unknown>, options?: {
1503
1352
  id?: string;
1504
1353
  version?: number;
1505
1354
  }): Promise<{
@@ -1536,6 +1385,30 @@ interface ToolHandler<I = unknown> {
1536
1385
  * when the turn's tool list is built (a denied actor is never shown it) and again on invoke.
1537
1386
  */
1538
1387
  canUse?(actor: Actor): boolean | Promise<boolean>;
1388
+ /**
1389
+ * What the model is told about this tool for THIS turn — a description and/or input schema that
1390
+ * depend on who is asking (a per-tenant component catalog, a per-plan list of options). Called
1391
+ * when the turn's tool list is built, after every gate has passed; whatever it returns replaces
1392
+ * the registered spec's `description` / `inputSchema` in the definition the model sees. Omit, or
1393
+ * return `undefined`, to use the registered spec as is.
1394
+ *
1395
+ * It shapes what the model is SHOWN only: the registry still validates a call against the
1396
+ * registered `inputSchema`, so a tool whose accepted input varies per turn registers a permissive
1397
+ * schema and validates in `execute`.
1398
+ */
1399
+ describe?(scope: ToolDescribeScope): ToolDescription | undefined | Promise<ToolDescription | undefined>;
1400
+ }
1401
+ /** Who a turn's tool list is being built for — what {@link ToolHandler.describe} can vary on. */
1402
+ interface ToolDescribeScope {
1403
+ actor: Actor;
1404
+ /** Absent where the list is built outside a conversation (the MCP server's `tools/list`). */
1405
+ threadId?: string;
1406
+ agentName?: string;
1407
+ }
1408
+ /** A per-turn override of a tool's model-facing definition ({@link ToolHandler.describe}). */
1409
+ interface ToolDescription {
1410
+ description?: string;
1411
+ inputSchema?: StandardSchemaV1;
1539
1412
  }
1540
1413
 
1541
- export { type ElicitationInput as $, type Actor as A, type Decision as B, type ToolStepEnvelope as C, type DetachedDelivery as D, type ElicitationRequest as E, ASK_TOOL_DESCRIPTION as F, ASK_TOOL_NAME as G, type HumanReply as H, type InputProcessor as I, type AgentApprovalRequest as J, type AgentApprovalSettlement as K, type LlmStepEnvelope as L, type ModelMessage as M, type AgentCatalogEntry as N, type OutputProcessor as O, type ProcessorContext as P, type QuotaState as Q, type AgentHistoryWindow as R, type StoredMessage as S, type ToolHandler as T, type UsagePurpose as U, type AskToolInput as V, DEFAULT_INCREMENTAL_LOOKBACK_CHARS as W, DEFAULT_INTAKE_PREAMBLE as X, DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS as Y, DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS as Z, ELICITATION_INPUT_TYPES as _, type ToolDefinition as a, type ElicitationInputType as a0, type ElicitationOption as a1, type ElicitationOutcome as a2, type ElicitationQuestion as a3, type ElicitationReply as a4, type ElicitationResult as a5, type HistoryPolicyContext as a6, type HistorySelection as a7, type HistorySummary as a8, type IncrementalGating as a9, normalizeElicitationReply as aA, questionOptions as aB, readElicitationInput as aC, readElicitationQuestions as aD, renderElicitationAnswers as aE, resolveElicitation as aF, resolveToolTransientRetryNumbers as aG, settleElicitation as aH, validateElicitationAnswer as aI, validateElicitationValue as aJ, type InvokeWithTransientRetryOptions as aa, MAX_ASK_QUESTIONS as ab, type MessageFeedbackValue as ac, type MessageRole as ad, OutputRejectedError as ae, type OutputVerdict as af, ProcessorFailedError as ag, type PromptContext as ah, type QuotaView as ai, type ToolCallApprovalStatus as aj, type ToolCatalogEntry as ak, type ToolConfirmation as al, type ToolPresentation as am, type ToolPresentationTone as an, type ToolResultField as ao, type ToolResultView as ap, type ToolStepCtx as aq, type ToolTransientRetryNumbers as ar, type ToolTransientRetryOptions as as, askInputSchema as at, askToolDefinition as au, decodeStreamEvent as av, encodeStreamEvent as aw, invokeWithTransientRetry as ax, isTransientToolError as ay, isTypedQuestion as az, type ToolCallRequest as b, type MessageUsage as c, type AgentUiComponent as d, type AiToolCtx as e, type AgentStreamEvent as f, type ThreadSummary as g, type ThreadDetail as h, type ToolResult as i, type MessageAttachment as j, type MessageFeedback as k, type ToolCallStatus as l, type ToolSpec as m, type AgentRunInput as n, type ToolKind as o, type ToolCallApproval as p, type HistoryPolicy as q, type ProcessedPrompt as r, type ModelAnswer as s, type PageContext as t, type AgentDefinition as u, type AgentDelegation as v, type PromptBuilder as w, type PromptContributor as x, type ToolTransientRetrySetting as y, type AgentIntake as z };
1414
+ export { type ElicitationQuestion as $, type Actor as A, type ToolStepEnvelope as B, ASK_TOOL_DESCRIPTION as C, type DetachedDelivery as D, type ElicitationRequest as E, ASK_TOOL_NAME as F, type AgentApprovalRequest as G, type HumanReply as H, type AgentApprovalSettlement as I, type AgentCatalogEntry as J, type AgentHistoryWindow as K, type LlmStepEnvelope as L, type ModelMessage as M, type AskToolInput as N, DEFAULT_INTAKE_PREAMBLE as O, type PageContext as P, type QuotaState as Q, DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS as R, type StoredMessage as S, type ToolSpec as T, type UsagePurpose as U, DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS as V, ELICITATION_INPUT_TYPES as W, type ElicitationInput as X, type ElicitationInputType as Y, type ElicitationOption as Z, type ElicitationOutcome as _, type ToolHandler as a, type ElicitationReply as a0, type ElicitationResult as a1, type HistoryPolicyContext as a2, type HistorySelection as a3, type HistorySummary as a4, type InvokeWithTransientRetryOptions as a5, MAX_ASK_QUESTIONS as a6, type MessageFeedbackValue as a7, type MessageRole as a8, type PromptContext as a9, validateElicitationAnswer as aA, validateElicitationValue as aB, type QuotaView as aa, type ToolCallApprovalStatus as ab, type ToolCatalogEntry as ac, type ToolConfirmation as ad, type ToolDescription as ae, type ToolPresentationTone as af, type ToolResultField as ag, type ToolResultView as ah, type ToolStepCtx as ai, type ToolTransientRetryNumbers as aj, type ToolTransientRetryOptions as ak, askInputSchema as al, askToolDefinition as am, decodeStreamEvent as an, encodeStreamEvent as ao, invokeWithTransientRetry as ap, isTransientToolError as aq, isTypedQuestion as ar, normalizeElicitationReply as as, questionOptions as at, readElicitationInput as au, readElicitationQuestions as av, renderElicitationAnswers as aw, resolveElicitation as ax, resolveToolTransientRetryNumbers as ay, settleElicitation as az, type ToolPresentation as b, type ToolDefinition as c, type ToolCallRequest as d, type MessageUsage as e, type AgentUiComponent as f, type AiToolCtx as g, type AgentStreamEvent as h, type ThreadSummary as i, type ThreadDetail as j, type ToolResult as k, type MessageAttachment as l, type MessageFeedback as m, type ToolCallStatus as n, type AgentRunInput as o, type ToolKind as p, type ToolCallApproval as q, type HistoryPolicy as r, type AgentDefinition as s, type AgentDelegation as t, type ToolDescribeScope as u, type PromptBuilder as v, type PromptContributor as w, type ToolTransientRetrySetting as x, type AgentIntake as y, type Decision as z };