@dudousxd/nestjs-agent-core 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/ag-ui/index.cjs +1064 -0
- package/dist/ag-ui/index.cjs.map +1 -0
- package/dist/ag-ui/index.d.cts +479 -0
- package/dist/ag-ui/index.d.ts +479 -0
- package/dist/ag-ui/index.js +1025 -0
- package/dist/ag-ui/index.js.map +1 -0
- package/dist/genui/index.d.cts +2 -1
- package/dist/genui/index.d.ts +2 -1
- package/dist/guardrails/index.d.cts +3 -2
- package/dist/guardrails/index.d.ts +3 -2
- package/dist/index.cjs +245 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -5
- package/dist/index.d.ts +79 -5
- package/dist/index.js +233 -6
- package/dist/index.js.map +1 -1
- package/dist/{processors-DvZDW5zS.d.cts → processors-C0snQzZJ.d.cts} +1 -1
- package/dist/{processors-C1jir8eB.d.ts → processors-D5110pit.d.ts} +1 -1
- package/dist/{tool-CMTuoJ2v.d.cts → stream-events-CgWqAI-1.d.cts} +786 -806
- package/dist/{tool-CMTuoJ2v.d.ts → stream-events-CgWqAI-1.d.ts} +786 -806
- package/dist/tool-DklsS3JX.d.ts +119 -0
- package/dist/tool-DuJ-_qXM.d.cts +119 -0
- package/package.json +14 -1
|
@@ -70,600 +70,200 @@ declare function readElicitationInput(raw: unknown): ElicitationInput | undefine
|
|
|
70
70
|
declare function readElicitationQuestions(input: unknown): ElicitationQuestion[];
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* What a store knows about a tool call, read back when its message carries no result for it — see
|
|
74
|
+
* {@link import('./spi/agent-store.js').AgentStore.toolCallOutcomes}.
|
|
75
|
+
*/
|
|
76
|
+
interface ToolCallOutcome {
|
|
77
|
+
id: string;
|
|
78
|
+
status: ToolCallStatus;
|
|
79
|
+
output?: unknown;
|
|
80
|
+
error?: string;
|
|
81
|
+
}
|
|
82
|
+
/** What the model is told about a call whose turn died before the call was settled. */
|
|
83
|
+
declare const UNFINISHED_TOOL_CALL: string;
|
|
84
|
+
/** What is written on the row of a call whose run ended without settling it. */
|
|
85
|
+
declare const RUN_ENDED_BEFORE_TOOL_CALL = "the run ended before this tool call was settled";
|
|
86
|
+
/** The ids of every tool call in `messages` that the message asking for it holds no result for. */
|
|
87
|
+
declare function danglingToolCallIds(messages: readonly ModelMessage[]): string[];
|
|
88
|
+
/**
|
|
89
|
+
* Give every tool call in a thread's history a result.
|
|
74
90
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
91
|
+
* A turn writes its results onto the assistant message once the step's LAST tool has settled. A turn
|
|
92
|
+
* that dies before that — its run failed, its worker was killed, an approval was never answered and
|
|
93
|
+
* the run was abandoned — leaves an assistant message that asks for tools and is answered by
|
|
94
|
+
* nothing. A provider refuses a prompt shaped like that (a tool call must be followed by its
|
|
95
|
+
* result), so every LATER turn on the thread fails too, with an error about the stream rather than
|
|
96
|
+
* about the history: one dead turn makes the whole conversation unusable.
|
|
97
|
+
*
|
|
98
|
+
* Here each such call is settled from what is actually known: the call's own row where the store
|
|
99
|
+
* can read it (`outcomes` — a tool that DID run hands the model its real output, so it is not run a
|
|
100
|
+
* second time), and otherwise a result saying the call was never completed. Pure: the same messages
|
|
101
|
+
* and outcomes always produce the same prompt, so a replay composes what the first attempt did.
|
|
102
|
+
* Messages with nothing dangling are returned as they are.
|
|
80
103
|
*/
|
|
104
|
+
declare function settleDanglingToolCalls(messages: ModelMessage[], outcomes?: readonly ToolCallOutcome[]): ModelMessage[];
|
|
81
105
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
/** What the user reads. */
|
|
87
|
-
label: string;
|
|
106
|
+
interface CreateThreadInput {
|
|
107
|
+
actor: Actor;
|
|
108
|
+
transient?: boolean;
|
|
109
|
+
title?: string;
|
|
88
110
|
/**
|
|
89
|
-
*
|
|
90
|
-
* the
|
|
111
|
+
* Create the thread under THIS id instead of a generated one — for a caller whose protocol names
|
|
112
|
+
* the conversation itself (AG-UI's `threadId`, at most 255 characters). OPTIONAL to honour: a
|
|
113
|
+
* store that ignores it still creates a thread, under an id of its own, and the caller reads the
|
|
114
|
+
* id off the result. A store that honours it rejects an id already taken (soft-deleted threads
|
|
115
|
+
* included).
|
|
91
116
|
*/
|
|
92
|
-
|
|
117
|
+
id?: string;
|
|
93
118
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
119
|
+
interface AppendMessageInput {
|
|
120
|
+
threadId: string;
|
|
121
|
+
role: StoredMessage['role'];
|
|
122
|
+
content: string;
|
|
123
|
+
/** Which agent produced this message (assistant messages) — provenance. */
|
|
124
|
+
agentName?: string;
|
|
125
|
+
toolCalls?: ToolCallRequest[];
|
|
126
|
+
toolResults?: ToolResult[];
|
|
127
|
+
/** Files the user attached to this message (image/PDF). Persisted verbatim. */
|
|
128
|
+
attachments?: MessageAttachment[];
|
|
129
|
+
followUps?: string[];
|
|
130
|
+
usage?: MessageUsage;
|
|
101
131
|
/**
|
|
102
|
-
* The
|
|
103
|
-
*
|
|
132
|
+
* The run (turn) that produced this message. Without it a consumer can only guess which turn a
|
|
133
|
+
* message belongs to by comparing timestamps against the run's `startedAt`, and that guess breaks
|
|
134
|
+
* the moment a turn is regenerated — the replaced answer is truncated away, so the times no longer
|
|
135
|
+
* line up 1:1. Optional so a caller predating this (and a host that appends messages outside a
|
|
136
|
+
* run) can omit it; the store persists it as `null` when absent.
|
|
104
137
|
*/
|
|
105
|
-
|
|
138
|
+
runId?: string;
|
|
139
|
+
/** The step's streamed thinking. See {@link StoredMessage.reasoning}. */
|
|
140
|
+
reasoning?: string;
|
|
141
|
+
/** Time spent thinking in this step, in ms. See {@link StoredMessage.reasoningMs}. */
|
|
142
|
+
reasoningMs?: number;
|
|
143
|
+
/** Components pushed during this step. See {@link StoredMessage.ui}. */
|
|
144
|
+
ui?: AgentUiComponent[];
|
|
145
|
+
}
|
|
146
|
+
interface RecordToolCallInput {
|
|
147
|
+
toolCallId: string;
|
|
148
|
+
messageId: string;
|
|
149
|
+
toolName: string;
|
|
150
|
+
toolType: 'read' | 'action';
|
|
151
|
+
input: unknown;
|
|
152
|
+
status: ToolCallStatus;
|
|
106
153
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* `
|
|
154
|
+
* The run (turn) this tool call belongs to — enables a governance surface to deep-link a tool
|
|
155
|
+
* call out to its trace waterfall. Optional so a caller predating this (or a store's own
|
|
156
|
+
* synthetic tool calls) can omit it; the store persists it as `null` when absent.
|
|
110
157
|
*/
|
|
111
|
-
|
|
112
|
-
/** More than one option may be chosen. Omit → single choice. */
|
|
113
|
-
multiple?: boolean;
|
|
158
|
+
runId?: string;
|
|
114
159
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* that: submitting without answering leaves this question unanswered.
|
|
160
|
+
* Who has to approve this call (`'requester'` or a role), from the turn's `ApprovalPolicy`. Set on
|
|
161
|
+
* an action call that was put to a person — or approved by a remembered decision — and never
|
|
162
|
+
* otherwise; persisted as `null` when absent.
|
|
119
163
|
*/
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
|
|
164
|
+
approver?: string;
|
|
165
|
+
/** ISO-8601 instant the approval request lapses. Absent → it never does. */
|
|
166
|
+
expiresAt?: string;
|
|
123
167
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
/** What the assistant says above the form. */
|
|
136
|
-
preamble?: string;
|
|
137
|
-
questions: ElicitationQuestion[];
|
|
168
|
+
interface UpdateToolCallInput {
|
|
169
|
+
toolCallId: string;
|
|
170
|
+
status: ToolCallStatus;
|
|
171
|
+
output?: unknown;
|
|
172
|
+
error?: string;
|
|
173
|
+
executionMs?: number;
|
|
174
|
+
executedByRef?: string;
|
|
175
|
+
/** The approval asked for later calls of this tool in this thread to run without asking. */
|
|
176
|
+
remember?: boolean;
|
|
177
|
+
/** The surface the decision came through. See {@link import('../types.js').Decision.decidedVia}. */
|
|
178
|
+
decidedVia?: string;
|
|
138
179
|
}
|
|
139
|
-
/** What a
|
|
140
|
-
interface
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
180
|
+
/** What the approve/reject routes need to know about a call before they signal a decision on it. */
|
|
181
|
+
interface ToolCallApprovalState {
|
|
182
|
+
status: ToolCallStatus;
|
|
183
|
+
/** `null` for a call no policy put to anyone (an `ask`, or a row written before approvers existed). */
|
|
184
|
+
approver: string | null;
|
|
185
|
+
expiresAt: string | null;
|
|
186
|
+
}
|
|
187
|
+
/** Patch applied by {@link AgentStore.updateThread}. An omitted key leaves that field untouched. */
|
|
188
|
+
interface UpdateThreadInput {
|
|
189
|
+
title?: string;
|
|
190
|
+
/** `null` clears the thread's default agent (falls back to the module default). */
|
|
191
|
+
defaultAgent?: string | null;
|
|
192
|
+
/** `null` unpins the thread's model (turns run on the provider default). */
|
|
193
|
+
model?: string | null;
|
|
194
|
+
}
|
|
195
|
+
interface RecordUsageInput {
|
|
196
|
+
threadId: string;
|
|
197
|
+
actorRef: string;
|
|
198
|
+
messageId?: string;
|
|
199
|
+
modelId: string;
|
|
200
|
+
purpose: UsagePurpose;
|
|
201
|
+
usage: MessageUsage;
|
|
202
|
+
/** Provider-reported actual USD cost for this turn, when known (gateways report it). */
|
|
203
|
+
costUsd?: number;
|
|
204
|
+
}
|
|
205
|
+
interface RecordRunStartInput {
|
|
206
|
+
runId: string;
|
|
207
|
+
threadId: string;
|
|
208
|
+
actorRef: string;
|
|
209
|
+
agentName?: string;
|
|
155
210
|
/**
|
|
156
|
-
* The
|
|
157
|
-
*
|
|
211
|
+
* The run that started this one, for a delegation's child run. The parent->child edge exists in
|
|
212
|
+
* the durable runtime's own journal, but only there: a governance surface reading run rows alone
|
|
213
|
+
* cannot roll a delegation's cost up to the turn that asked for it, and a DETACHED child outlives
|
|
214
|
+
* its parent's turn entirely, so nothing in the transcript pairs them either.
|
|
215
|
+
*
|
|
216
|
+
* Optional, and a store that persists nothing for it still works — it loses the tree, not the run.
|
|
158
217
|
*/
|
|
159
|
-
|
|
218
|
+
parentRunId?: string;
|
|
219
|
+
/** sha256 hex of the run's resolved (pre-RAG) system prompt — identifies the prompt VERSION. */
|
|
220
|
+
promptHash?: string;
|
|
160
221
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
/** One entry per question, in request order — always present, so a caller never re-applies defaults. */
|
|
164
|
-
answers: Record<string, string[]>;
|
|
165
|
-
skipped: boolean;
|
|
166
|
-
/** Question ids filled from the request's `defaults` rather than by the human. */
|
|
167
|
-
defaulted: string[];
|
|
222
|
+
interface RecordRunEndInput {
|
|
223
|
+
runId: string;
|
|
168
224
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
225
|
+
* `cancelled` is a THIRD terminal, not a flavour of `failed`: someone asked the run to stop and it
|
|
226
|
+
* did, which is the control working. A consumer computing a failure rate over these rows has to be
|
|
227
|
+
* able to leave it out — counting a user pressing Stop as an error pages whoever is on call for
|
|
228
|
+
* model failures. It carries no `errorCode`/`errorMessage`, since there is nothing to diagnose.
|
|
171
229
|
*/
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
230
|
+
status: 'completed' | 'failed' | 'cancelled';
|
|
231
|
+
durationMs?: number;
|
|
232
|
+
errorCode?: string;
|
|
233
|
+
errorMessage?: string;
|
|
234
|
+
}
|
|
235
|
+
/** Which thread, and how many of its newest messages, {@link ThreadTurnReader.loadThreadForTurn} reads. */
|
|
236
|
+
interface ThreadTurnQuery {
|
|
237
|
+
threadId: string;
|
|
238
|
+
/** Omitted reads every message; `0` reads none. */
|
|
239
|
+
messageLimit?: number;
|
|
240
|
+
}
|
|
241
|
+
/** What a turn reads off a thread — a bounded window, not the transcript. */
|
|
242
|
+
interface ThreadTurnPage {
|
|
243
|
+
title: string;
|
|
244
|
+
defaultAgent: string | null;
|
|
245
|
+
/** Whether the THREAD has ever been answered, not whether {@link messages} holds an answer. */
|
|
246
|
+
hasAssistantMessage: boolean;
|
|
247
|
+
/** Oldest first, carrying only the fields a model turn reads. */
|
|
248
|
+
messages: StoredMessage[];
|
|
175
249
|
}
|
|
176
250
|
/**
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* A question set is persisted as an `action` tool call in `pending_approval` — that is what puts it
|
|
180
|
-
* in the approvals inbox a deployment already has, instead of needing one of its own. The cost of
|
|
181
|
-
* that choice is that the thing which comes back may be a {@link Decision} someone pressed
|
|
182
|
-
* Approve/Reject on rather than a set of answers, and a `Decision` carries no `answers` at all.
|
|
251
|
+
* A store that can hand a turn the WINDOW it is about to send, instead of the thread's transcript.
|
|
183
252
|
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
253
|
+
* {@link AgentStore.getThread} materializes every message row, every attachment and every tool
|
|
254
|
+
* output a thread ever recorded, and the run then journals what it loaded — so a long thread pays
|
|
255
|
+
* for its whole history on every turn and again on every replay, to send a prompt bounded to its
|
|
256
|
+
* last few messages. This read is bounded by the database (`order by created_at desc limit ?`),
|
|
257
|
+
* projected to the columns a model turn actually reads.
|
|
188
258
|
*
|
|
189
|
-
*
|
|
190
|
-
* and a
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Settle a reply against the request it answers: fill every unanswered question from its own
|
|
195
|
-
* `defaults`, drop submitted values that aren't on offer, and collapse a single-choice question to
|
|
196
|
-
* one value.
|
|
259
|
+
* `hasAssistantMessage` is answered over the WHOLE thread, never the page: it answers "has this
|
|
260
|
+
* conversation been answered before?" — what a `thread-start` intake asks — and a thread whose window
|
|
261
|
+
* happens to hold only the user's last questions has still been answered. `null` for a thread that is
|
|
262
|
+
* unknown or soft-deleted, matching `getThread`.
|
|
197
263
|
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
* its own. Resolving defaults in the HTTP layer instead would put them behind a store read that a
|
|
202
|
-
* replay would have to repeat.
|
|
203
|
-
*/
|
|
204
|
-
declare function resolveElicitation(request: ElicitationRequest, raw: ElicitationReply | Decision): ElicitationOutcome;
|
|
205
|
-
/**
|
|
206
|
-
* What a settled elicitation looks like to everyone downstream: the model reading it back as a tool
|
|
207
|
-
* result, the thread reader rendering it, the auditor asking what the agent was told to do. One
|
|
208
|
-
* shape for both surfaces — nothing here records which of them asked.
|
|
209
|
-
*/
|
|
210
|
-
interface ElicitationResult extends ElicitationOutcome {
|
|
211
|
-
/** The questions against the chosen LABELS, so a reader (and a model) can act on it. */
|
|
212
|
-
summary: string;
|
|
213
|
-
}
|
|
214
|
-
/** {@link resolveElicitation} plus its human-readable rendering. Pure, for the same reason. */
|
|
215
|
-
declare function settleElicitation(request: ElicitationRequest, reply: ElicitationReply | Decision): ElicitationResult;
|
|
216
|
-
/**
|
|
217
|
-
* The answers as the model reads them: the question's own prompt against the chosen options' LABELS,
|
|
218
|
-
* not their opaque `value`s — a model shown `{"scope":["b"]}` has been told nothing.
|
|
219
|
-
*/
|
|
220
|
-
declare function renderElicitationAnswers(request: ElicitationRequest, outcome: ElicitationOutcome): string;
|
|
221
|
-
/** The reserved tool name the model calls to ask the user something. */
|
|
222
|
-
declare const ASK_TOOL_NAME = "ask";
|
|
223
|
-
/** What the model must supply when it calls `ask`. */
|
|
224
|
-
interface AskToolInput {
|
|
225
|
-
preamble?: string;
|
|
226
|
-
questions: ElicitationQuestion[];
|
|
227
|
-
}
|
|
228
|
-
/** How many questions one `ask` may carry. A form the user has to scroll is a form they skip. */
|
|
229
|
-
declare const MAX_ASK_QUESTIONS = 5;
|
|
230
|
-
/**
|
|
231
|
-
* The `ask` tool's input schema, hand-written rather than borrowed from a validation library: core
|
|
232
|
-
* depends on no validator, and the schema has to carry a JSON Schema a provider can constrain
|
|
233
|
-
* generation against. It publishes one through the Standard JSON Schema extension
|
|
234
|
-
* (`~standard.jsonSchema.input`), which is the path the AI SDK adapter already recognises for
|
|
235
|
-
* Valibot / ArkType / Zod 4.
|
|
236
|
-
*/
|
|
237
|
-
declare const askInputSchema: StandardSchemaV1<unknown, AskToolInput>;
|
|
238
|
-
/**
|
|
239
|
-
* What the model is told the `ask` tool is for. Written to discourage the two failure modes that
|
|
240
|
-
* make a clarifying question worse than a guess: asking about something the conversation already
|
|
241
|
-
* settled, and asking without saying what you would have done.
|
|
242
|
-
*/
|
|
243
|
-
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.";
|
|
244
|
-
/**
|
|
245
|
-
* The `ask` tool as the model sees it. NOT a `ToolSpec` and never registered: `ask` has no handler,
|
|
246
|
-
* because the loop settles it against a human instead of invoking anything. Keeping it out of the
|
|
247
|
-
* `ToolRegistry` is also what keeps the kind decision off a process-local lookup — see
|
|
248
|
-
* `claimToolCall`.
|
|
249
|
-
*/
|
|
250
|
-
declare function askToolDefinition(): ToolDefinition;
|
|
251
|
-
/** A question set an `@Agent` asks before it starts working. See `AgentLoopDeps.intake`. */
|
|
252
|
-
interface AgentIntake {
|
|
253
|
-
questions: ElicitationQuestion[];
|
|
254
|
-
/** What the assistant says above the form. Omit → {@link DEFAULT_INTAKE_PREAMBLE}. */
|
|
255
|
-
preamble?: string;
|
|
256
|
-
/**
|
|
257
|
-
* `'thread-start'` (default) asks once, on the first turn of a thread; `'every-turn'` asks before
|
|
258
|
-
* every turn. Both are decided from what `load:thread` recorded about the thread when the turn
|
|
259
|
-
* began, never from anything this process happens to know — by the time a replay reaches the
|
|
260
|
-
* question, the thread already holds the assistant message the first attempt wrote.
|
|
261
|
-
*/
|
|
262
|
-
when?: 'thread-start' | 'every-turn';
|
|
263
|
-
}
|
|
264
|
-
declare const DEFAULT_INTAKE_PREAMBLE = "A few questions before I start. I have pre-picked what I would choose, so confirming is enough.";
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* The structured live-stream vocabulary carried over the {@link SinkWriter} byte channel.
|
|
268
|
-
*
|
|
269
|
-
* The model turn (via the AI-SDK adapter) and the agent loop write these events as NDJSON — one
|
|
270
|
-
* `JSON.stringify(event)\n` per {@link SinkWriter.write}. The HTTP layer forwards each line as an
|
|
271
|
-
* SSE `data:` frame, and the client transport maps them back to the AI SDK UI-message chunk
|
|
272
|
-
* protocol so the browser renders text, reasoning, and tool cards (input + output) LIVE — the same
|
|
273
|
-
* rich rendering a raw `streamText().toUIMessageStream()` would give, but reconstructed on the
|
|
274
|
-
* client so the sink stays a format-agnostic byte buffer (durable buffering/replay is untouched).
|
|
275
|
-
*
|
|
276
|
-
* Keeping this vocabulary neutral (not AI-SDK `UIMessageChunk`) means core never depends on `ai`:
|
|
277
|
-
* the adapter owns model-parts → event, the transport owns event → UI-chunk.
|
|
278
|
-
*
|
|
279
|
-
* The vocabulary is also a CONTRACT for runners that are not this library's loop: anything that
|
|
280
|
-
* writes these frames (one JSON object per SSE `data:` line) gets the React transport, transcript
|
|
281
|
-
* model and hooks for free. Two rules keep it evolvable:
|
|
282
|
-
* - every frame is a JSON object with a string `kind`; a reader MUST tolerate kinds it does not
|
|
283
|
-
* know (the React transport forwards them as `data-<kind>` parts rather than dropping them);
|
|
284
|
-
* - fields are only ever added, and new fields are optional.
|
|
285
|
-
*/
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* A component the server pushed into the conversation: generative UI that is NOT a tool call's
|
|
289
|
-
* rendering. It is addressed by `component` (a key in the client's own component registry), never
|
|
290
|
-
* by a tool name, so a runner can emit one from anywhere — a tool body, a post-processing step, a
|
|
291
|
-
* sandboxed agent that renders through its own protocol.
|
|
292
|
-
*
|
|
293
|
-
* `id` is the component's identity within the message: a second frame with the same `id` REPLACES
|
|
294
|
-
* the first (streaming props into a chart, flipping a card from "loading" to "ready"), it never
|
|
295
|
-
* adds a second component.
|
|
296
|
-
*/
|
|
297
|
-
interface AgentUiComponent {
|
|
298
|
-
id: string;
|
|
299
|
-
/** Registry key the client resolves to its own renderer, e.g. `data-table`. */
|
|
300
|
-
component: string;
|
|
301
|
-
props: Record<string, unknown>;
|
|
302
|
-
/** Schema version of `props`, so a client can keep rendering components persisted by an older server. */
|
|
303
|
-
version?: number;
|
|
304
|
-
/**
|
|
305
|
-
* The tool call that pushed the component (`ctx.emitUi`), when one did. Lets a client place it
|
|
306
|
-
* with that call — a reloaded message puts it right after the call's tool part, where the live
|
|
307
|
-
* stream showed it. Absent for a component pushed outside a tool.
|
|
308
|
-
*/
|
|
309
|
-
toolCallId?: string;
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Who has to settle an action tool call, and until when. Metadata only: the call itself is still
|
|
313
|
-
* settled through the tool-call approve/reject routes, by its `toolCallId`.
|
|
314
|
-
*/
|
|
315
|
-
interface AgentApprovalRequest {
|
|
316
|
-
/** The tool call awaiting the decision — the `id` of a call already announced on this stream. */
|
|
317
|
-
id: string;
|
|
318
|
-
/**
|
|
319
|
-
* Who may decide. Open vocabulary the host defines — `'requester'` (the person chatting),
|
|
320
|
-
* `'admin'`, a role name, a team. A client uses it to say "waiting on an admin" instead of
|
|
321
|
-
* offering buttons the viewer cannot use.
|
|
322
|
-
*/
|
|
323
|
-
approver: string;
|
|
324
|
-
/** ISO-8601 instant after which the request lapses. Absent → it never expires. */
|
|
325
|
-
expiresAt?: string;
|
|
326
|
-
/** Why this call needs a person, in words for that person. */
|
|
327
|
-
reason?: string;
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* How an approval settled — the other half of {@link AgentApprovalRequest}, under the same `id`.
|
|
331
|
-
* Metadata again: the call's own outcome still arrives as `tool-output` (approved and ran),
|
|
332
|
-
* `tool-output-error` (approved and failed) or `tool-output-denied` (rejected or expired).
|
|
333
|
-
*/
|
|
334
|
-
interface AgentApprovalSettlement {
|
|
335
|
-
id: string;
|
|
336
|
-
status: 'approved' | 'rejected' | 'expired';
|
|
337
|
-
/** Repeated from the request, for a call approved without one being streamed (a remembered approval). */
|
|
338
|
-
approver?: string;
|
|
339
|
-
/** Opaque ref of who decided. Absent on an expiry. */
|
|
340
|
-
decidedBy?: string;
|
|
341
|
-
/** The surface the decision came through: `'web'`, `'slack'`, `'remembered'`, … */
|
|
342
|
-
decidedVia?: string;
|
|
343
|
-
/** The approval also covers later calls of this tool in this thread. */
|
|
344
|
-
remember?: boolean;
|
|
345
|
-
/** What the person said when declining. */
|
|
346
|
-
reason?: string;
|
|
347
|
-
}
|
|
348
|
-
type AgentStreamEvent = {
|
|
349
|
-
kind: 'step-start';
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Closes the step opened by the matching `step-start`. Carries the model call's token usage and
|
|
353
|
-
* `costUsd` (an estimate from the bound pricing store, or `null` when unpriced/unbound — never a
|
|
354
|
-
* fabricated `0`) so a live client can render running cost without waiting for a thread re-fetch.
|
|
355
|
-
*/
|
|
356
|
-
| {
|
|
357
|
-
kind: 'step-finish';
|
|
358
|
-
usage?: MessageUsage;
|
|
359
|
-
costUsd?: number | null;
|
|
360
|
-
/**
|
|
361
|
-
* How long the model spent thinking in this step, in ms — the same number persisted as
|
|
362
|
-
* `StoredMessage.reasoningMs`, so a live thread and a reloaded one read the same duration.
|
|
363
|
-
* Absent when the step had no reasoning.
|
|
364
|
-
*/
|
|
365
|
-
reasoningMs?: number;
|
|
366
|
-
} | {
|
|
367
|
-
kind: 'text';
|
|
368
|
-
text: string;
|
|
369
|
-
} | {
|
|
370
|
-
kind: 'reasoning';
|
|
371
|
-
text: string;
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* `toolKind` collapses `ToolKind`'s `'agent'` into `'read'` — delegation tools auto-execute like a read tool.
|
|
375
|
-
*
|
|
376
|
-
* `parentId` nests this call under another call on the same stream: the inner calls a code-mode
|
|
377
|
-
* `execute` makes, the tools a delegated sub-agent runs. The parent must be announced first. A
|
|
378
|
-
* call's parent is fixed by its first frame that names one; later frames may omit it.
|
|
379
|
-
*/
|
|
380
|
-
| {
|
|
381
|
-
kind: 'tool-input-start';
|
|
382
|
-
id: string;
|
|
383
|
-
name: string;
|
|
384
|
-
toolKind: 'read' | 'action';
|
|
385
|
-
parentId?: string;
|
|
386
|
-
} | {
|
|
387
|
-
kind: 'tool-input-delta';
|
|
388
|
-
id: string;
|
|
389
|
-
delta: string;
|
|
390
|
-
} | {
|
|
391
|
-
kind: 'tool-input-available';
|
|
392
|
-
id: string;
|
|
393
|
-
name: string;
|
|
394
|
-
input: unknown;
|
|
395
|
-
toolKind: 'read' | 'action';
|
|
396
|
-
/** Same as on `tool-input-start`, for a runner that announces a call without streaming its input. */
|
|
397
|
-
parentId?: string;
|
|
398
|
-
} | {
|
|
399
|
-
kind: 'tool-output';
|
|
400
|
-
id: string;
|
|
401
|
-
output: unknown;
|
|
402
|
-
} | {
|
|
403
|
-
kind: 'tool-output-error';
|
|
404
|
-
id: string;
|
|
405
|
-
error: string;
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* A person was asked to approve an action tool and declined it. Its own frame, NOT
|
|
409
|
-
* `tool-output-error`: a refusal is a decision with a known outcome — nothing ran — while a
|
|
410
|
-
* failure is an outcome nobody chose and whose effects are unknown. Rendering them the same way
|
|
411
|
-
* tells an operator their own "no" was a malfunction. Maps onto the SDK's `output-denied` tool
|
|
412
|
-
* part state, which a client reads without knowing any tool's name.
|
|
413
|
-
*/
|
|
414
|
-
| {
|
|
415
|
-
kind: 'tool-output-denied';
|
|
416
|
-
id: string;
|
|
417
|
-
reason?: string;
|
|
418
|
-
}
|
|
419
|
-
/**
|
|
420
|
-
* The run has put a question set to the user and is parked until someone answers it (or skips).
|
|
421
|
-
* Written by the LOOP for both elicitation surfaces — the configured intake and the model's `ask`
|
|
422
|
-
* tool — so a client renders one form either way rather than learning to recognise a tool name.
|
|
423
|
-
* The matching `tool-output` frame, under the same `id`, carries the settled answers.
|
|
424
|
-
*/
|
|
425
|
-
| {
|
|
426
|
-
kind: 'elicitation';
|
|
427
|
-
id: string;
|
|
428
|
-
request: ElicitationRequest;
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* An action tool call (already announced by `tool-input-start`/`tool-input-available` under the
|
|
432
|
-
* same `id`) is parked on a person. Optional: a client still treats an `action` call stuck at
|
|
433
|
-
* its input as pending — this frame adds WHO has to decide and UNTIL WHEN, and moves the call
|
|
434
|
-
* into the AI SDK's native `approval-requested` state.
|
|
435
|
-
*/
|
|
436
|
-
| ({
|
|
437
|
-
kind: 'approval-requested';
|
|
438
|
-
} & AgentApprovalRequest)
|
|
439
|
-
/**
|
|
440
|
-
* A parked action call was decided (or lapsed). Optional, like `approval-requested`: it adds WHO
|
|
441
|
-
* decided, THROUGH WHAT and whether the approval is REMEMBERED; the outcome itself rides the
|
|
442
|
-
* call's own output frame. See {@link AgentApprovalSettlement}.
|
|
443
|
-
*/
|
|
444
|
-
| ({
|
|
445
|
-
kind: 'approval-settled';
|
|
446
|
-
} & AgentApprovalSettlement)
|
|
447
|
-
/**
|
|
448
|
-
* Server-pushed generative UI, positioned in the message where it arrives. Not tied to a tool
|
|
449
|
-
* call. See {@link AgentUiComponent}.
|
|
450
|
-
*/
|
|
451
|
-
| ({
|
|
452
|
-
kind: 'ui';
|
|
453
|
-
} & AgentUiComponent)
|
|
454
|
-
/**
|
|
455
|
-
* The thread's title was set or changed while this run streamed (typically derived from the
|
|
456
|
-
* first exchange). Thread-level, not message content: a client updates its header/sidebar and
|
|
457
|
-
* does not render it in the transcript.
|
|
458
|
-
*/
|
|
459
|
-
| {
|
|
460
|
-
kind: 'title';
|
|
461
|
-
title: string;
|
|
462
|
-
}
|
|
463
|
-
/**
|
|
464
|
-
* Host-defined facts about the message being streamed (the model that answered, how long it took,
|
|
465
|
-
* the error it ended with), merged into the client message's `metadata`. The persisted
|
|
466
|
-
* counterpart is `StoredMessage.metadata`, so a reload reads the same values. The library's own
|
|
467
|
-
* loop never writes it; a runner that is not this library's loop uses it for what its store keeps
|
|
468
|
-
* per message.
|
|
469
|
-
*/
|
|
470
|
-
| {
|
|
471
|
-
kind: 'message-metadata';
|
|
472
|
-
metadata: Record<string, unknown>;
|
|
473
|
-
}
|
|
474
|
-
/**
|
|
475
|
-
* Someone stopped this run. The stream's LAST frame, written by the runner that settled the
|
|
476
|
-
* cancel, immediately before a normal `end()` — never a `fail()`, because a cancel is not an
|
|
477
|
-
* error and a client that retries on a failed stream must not retry this.
|
|
478
|
-
*
|
|
479
|
-
* A run that simply ends wrote everything it had; one that ends after this frame did not, and the
|
|
480
|
-
* difference is the whole point: without it a reader cannot tell a truncated answer from a
|
|
481
|
-
* complete one. Consumers that predate the frame ignore it and see the `end()` they always saw.
|
|
482
|
-
*/
|
|
483
|
-
| {
|
|
484
|
-
kind: 'cancelled';
|
|
485
|
-
}
|
|
486
|
-
/**
|
|
487
|
-
* The thread's message queue changed — a snapshot of the whole queue, never a delta, so a client
|
|
488
|
-
* that missed one frame is corrected by the next. Written into the stream of the run that is
|
|
489
|
-
* holding the thread: when someone queues, edits, reorders or removes a waiting message, and, just
|
|
490
|
-
* before this run's own terminal frame, with what happens next — `started` names the queued
|
|
491
|
-
* message that became the next turn and that turn's run id (attach to it with
|
|
492
|
-
* `GET <base>/chat/:runId/stream`), `queue.paused` says why nothing starts.
|
|
493
|
-
*/
|
|
494
|
-
| {
|
|
495
|
-
kind: 'queue';
|
|
496
|
-
queue: ChatQueueState;
|
|
497
|
-
started?: {
|
|
498
|
-
messageId: string;
|
|
499
|
-
runId: string;
|
|
500
|
-
};
|
|
501
|
-
};
|
|
502
|
-
/** Encode one event as an NDJSON line (`{...}\n`) for {@link SinkWriter.write}. */
|
|
503
|
-
declare function encodeStreamEvent(event: AgentStreamEvent): Uint8Array;
|
|
504
|
-
/**
|
|
505
|
-
* Read one NDJSON line back, or `null` when the line is not a stream event at all.
|
|
506
|
-
*
|
|
507
|
-
* `null` covers a genuinely opaque chunk, not just malformed JSON: the sink is a byte channel, so a
|
|
508
|
-
* model provider is free to write anything into it and some write bare text. A caller that has to
|
|
509
|
-
* CLASSIFY a chunk — the output gate, which may only forward what it can prove is not the answer —
|
|
510
|
-
* treats an unreadable frame as unclassifiable rather than guessing.
|
|
511
|
-
*/
|
|
512
|
-
declare function decodeStreamEvent(line: string): AgentStreamEvent | null;
|
|
513
|
-
|
|
514
|
-
interface CreateThreadInput {
|
|
515
|
-
actor: Actor;
|
|
516
|
-
transient?: boolean;
|
|
517
|
-
title?: string;
|
|
518
|
-
}
|
|
519
|
-
interface AppendMessageInput {
|
|
520
|
-
threadId: string;
|
|
521
|
-
role: StoredMessage['role'];
|
|
522
|
-
content: string;
|
|
523
|
-
/** Which agent produced this message (assistant messages) — provenance. */
|
|
524
|
-
agentName?: string;
|
|
525
|
-
toolCalls?: ToolCallRequest[];
|
|
526
|
-
toolResults?: ToolResult[];
|
|
527
|
-
/** Files the user attached to this message (image/PDF). Persisted verbatim. */
|
|
528
|
-
attachments?: MessageAttachment[];
|
|
529
|
-
followUps?: string[];
|
|
530
|
-
usage?: MessageUsage;
|
|
531
|
-
/**
|
|
532
|
-
* The run (turn) that produced this message. Without it a consumer can only guess which turn a
|
|
533
|
-
* message belongs to by comparing timestamps against the run's `startedAt`, and that guess breaks
|
|
534
|
-
* the moment a turn is regenerated — the replaced answer is truncated away, so the times no longer
|
|
535
|
-
* line up 1:1. Optional so a caller predating this (and a host that appends messages outside a
|
|
536
|
-
* run) can omit it; the store persists it as `null` when absent.
|
|
537
|
-
*/
|
|
538
|
-
runId?: string;
|
|
539
|
-
/** The step's streamed thinking. See {@link StoredMessage.reasoning}. */
|
|
540
|
-
reasoning?: string;
|
|
541
|
-
/** Time spent thinking in this step, in ms. See {@link StoredMessage.reasoningMs}. */
|
|
542
|
-
reasoningMs?: number;
|
|
543
|
-
/** Components pushed during this step. See {@link StoredMessage.ui}. */
|
|
544
|
-
ui?: AgentUiComponent[];
|
|
545
|
-
}
|
|
546
|
-
interface RecordToolCallInput {
|
|
547
|
-
toolCallId: string;
|
|
548
|
-
messageId: string;
|
|
549
|
-
toolName: string;
|
|
550
|
-
toolType: 'read' | 'action';
|
|
551
|
-
input: unknown;
|
|
552
|
-
status: ToolCallStatus;
|
|
553
|
-
/**
|
|
554
|
-
* The run (turn) this tool call belongs to — enables a governance surface to deep-link a tool
|
|
555
|
-
* call out to its trace waterfall. Optional so a caller predating this (or a store's own
|
|
556
|
-
* synthetic tool calls) can omit it; the store persists it as `null` when absent.
|
|
557
|
-
*/
|
|
558
|
-
runId?: string;
|
|
559
|
-
/**
|
|
560
|
-
* Who has to approve this call (`'requester'` or a role), from the turn's `ApprovalPolicy`. Set on
|
|
561
|
-
* an action call that was put to a person — or approved by a remembered decision — and never
|
|
562
|
-
* otherwise; persisted as `null` when absent.
|
|
563
|
-
*/
|
|
564
|
-
approver?: string;
|
|
565
|
-
/** ISO-8601 instant the approval request lapses. Absent → it never does. */
|
|
566
|
-
expiresAt?: string;
|
|
567
|
-
}
|
|
568
|
-
interface UpdateToolCallInput {
|
|
569
|
-
toolCallId: string;
|
|
570
|
-
status: ToolCallStatus;
|
|
571
|
-
output?: unknown;
|
|
572
|
-
error?: string;
|
|
573
|
-
executionMs?: number;
|
|
574
|
-
executedByRef?: string;
|
|
575
|
-
/** The approval asked for later calls of this tool in this thread to run without asking. */
|
|
576
|
-
remember?: boolean;
|
|
577
|
-
/** The surface the decision came through. See {@link import('../types.js').Decision.decidedVia}. */
|
|
578
|
-
decidedVia?: string;
|
|
579
|
-
}
|
|
580
|
-
/** What the approve/reject routes need to know about a call before they signal a decision on it. */
|
|
581
|
-
interface ToolCallApprovalState {
|
|
582
|
-
status: ToolCallStatus;
|
|
583
|
-
/** `null` for a call no policy put to anyone (an `ask`, or a row written before approvers existed). */
|
|
584
|
-
approver: string | null;
|
|
585
|
-
expiresAt: string | null;
|
|
586
|
-
}
|
|
587
|
-
/** Patch applied by {@link AgentStore.updateThread}. An omitted key leaves that field untouched. */
|
|
588
|
-
interface UpdateThreadInput {
|
|
589
|
-
title?: string;
|
|
590
|
-
/** `null` clears the thread's default agent (falls back to the module default). */
|
|
591
|
-
defaultAgent?: string | null;
|
|
592
|
-
/** `null` unpins the thread's model (turns run on the provider default). */
|
|
593
|
-
model?: string | null;
|
|
594
|
-
}
|
|
595
|
-
interface RecordUsageInput {
|
|
596
|
-
threadId: string;
|
|
597
|
-
actorRef: string;
|
|
598
|
-
messageId?: string;
|
|
599
|
-
modelId: string;
|
|
600
|
-
purpose: UsagePurpose;
|
|
601
|
-
usage: MessageUsage;
|
|
602
|
-
/** Provider-reported actual USD cost for this turn, when known (gateways report it). */
|
|
603
|
-
costUsd?: number;
|
|
604
|
-
}
|
|
605
|
-
interface RecordRunStartInput {
|
|
606
|
-
runId: string;
|
|
607
|
-
threadId: string;
|
|
608
|
-
actorRef: string;
|
|
609
|
-
agentName?: string;
|
|
610
|
-
/**
|
|
611
|
-
* The run that started this one, for a delegation's child run. The parent->child edge exists in
|
|
612
|
-
* the durable runtime's own journal, but only there: a governance surface reading run rows alone
|
|
613
|
-
* cannot roll a delegation's cost up to the turn that asked for it, and a DETACHED child outlives
|
|
614
|
-
* its parent's turn entirely, so nothing in the transcript pairs them either.
|
|
615
|
-
*
|
|
616
|
-
* Optional, and a store that persists nothing for it still works — it loses the tree, not the run.
|
|
617
|
-
*/
|
|
618
|
-
parentRunId?: string;
|
|
619
|
-
/** sha256 hex of the run's resolved (pre-RAG) system prompt — identifies the prompt VERSION. */
|
|
620
|
-
promptHash?: string;
|
|
621
|
-
}
|
|
622
|
-
interface RecordRunEndInput {
|
|
623
|
-
runId: string;
|
|
624
|
-
/**
|
|
625
|
-
* `cancelled` is a THIRD terminal, not a flavour of `failed`: someone asked the run to stop and it
|
|
626
|
-
* did, which is the control working. A consumer computing a failure rate over these rows has to be
|
|
627
|
-
* able to leave it out — counting a user pressing Stop as an error pages whoever is on call for
|
|
628
|
-
* model failures. It carries no `errorCode`/`errorMessage`, since there is nothing to diagnose.
|
|
629
|
-
*/
|
|
630
|
-
status: 'completed' | 'failed' | 'cancelled';
|
|
631
|
-
durationMs?: number;
|
|
632
|
-
errorCode?: string;
|
|
633
|
-
errorMessage?: string;
|
|
634
|
-
}
|
|
635
|
-
/** Which thread, and how many of its newest messages, {@link ThreadTurnReader.loadThreadForTurn} reads. */
|
|
636
|
-
interface ThreadTurnQuery {
|
|
637
|
-
threadId: string;
|
|
638
|
-
/** Omitted reads every message; `0` reads none. */
|
|
639
|
-
messageLimit?: number;
|
|
640
|
-
}
|
|
641
|
-
/** What a turn reads off a thread — a bounded window, not the transcript. */
|
|
642
|
-
interface ThreadTurnPage {
|
|
643
|
-
title: string;
|
|
644
|
-
defaultAgent: string | null;
|
|
645
|
-
/** Whether the THREAD has ever been answered, not whether {@link messages} holds an answer. */
|
|
646
|
-
hasAssistantMessage: boolean;
|
|
647
|
-
/** Oldest first, carrying only the fields a model turn reads. */
|
|
648
|
-
messages: StoredMessage[];
|
|
649
|
-
}
|
|
650
|
-
/**
|
|
651
|
-
* A store that can hand a turn the WINDOW it is about to send, instead of the thread's transcript.
|
|
652
|
-
*
|
|
653
|
-
* {@link AgentStore.getThread} materializes every message row, every attachment and every tool
|
|
654
|
-
* output a thread ever recorded, and the run then journals what it loaded — so a long thread pays
|
|
655
|
-
* for its whole history on every turn and again on every replay, to send a prompt bounded to its
|
|
656
|
-
* last few messages. This read is bounded by the database (`order by created_at desc limit ?`),
|
|
657
|
-
* projected to the columns a model turn actually reads.
|
|
658
|
-
*
|
|
659
|
-
* `hasAssistantMessage` is answered over the WHOLE thread, never the page: it answers "has this
|
|
660
|
-
* conversation been answered before?" — what a `thread-start` intake asks — and a thread whose window
|
|
661
|
-
* happens to hold only the user's last questions has still been answered. `null` for a thread that is
|
|
662
|
-
* unknown or soft-deleted, matching `getThread`.
|
|
663
|
-
*
|
|
664
|
-
* Probed STRUCTURALLY rather than declared on {@link AgentStore}, the same way `defaultAgentForThread`
|
|
665
|
-
* is: it is an optimization a store either offers or does not, and one that predates it still answers
|
|
666
|
-
* correctly through the full read.
|
|
264
|
+
* Probed STRUCTURALLY rather than declared on {@link AgentStore}, the same way `defaultAgentForThread`
|
|
265
|
+
* is: it is an optimization a store either offers or does not, and one that predates it still answers
|
|
266
|
+
* correctly through the full read.
|
|
667
267
|
*/
|
|
668
268
|
interface ThreadTurnReader {
|
|
669
269
|
loadThreadForTurn(query: ThreadTurnQuery): Promise<ThreadTurnPage | null>;
|
|
@@ -773,6 +373,21 @@ interface AgentStore {
|
|
|
773
373
|
setMessageFeedback?(messageId: string, feedback: MessageFeedback | null): Promise<void>;
|
|
774
374
|
recordToolCall(input: RecordToolCallInput): Promise<void>;
|
|
775
375
|
updateToolCall(input: UpdateToolCallInput): Promise<void>;
|
|
376
|
+
/**
|
|
377
|
+
* OPTIONAL: what is recorded about each of these calls — its status, and the output or error it
|
|
378
|
+
* settled with. Read when a thread's history holds a tool call its message has no result for (the
|
|
379
|
+
* turn died mid-step), so the next turn can be told what actually happened instead of being
|
|
380
|
+
* handed a call answered by nothing. Unknown ids are left out. Absent → such a call is put to the
|
|
381
|
+
* model as never completed.
|
|
382
|
+
*/
|
|
383
|
+
toolCallOutcomes?(toolCallIds: readonly string[]): Promise<ToolCallOutcome[]>;
|
|
384
|
+
/**
|
|
385
|
+
* OPTIONAL: settle every call of `runId` still awaiting a decision (`pending_approval`) as
|
|
386
|
+
* `failed` with `error`, and answer how many there were. Called when the run ends without
|
|
387
|
+
* settling them — it failed, or it was found dead — so an approval card never waits on a run that
|
|
388
|
+
* will not come back. Calls already settled are left alone, so a repeat changes nothing.
|
|
389
|
+
*/
|
|
390
|
+
failUnsettledToolCalls?(runId: string, error: string): Promise<number>;
|
|
776
391
|
/**
|
|
777
392
|
* OPTIONAL: the names of the tools whose approval someone asked to REMEMBER in this thread — an
|
|
778
393
|
* approved call persisted with `remember: true`. The loop approves a later call of one of them
|
|
@@ -1695,281 +1310,646 @@ interface ThreadSummary {
|
|
|
1695
1310
|
*/
|
|
1696
1311
|
model?: string | null;
|
|
1697
1312
|
}
|
|
1698
|
-
interface StoredMessage {
|
|
1699
|
-
id: string;
|
|
1700
|
-
role: MessageRole;
|
|
1701
|
-
content: string;
|
|
1702
|
-
/** Which agent produced this message (assistant messages) — provenance for replay / UI / telescope. */
|
|
1703
|
-
agentName?: string;
|
|
1704
|
-
toolCalls?: ToolCallRequest[];
|
|
1705
|
-
toolResults?: ToolResult[];
|
|
1706
|
-
/** Files the user attached to this message (image/PDF). Persisted with the message, replayed as-is. */
|
|
1707
|
-
attachments?: MessageAttachment[];
|
|
1708
|
-
followUps?: string[];
|
|
1709
|
-
usage?: MessageUsage;
|
|
1710
|
-
/** The run (turn) that produced this message; absent on a row written before this was recorded. */
|
|
1711
|
-
runId?: string;
|
|
1313
|
+
interface StoredMessage {
|
|
1314
|
+
id: string;
|
|
1315
|
+
role: MessageRole;
|
|
1316
|
+
content: string;
|
|
1317
|
+
/** Which agent produced this message (assistant messages) — provenance for replay / UI / telescope. */
|
|
1318
|
+
agentName?: string;
|
|
1319
|
+
toolCalls?: ToolCallRequest[];
|
|
1320
|
+
toolResults?: ToolResult[];
|
|
1321
|
+
/** Files the user attached to this message (image/PDF). Persisted with the message, replayed as-is. */
|
|
1322
|
+
attachments?: MessageAttachment[];
|
|
1323
|
+
followUps?: string[];
|
|
1324
|
+
usage?: MessageUsage;
|
|
1325
|
+
/** The run (turn) that produced this message; absent on a row written before this was recorded. */
|
|
1326
|
+
runId?: string;
|
|
1327
|
+
/**
|
|
1328
|
+
* The model's thinking for this step, as it streamed (`reasoning` frames), so a reloaded thread
|
|
1329
|
+
* shows it where the live one did. Absent when the model produced none, or on a row written
|
|
1330
|
+
* before this was recorded.
|
|
1331
|
+
*/
|
|
1332
|
+
reasoning?: string;
|
|
1333
|
+
/** How long the model spent thinking in this step, in ms — what a "Thought for 4s" label reads. */
|
|
1334
|
+
reasoningMs?: number;
|
|
1335
|
+
/**
|
|
1336
|
+
* Components the server pushed into this step (`ui` frames), in first-seen order with the last
|
|
1337
|
+
* props for each `id` — a reloaded thread replays them as `data-ui` parts.
|
|
1338
|
+
*/
|
|
1339
|
+
ui?: AgentUiComponent[];
|
|
1340
|
+
/**
|
|
1341
|
+
* The approval record of every call on this message that was put to a person under an
|
|
1342
|
+
* {@link import('./spi/approval-policy.js').ApprovalPolicy} — who had to decide, until when, and how
|
|
1343
|
+
* it settled. Read off the tool-call rows by the store; absent when no call on the message asked
|
|
1344
|
+
* for one, or on a store that does not record approvals.
|
|
1345
|
+
*/
|
|
1346
|
+
approvals?: ToolCallApproval[];
|
|
1347
|
+
/**
|
|
1348
|
+
* The thread owner's rating of this message (`POST <base>/messages/:id/feedback`). Absent when
|
|
1349
|
+
* nobody rated it, or on a store that does not record feedback.
|
|
1350
|
+
*/
|
|
1351
|
+
feedback?: MessageFeedback;
|
|
1352
|
+
/**
|
|
1353
|
+
* Host-defined facts about the message (which model answered, how long it took, the error a turn
|
|
1354
|
+
* ended with, …) — what a runner that is not this library's loop streamed as a
|
|
1355
|
+
* `message-metadata` frame. Replayed into the client message's `metadata`, under the library's
|
|
1356
|
+
* own keys (`usage`, `feedback`, `createdAt` win on a clash). Never read by the library itself.
|
|
1357
|
+
*/
|
|
1358
|
+
metadata?: Record<string, unknown>;
|
|
1359
|
+
createdAt: string;
|
|
1360
|
+
}
|
|
1361
|
+
/** A thumbs-up/down on one message, with an optional free-text comment. */
|
|
1362
|
+
type MessageFeedbackValue = 'up' | 'down';
|
|
1363
|
+
/** What {@link StoredMessage.feedback} holds. Not copied when a thread is forked. */
|
|
1364
|
+
interface MessageFeedback {
|
|
1365
|
+
value: MessageFeedbackValue;
|
|
1366
|
+
comment?: string;
|
|
1367
|
+
/** ISO-8601 instant the rating was last set. */
|
|
1368
|
+
updatedAt: string;
|
|
1369
|
+
}
|
|
1370
|
+
/**
|
|
1371
|
+
* How one approval stands. `pending` → still parked; `approved` → someone said yes (or a remembered
|
|
1372
|
+
* approval did); `rejected` → someone said no; `expired` → nobody answered before `expiresAt`.
|
|
1373
|
+
*/
|
|
1374
|
+
type ToolCallApprovalStatus = 'pending' | 'approved' | 'rejected' | 'expired';
|
|
1375
|
+
/** The persisted approval metadata of one action tool call. See {@link StoredMessage.approvals}. */
|
|
1376
|
+
interface ToolCallApproval {
|
|
1377
|
+
toolCallId: string;
|
|
1378
|
+
/** Who may decide: `'requester'` (the thread's own actor) or a role name. */
|
|
1379
|
+
approver: string;
|
|
1380
|
+
/** ISO-8601 instant the request lapses; absent → it never does. */
|
|
1381
|
+
expiresAt?: string;
|
|
1382
|
+
status: ToolCallApprovalStatus;
|
|
1383
|
+
/** The decision asked for later calls of this tool in this thread to be approved automatically. */
|
|
1384
|
+
remember?: boolean;
|
|
1385
|
+
/** Opaque ref of who decided. Absent while pending and on an expiry. */
|
|
1386
|
+
decidedBy?: string;
|
|
1387
|
+
/** The surface the decision came through (`'web'`, `'slack'`, `'remembered'`, …). */
|
|
1388
|
+
decidedVia?: string;
|
|
1389
|
+
/** What the person said when declining. */
|
|
1390
|
+
reason?: string;
|
|
1391
|
+
}
|
|
1392
|
+
interface ThreadDetail extends ThreadSummary {
|
|
1393
|
+
messages: StoredMessage[];
|
|
1394
|
+
/**
|
|
1395
|
+
* Messages sent while a turn was running, waiting to run after it, and whether the queue is
|
|
1396
|
+
* draining. Present when the store supports a queue (`ChatQueueStore`); the REST read-model
|
|
1397
|
+
* omits it otherwise.
|
|
1398
|
+
*/
|
|
1399
|
+
queue?: ChatQueueState;
|
|
1400
|
+
}
|
|
1401
|
+
type ToolCallStatus = 'auto_executed' | 'pending_approval' | 'executed' | 'rejected' | 'failed'
|
|
1402
|
+
/** An approval request lapsed before anyone decided; the tool never ran. */
|
|
1403
|
+
| 'expired';
|
|
1404
|
+
/**
|
|
1405
|
+
* Serializable input for a dispatched model-turn step. Carries only data — the serving worker
|
|
1406
|
+
* re-resolves the model/sink/registry from its own DI via AGENT_DEPS_FACTORY.forAgent(agentName).
|
|
1407
|
+
*/
|
|
1408
|
+
interface LlmStepEnvelope {
|
|
1409
|
+
/** Undefined = default agent (same semantics as {@link AgentRunInput.agentName}). */
|
|
1410
|
+
agentName?: string;
|
|
1411
|
+
system: string;
|
|
1412
|
+
messages: ModelMessage[];
|
|
1413
|
+
/** The turn's actor — the handler re-derives tool definitions from it (definitionsFor). */
|
|
1414
|
+
actor: Actor;
|
|
1415
|
+
/**
|
|
1416
|
+
* The turn's thread — with {@link actor}, what a tool's per-turn `describe` is scoped on. Optional
|
|
1417
|
+
* so an envelope from a loop that predates it still parses.
|
|
1418
|
+
*/
|
|
1419
|
+
threadId?: string;
|
|
1420
|
+
/**
|
|
1421
|
+
* Hold this call's stream frames rather than writing them to the run's sink, and return them on
|
|
1422
|
+
* the result. Set by the loop when an output processor has to see the whole answer before the
|
|
1423
|
+
* subscriber does — the dispatched handler streams to a worker-side sink the loop cannot
|
|
1424
|
+
* interpose on, so the instruction has to ride the envelope. Absent → stream live, as before.
|
|
1425
|
+
*/
|
|
1426
|
+
bufferOutput?: boolean;
|
|
1427
|
+
/** The turn's selected model ({@link AgentRunInput.model}), for the worker's provider call. */
|
|
1428
|
+
model?: string;
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* The serializable subset of `AiToolCtx` — everything except `host` (re-attached handler-side
|
|
1432
|
+
* from DI).
|
|
1433
|
+
*/
|
|
1434
|
+
interface ToolStepCtx {
|
|
1435
|
+
actor: Actor;
|
|
1436
|
+
threadId: string;
|
|
1437
|
+
runId: string;
|
|
1438
|
+
requestId: string;
|
|
1439
|
+
agentName?: string;
|
|
1440
|
+
pageContext?: PageContext;
|
|
1441
|
+
}
|
|
1442
|
+
/** Serializable input for a dispatched tool-execution step. */
|
|
1443
|
+
interface ToolStepEnvelope {
|
|
1444
|
+
toolName: string;
|
|
1445
|
+
input: unknown;
|
|
1446
|
+
ctx: ToolStepCtx;
|
|
1447
|
+
/** Applied INSIDE the handler (`withToolTimeout`) — never as a durable step `timeoutMs`. */
|
|
1448
|
+
timeoutMs?: number;
|
|
1449
|
+
/**
|
|
1450
|
+
* The numeric half of `toolTransientRetry` (resolved by the loop from `AgentLoopDeps`, always a
|
|
1451
|
+
* definite value — `false` when disabled, else concrete `{ attempts, backoffMs }` with defaults
|
|
1452
|
+
* already filled in) — never `undefined`, so the dispatched handler gets the SAME policy the
|
|
1453
|
+
* loop would have used locally. The `classify` function is deliberately absent: it isn't
|
|
1454
|
+
* wire-safe, so the handler resolves its own from its local module options (see
|
|
1455
|
+
* `AgentRunSteps.tool`) instead of trying to serialize a function.
|
|
1456
|
+
*/
|
|
1457
|
+
transientRetry: ToolTransientRetryNumbers | false;
|
|
1458
|
+
/**
|
|
1459
|
+
* The dispatching loop reads the components the tool pushed (`ctx.emitUi`) off the step's result.
|
|
1460
|
+
* A handler that sees it returns {@link wrapToolStepOutput}'s envelope when the tool pushed any —
|
|
1461
|
+
* and ONLY then, so a loop that predates this (and never sets it) always gets the bare output.
|
|
1462
|
+
*/
|
|
1463
|
+
collectUi?: boolean;
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* What `GET <base>/config` answers — server-side facts a client would otherwise repeat in its own
|
|
1467
|
+
* configuration (and let drift).
|
|
1468
|
+
*/
|
|
1469
|
+
interface AgentClientConfig {
|
|
1470
|
+
attachments: AgentAttachmentConfig;
|
|
1471
|
+
/** A model catalog is bound, so `GET <base>/models` lists something to pick. */
|
|
1472
|
+
models: {
|
|
1473
|
+
enabled: boolean;
|
|
1474
|
+
};
|
|
1475
|
+
/** Sends are refused with `429` once `GET <base>/quota` reports `blocked`. */
|
|
1476
|
+
quota: {
|
|
1477
|
+
enforced: boolean;
|
|
1478
|
+
};
|
|
1479
|
+
/** No `actorResolver`: every browser is its own anonymous actor. */
|
|
1480
|
+
identity: {
|
|
1481
|
+
anonymous: boolean;
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
/** The attachment rules in force — what the upload route enforces. */
|
|
1485
|
+
interface AgentAttachmentConfig {
|
|
1486
|
+
/** A staging store is bound, so uploads work at all. */
|
|
1487
|
+
enabled: boolean;
|
|
1488
|
+
/** How a client uploads (`null` when `enabled` is false). */
|
|
1489
|
+
upload: 'multipart' | 'resumable' | null;
|
|
1490
|
+
maxBytes: number;
|
|
1491
|
+
allowedContentTypes: readonly string[];
|
|
1492
|
+
/** How many attachments one message may name. */
|
|
1493
|
+
maxPerMessage: number;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
/**
|
|
1497
|
+
* Asking the USER a structured question, and waiting for the answer.
|
|
1498
|
+
*
|
|
1499
|
+
* `awaitApproval` collects a yes/no about work already proposed; this collects the scope BEFORE the
|
|
1500
|
+
* work. Two surfaces produce it — a configured intake (`AgentLoopDeps.intake`) and the model-callable
|
|
1501
|
+
* `ask` tool (`AgentLoopDeps.ask`) — and they deliberately produce the SAME {@link
|
|
1502
|
+
* ElicitationRequest}, persist through the same tool-call row, and resume through the same
|
|
1503
|
+
* `tool:<runId>:<callId>` signal. A consumer cannot tell which one asked, and should not have to.
|
|
1504
|
+
*/
|
|
1505
|
+
|
|
1506
|
+
/** One choice a question offers. */
|
|
1507
|
+
interface ElicitationOption {
|
|
1508
|
+
/** Stable identifier submitted back. Never shown to the user. */
|
|
1509
|
+
value: string;
|
|
1510
|
+
/** What the user reads. */
|
|
1511
|
+
label: string;
|
|
1512
|
+
/**
|
|
1513
|
+
* A single character a UI may bind as a keyboard shortcut for this option. Advisory — nothing in
|
|
1514
|
+
* the library reads it, and a client is free to render its own.
|
|
1515
|
+
*/
|
|
1516
|
+
hotkey?: string;
|
|
1517
|
+
}
|
|
1518
|
+
/** One question in a set. */
|
|
1519
|
+
interface ElicitationQuestion {
|
|
1520
|
+
/** Unique within its request; the key answers come back under. */
|
|
1521
|
+
id: string;
|
|
1522
|
+
prompt: string;
|
|
1523
|
+
/** A line of help under the prompt. */
|
|
1524
|
+
description?: string;
|
|
1525
|
+
/**
|
|
1526
|
+
* The choices. Required unless {@link input} asks for a typed value — then optional, and a UI may
|
|
1527
|
+
* offer them as suggestions. An `input` of type `select` still picks from these.
|
|
1528
|
+
*/
|
|
1529
|
+
options?: ElicitationOption[];
|
|
1530
|
+
/**
|
|
1531
|
+
* Ask for a typed value (a number, a date, a sentence…) instead of — or, for `select`, on top of —
|
|
1532
|
+
* a pick from `options`. The answer is still a `string[]`, in the type's canonical form; see
|
|
1533
|
+
* `elicitation-input.ts`.
|
|
1534
|
+
*/
|
|
1535
|
+
input?: ElicitationInput;
|
|
1536
|
+
/** More than one option may be chosen. Omit → single choice. */
|
|
1537
|
+
multiple?: boolean;
|
|
1538
|
+
/**
|
|
1539
|
+
* The options already picked for the user. The claim this whole surface makes is that confirming
|
|
1540
|
+
* is enough, so a question with no defaults is a question the user must stop and think about —
|
|
1541
|
+
* which is the case the design is trying to avoid. Empty/omitted is allowed and means exactly
|
|
1542
|
+
* that: submitting without answering leaves this question unanswered.
|
|
1543
|
+
*/
|
|
1544
|
+
defaults?: string[];
|
|
1545
|
+
/** Accept values that are not among `options` (a typed-in answer). Omit → options only. */
|
|
1546
|
+
allowFreeText?: boolean;
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* A question set awaiting a human. Identical in shape whether an `@Agent`'s configured intake or
|
|
1550
|
+
* the model's `ask` tool authored it — `source` records which, for audit, not for control flow.
|
|
1551
|
+
*
|
|
1552
|
+
* `questions.length` is known when the request is written, which is what lets a client render
|
|
1553
|
+
* "Question 1 of 3" without guessing whether a fourth is coming.
|
|
1554
|
+
*/
|
|
1555
|
+
interface ElicitationRequest {
|
|
1556
|
+
/** The tool-call id this request is persisted under, and the signal it is answered through. */
|
|
1557
|
+
id: string;
|
|
1558
|
+
source: 'intake' | 'ask';
|
|
1559
|
+
/** What the assistant says above the form. */
|
|
1560
|
+
preamble?: string;
|
|
1561
|
+
questions: ElicitationQuestion[];
|
|
1562
|
+
}
|
|
1563
|
+
/** What a human sent back for an {@link ElicitationRequest}. */
|
|
1564
|
+
interface ElicitationReply {
|
|
1712
1565
|
/**
|
|
1713
|
-
*
|
|
1714
|
-
*
|
|
1715
|
-
*
|
|
1566
|
+
* questionId → chosen values. A question whose id is ABSENT takes the request's own `defaults` —
|
|
1567
|
+
* that is what makes "just submit" mean "yes, your pre-picked answers". A present-but-empty array
|
|
1568
|
+
* is an explicit "none of these" and does NOT fall back.
|
|
1716
1569
|
*/
|
|
1717
|
-
|
|
1718
|
-
/** How long the model spent thinking in this step, in ms — what a "Thought for 4s" label reads. */
|
|
1719
|
-
reasoningMs?: number;
|
|
1570
|
+
answers: Record<string, string[]>;
|
|
1720
1571
|
/**
|
|
1721
|
-
*
|
|
1722
|
-
*
|
|
1572
|
+
* The user declined to answer and told the agent to proceed on its own assumptions. Distinct from
|
|
1573
|
+
* confirming the defaults even though the resulting values are the same: one is a decision the
|
|
1574
|
+
* user made, the other is one they refused to make, and only the first is evidence of intent.
|
|
1723
1575
|
*/
|
|
1724
|
-
|
|
1576
|
+
skipped?: boolean;
|
|
1577
|
+
/** Opaque ref of WHO answered, when it wasn't the run's own actor. */
|
|
1578
|
+
answeredByRef?: string;
|
|
1725
1579
|
/**
|
|
1726
|
-
* The
|
|
1727
|
-
*
|
|
1728
|
-
* it settled. Read off the tool-call rows by the store; absent when no call on the message asked
|
|
1729
|
-
* for one, or on a store that does not record approvals.
|
|
1580
|
+
* The surface the answer came through — `'web'`, `'slack'`, `'console'`, … — the counterpart of
|
|
1581
|
+
* an approval's `decidedVia`.
|
|
1730
1582
|
*/
|
|
1731
|
-
|
|
1583
|
+
answeredVia?: string;
|
|
1584
|
+
}
|
|
1585
|
+
/** A settled elicitation: what the agent proceeds on, and how it got there. */
|
|
1586
|
+
interface ElicitationOutcome {
|
|
1587
|
+
/** One entry per question, in request order — always present, so a caller never re-applies defaults. */
|
|
1588
|
+
answers: Record<string, string[]>;
|
|
1589
|
+
skipped: boolean;
|
|
1590
|
+
/** Question ids filled from the request's `defaults` rather than by the human. */
|
|
1591
|
+
defaulted: string[];
|
|
1732
1592
|
/**
|
|
1733
|
-
*
|
|
1734
|
-
*
|
|
1593
|
+
* Who answered (or skipped) — the reply's `answeredByRef`, which a host may make a display name.
|
|
1594
|
+
* The counterpart of an approval's `decidedBy`. Absent when the reply did not say.
|
|
1735
1595
|
*/
|
|
1736
|
-
|
|
1596
|
+
answeredBy?: string;
|
|
1597
|
+
/** The surface it came through (`'web'`, `'slack'`, …) — an approval's `decidedVia`. */
|
|
1598
|
+
answeredVia?: string;
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Read whatever the human channel delivered as an {@link ElicitationReply}.
|
|
1602
|
+
*
|
|
1603
|
+
* A question set is persisted as an `action` tool call in `pending_approval` — that is what puts it
|
|
1604
|
+
* in the approvals inbox a deployment already has, instead of needing one of its own. The cost of
|
|
1605
|
+
* that choice is that the thing which comes back may be a {@link Decision} someone pressed
|
|
1606
|
+
* Approve/Reject on rather than a set of answers, and a `Decision` carries no `answers` at all.
|
|
1607
|
+
*
|
|
1608
|
+
* Approve means every question keeps its own pre-picked `defaults`, which is exactly what "just
|
|
1609
|
+
* submit" already means on this surface; Reject is the same declining-to-answer a skip is. Neither
|
|
1610
|
+
* reading is a guess — a yes/no channel cannot say more than that, and saying it here is what lets
|
|
1611
|
+
* one inbox settle both kinds of pending work.
|
|
1612
|
+
*
|
|
1613
|
+
* Returns the reply UNCHANGED when it already carries answers, so the common path allocates nothing
|
|
1614
|
+
* and a caller can identity-compare.
|
|
1615
|
+
*/
|
|
1616
|
+
declare function normalizeElicitationReply(reply: ElicitationReply | Decision): ElicitationReply;
|
|
1617
|
+
/**
|
|
1618
|
+
* Settle a reply against the request it answers: fill every unanswered question from its own
|
|
1619
|
+
* `defaults`, drop submitted values that aren't on offer, and collapse a single-choice question to
|
|
1620
|
+
* one value.
|
|
1621
|
+
*
|
|
1622
|
+
* PURE, and deliberately so. Both of its inputs are already journaled by the time the loop calls it
|
|
1623
|
+
* — the request came from module config or from an `llm:<i>` checkpoint, the reply from the signal
|
|
1624
|
+
* checkpoint — so every process replaying the turn reaches the same values without a checkpoint of
|
|
1625
|
+
* its own. Resolving defaults in the HTTP layer instead would put them behind a store read that a
|
|
1626
|
+
* replay would have to repeat.
|
|
1627
|
+
*/
|
|
1628
|
+
declare function resolveElicitation(request: ElicitationRequest, raw: ElicitationReply | Decision): ElicitationOutcome;
|
|
1629
|
+
/**
|
|
1630
|
+
* What a settled elicitation looks like to everyone downstream: the model reading it back as a tool
|
|
1631
|
+
* result, the thread reader rendering it, the auditor asking what the agent was told to do. One
|
|
1632
|
+
* shape for both surfaces — nothing here records which of them asked.
|
|
1633
|
+
*/
|
|
1634
|
+
interface ElicitationResult extends ElicitationOutcome {
|
|
1635
|
+
/** The questions against the chosen LABELS, so a reader (and a model) can act on it. */
|
|
1636
|
+
summary: string;
|
|
1637
|
+
}
|
|
1638
|
+
/** {@link resolveElicitation} plus its human-readable rendering. Pure, for the same reason. */
|
|
1639
|
+
declare function settleElicitation(request: ElicitationRequest, reply: ElicitationReply | Decision): ElicitationResult;
|
|
1640
|
+
/**
|
|
1641
|
+
* The answers as the model reads them: the question's own prompt against the chosen options' LABELS,
|
|
1642
|
+
* not their opaque `value`s — a model shown `{"scope":["b"]}` has been told nothing.
|
|
1643
|
+
*/
|
|
1644
|
+
declare function renderElicitationAnswers(request: ElicitationRequest, outcome: ElicitationOutcome): string;
|
|
1645
|
+
/** The reserved tool name the model calls to ask the user something. */
|
|
1646
|
+
declare const ASK_TOOL_NAME = "ask";
|
|
1647
|
+
/** What the model must supply when it calls `ask`. */
|
|
1648
|
+
interface AskToolInput {
|
|
1649
|
+
preamble?: string;
|
|
1650
|
+
questions: ElicitationQuestion[];
|
|
1651
|
+
}
|
|
1652
|
+
/** How many questions one `ask` may carry. A form the user has to scroll is a form they skip. */
|
|
1653
|
+
declare const MAX_ASK_QUESTIONS = 5;
|
|
1654
|
+
/**
|
|
1655
|
+
* The `ask` tool's input schema, hand-written rather than borrowed from a validation library: core
|
|
1656
|
+
* depends on no validator, and the schema has to carry a JSON Schema a provider can constrain
|
|
1657
|
+
* generation against. It publishes one through the Standard JSON Schema extension
|
|
1658
|
+
* (`~standard.jsonSchema.input`), which is the path the AI SDK adapter already recognises for
|
|
1659
|
+
* Valibot / ArkType / Zod 4.
|
|
1660
|
+
*/
|
|
1661
|
+
declare const askInputSchema: StandardSchemaV1<unknown, AskToolInput>;
|
|
1662
|
+
/**
|
|
1663
|
+
* What the model is told the `ask` tool is for. Written to discourage the two failure modes that
|
|
1664
|
+
* make a clarifying question worse than a guess: asking about something the conversation already
|
|
1665
|
+
* settled, and asking without saying what you would have done.
|
|
1666
|
+
*/
|
|
1667
|
+
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.";
|
|
1668
|
+
/**
|
|
1669
|
+
* The `ask` tool as the model sees it. NOT a `ToolSpec` and never registered: `ask` has no handler,
|
|
1670
|
+
* because the loop settles it against a human instead of invoking anything. Keeping it out of the
|
|
1671
|
+
* `ToolRegistry` is also what keeps the kind decision off a process-local lookup — see
|
|
1672
|
+
* `claimToolCall`.
|
|
1673
|
+
*/
|
|
1674
|
+
declare function askToolDefinition(): ToolDefinition;
|
|
1675
|
+
/** A question set an `@Agent` asks before it starts working. See `AgentLoopDeps.intake`. */
|
|
1676
|
+
interface AgentIntake {
|
|
1677
|
+
questions: ElicitationQuestion[];
|
|
1678
|
+
/** What the assistant says above the form. Omit → {@link DEFAULT_INTAKE_PREAMBLE}. */
|
|
1679
|
+
preamble?: string;
|
|
1737
1680
|
/**
|
|
1738
|
-
*
|
|
1739
|
-
*
|
|
1740
|
-
*
|
|
1741
|
-
*
|
|
1681
|
+
* `'thread-start'` (default) asks once, on the first turn of a thread; `'every-turn'` asks before
|
|
1682
|
+
* every turn. Both are decided from what `load:thread` recorded about the thread when the turn
|
|
1683
|
+
* began, never from anything this process happens to know — by the time a replay reaches the
|
|
1684
|
+
* question, the thread already holds the assistant message the first attempt wrote.
|
|
1742
1685
|
*/
|
|
1743
|
-
|
|
1744
|
-
createdAt: string;
|
|
1686
|
+
when?: 'thread-start' | 'every-turn';
|
|
1745
1687
|
}
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
/**
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1688
|
+
declare const DEFAULT_INTAKE_PREAMBLE = "A few questions before I start. I have pre-picked what I would choose, so confirming is enough.";
|
|
1689
|
+
|
|
1690
|
+
/**
|
|
1691
|
+
* The structured live-stream vocabulary carried over the {@link SinkWriter} byte channel.
|
|
1692
|
+
*
|
|
1693
|
+
* The model turn (via the AI-SDK adapter) and the agent loop write these events as NDJSON — one
|
|
1694
|
+
* `JSON.stringify(event)\n` per {@link SinkWriter.write}. The HTTP layer forwards each line as an
|
|
1695
|
+
* SSE `data:` frame, and the client transport maps them back to the AI SDK UI-message chunk
|
|
1696
|
+
* protocol so the browser renders text, reasoning, and tool cards (input + output) LIVE — the same
|
|
1697
|
+
* rich rendering a raw `streamText().toUIMessageStream()` would give, but reconstructed on the
|
|
1698
|
+
* client so the sink stays a format-agnostic byte buffer (durable buffering/replay is untouched).
|
|
1699
|
+
*
|
|
1700
|
+
* Keeping this vocabulary neutral (not AI-SDK `UIMessageChunk`) means core never depends on `ai`:
|
|
1701
|
+
* the adapter owns model-parts → event, the transport owns event → UI-chunk.
|
|
1702
|
+
*
|
|
1703
|
+
* The vocabulary is also a CONTRACT for runners that are not this library's loop: anything that
|
|
1704
|
+
* writes these frames (one JSON object per SSE `data:` line) gets the React transport, transcript
|
|
1705
|
+
* model and hooks for free. Two rules keep it evolvable:
|
|
1706
|
+
* - every frame is a JSON object with a string `kind`; a reader MUST tolerate kinds it does not
|
|
1707
|
+
* know (the React transport forwards them as `data-<kind>` parts rather than dropping them);
|
|
1708
|
+
* - fields are only ever added, and new fields are optional.
|
|
1709
|
+
*/
|
|
1710
|
+
|
|
1711
|
+
/**
|
|
1712
|
+
* A component the server pushed into the conversation: generative UI that is NOT a tool call's
|
|
1713
|
+
* rendering. It is addressed by `component` (a key in the client's own component registry), never
|
|
1714
|
+
* by a tool name, so a runner can emit one from anywhere — a tool body, a post-processing step, a
|
|
1715
|
+
* sandboxed agent that renders through its own protocol.
|
|
1716
|
+
*
|
|
1717
|
+
* `id` is the component's identity within the message: a second frame with the same `id` REPLACES
|
|
1718
|
+
* the first (streaming props into a chart, flipping a card from "loading" to "ready"), it never
|
|
1719
|
+
* adds a second component.
|
|
1720
|
+
*/
|
|
1721
|
+
interface AgentUiComponent {
|
|
1722
|
+
id: string;
|
|
1723
|
+
/** Registry key the client resolves to its own renderer, e.g. `data-table`. */
|
|
1724
|
+
component: string;
|
|
1725
|
+
props: Record<string, unknown>;
|
|
1726
|
+
/** Schema version of `props`, so a client can keep rendering components persisted by an older server. */
|
|
1727
|
+
version?: number;
|
|
1728
|
+
/**
|
|
1729
|
+
* The tool call that pushed the component (`ctx.emitUi`), when one did. Lets a client place it
|
|
1730
|
+
* with that call — a reloaded message puts it right after the call's tool part, where the live
|
|
1731
|
+
* stream showed it. Absent for a component pushed outside a tool.
|
|
1732
|
+
*/
|
|
1733
|
+
toolCallId?: string;
|
|
1754
1734
|
}
|
|
1755
1735
|
/**
|
|
1756
|
-
*
|
|
1757
|
-
*
|
|
1736
|
+
* Who has to settle an action tool call, and until when. Metadata only: the call itself is still
|
|
1737
|
+
* settled through the tool-call approve/reject routes, by its `toolCallId`.
|
|
1758
1738
|
*/
|
|
1759
|
-
|
|
1760
|
-
/** The
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1739
|
+
interface AgentApprovalRequest {
|
|
1740
|
+
/** The tool call awaiting the decision — the `id` of a call already announced on this stream. */
|
|
1741
|
+
id: string;
|
|
1742
|
+
/**
|
|
1743
|
+
* Who may decide. Open vocabulary the host defines — `'requester'` (the person chatting),
|
|
1744
|
+
* `'admin'`, a role name, a team. A client uses it to say "waiting on an admin" instead of
|
|
1745
|
+
* offering buttons the viewer cannot use.
|
|
1746
|
+
*/
|
|
1764
1747
|
approver: string;
|
|
1765
|
-
/** ISO-8601 instant the request lapses
|
|
1748
|
+
/** ISO-8601 instant after which the request lapses. Absent → it never expires. */
|
|
1766
1749
|
expiresAt?: string;
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1750
|
+
/** Why this call needs a person, in words for that person. */
|
|
1751
|
+
reason?: string;
|
|
1752
|
+
}
|
|
1753
|
+
/**
|
|
1754
|
+
* How an approval settled — the other half of {@link AgentApprovalRequest}, under the same `id`.
|
|
1755
|
+
* Metadata again: the call's own outcome still arrives as `tool-output` (approved and ran),
|
|
1756
|
+
* `tool-output-error` (approved and failed) or `tool-output-denied` (rejected or expired).
|
|
1757
|
+
*/
|
|
1758
|
+
interface AgentApprovalSettlement {
|
|
1759
|
+
id: string;
|
|
1760
|
+
status: 'approved' | 'rejected' | 'expired';
|
|
1761
|
+
/** Repeated from the request, for a call approved without one being streamed (a remembered approval). */
|
|
1762
|
+
approver?: string;
|
|
1763
|
+
/** Opaque ref of who decided. Absent on an expiry. */
|
|
1771
1764
|
decidedBy?: string;
|
|
1772
|
-
/** The surface the decision came through
|
|
1765
|
+
/** The surface the decision came through: `'web'`, `'slack'`, `'remembered'`, … */
|
|
1773
1766
|
decidedVia?: string;
|
|
1767
|
+
/** The approval also covers later calls of this tool in this thread. */
|
|
1768
|
+
remember?: boolean;
|
|
1774
1769
|
/** What the person said when declining. */
|
|
1775
|
-
reason?: string;
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
/**
|
|
1780
|
-
* Messages sent while a turn was running, waiting to run after it, and whether the queue is
|
|
1781
|
-
* draining. Present when the store supports a queue (`ChatQueueStore`); the REST read-model
|
|
1782
|
-
* omits it otherwise.
|
|
1783
|
-
*/
|
|
1784
|
-
queue?: ChatQueueState;
|
|
1770
|
+
reason?: string;
|
|
1771
|
+
}
|
|
1772
|
+
type AgentStreamEvent = {
|
|
1773
|
+
kind: 'step-start';
|
|
1785
1774
|
}
|
|
1786
|
-
type ToolCallStatus = 'auto_executed' | 'pending_approval' | 'executed' | 'rejected' | 'failed'
|
|
1787
|
-
/** An approval request lapsed before anyone decided; the tool never ran. */
|
|
1788
|
-
| 'expired';
|
|
1789
1775
|
/**
|
|
1790
|
-
*
|
|
1791
|
-
*
|
|
1776
|
+
* Closes the step opened by the matching `step-start`. Carries the model call's token usage and
|
|
1777
|
+
* `costUsd` (an estimate from the bound pricing store, or `null` when unpriced/unbound — never a
|
|
1778
|
+
* fabricated `0`) so a live client can render running cost without waiting for a thread re-fetch.
|
|
1792
1779
|
*/
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
messages: ModelMessage[];
|
|
1798
|
-
/** The turn's actor — the handler re-derives tool definitions from it (definitionsFor). */
|
|
1799
|
-
actor: Actor;
|
|
1780
|
+
| {
|
|
1781
|
+
kind: 'step-finish';
|
|
1782
|
+
usage?: MessageUsage;
|
|
1783
|
+
costUsd?: number | null;
|
|
1800
1784
|
/**
|
|
1801
|
-
*
|
|
1802
|
-
* so
|
|
1785
|
+
* How long the model spent thinking in this step, in ms — the same number persisted as
|
|
1786
|
+
* `StoredMessage.reasoningMs`, so a live thread and a reloaded one read the same duration.
|
|
1787
|
+
* Absent when the step had no reasoning.
|
|
1803
1788
|
*/
|
|
1804
|
-
|
|
1789
|
+
reasoningMs?: number;
|
|
1805
1790
|
/**
|
|
1806
|
-
*
|
|
1807
|
-
*
|
|
1808
|
-
*
|
|
1809
|
-
* interpose on, so the instruction has to ride the envelope. Absent → stream live, as before.
|
|
1791
|
+
* The model the step ran on: the one the provider reported, else the configured `modelId`.
|
|
1792
|
+
* What a per-model usage report keys on (the AG-UI producer's `RUN_FINISHED.usage`). Absent
|
|
1793
|
+
* when neither is known; a reader that does not know the field ignores it.
|
|
1810
1794
|
*/
|
|
1811
|
-
bufferOutput?: boolean;
|
|
1812
|
-
/** The turn's selected model ({@link AgentRunInput.model}), for the worker's provider call. */
|
|
1813
1795
|
model?: string;
|
|
1796
|
+
} | {
|
|
1797
|
+
kind: 'text';
|
|
1798
|
+
text: string;
|
|
1799
|
+
} | {
|
|
1800
|
+
kind: 'reasoning';
|
|
1801
|
+
text: string;
|
|
1814
1802
|
}
|
|
1815
1803
|
/**
|
|
1816
|
-
*
|
|
1817
|
-
*
|
|
1804
|
+
* `toolKind` collapses `ToolKind`'s `'agent'` into `'read'` — delegation tools auto-execute like a read tool.
|
|
1805
|
+
*
|
|
1806
|
+
* `parentId` nests this call under another call on the same stream: the inner calls a code-mode
|
|
1807
|
+
* `execute` makes, the tools a delegated sub-agent runs. The parent must be announced first. A
|
|
1808
|
+
* call's parent is fixed by its first frame that names one; later frames may omit it.
|
|
1818
1809
|
*/
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1810
|
+
| {
|
|
1811
|
+
kind: 'tool-input-start';
|
|
1812
|
+
id: string;
|
|
1813
|
+
name: string;
|
|
1814
|
+
toolKind: 'read' | 'action';
|
|
1815
|
+
parentId?: string;
|
|
1816
|
+
} | {
|
|
1817
|
+
kind: 'tool-input-delta';
|
|
1818
|
+
id: string;
|
|
1819
|
+
delta: string;
|
|
1820
|
+
} | {
|
|
1821
|
+
kind: 'tool-input-available';
|
|
1822
|
+
id: string;
|
|
1823
|
+
name: string;
|
|
1830
1824
|
input: unknown;
|
|
1831
|
-
|
|
1832
|
-
/**
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
transientRetry: ToolTransientRetryNumbers | false;
|
|
1843
|
-
/**
|
|
1844
|
-
* The dispatching loop reads the components the tool pushed (`ctx.emitUi`) off the step's result.
|
|
1845
|
-
* A handler that sees it returns {@link wrapToolStepOutput}'s envelope when the tool pushed any —
|
|
1846
|
-
* and ONLY then, so a loop that predates this (and never sets it) always gets the bare output.
|
|
1847
|
-
*/
|
|
1848
|
-
collectUi?: boolean;
|
|
1825
|
+
toolKind: 'read' | 'action';
|
|
1826
|
+
/** Same as on `tool-input-start`, for a runner that announces a call without streaming its input. */
|
|
1827
|
+
parentId?: string;
|
|
1828
|
+
} | {
|
|
1829
|
+
kind: 'tool-output';
|
|
1830
|
+
id: string;
|
|
1831
|
+
output: unknown;
|
|
1832
|
+
} | {
|
|
1833
|
+
kind: 'tool-output-error';
|
|
1834
|
+
id: string;
|
|
1835
|
+
error: string;
|
|
1849
1836
|
}
|
|
1850
1837
|
/**
|
|
1851
|
-
*
|
|
1852
|
-
*
|
|
1838
|
+
* A person was asked to approve an action tool and declined it. Its own frame, NOT
|
|
1839
|
+
* `tool-output-error`: a refusal is a decision with a known outcome — nothing ran — while a
|
|
1840
|
+
* failure is an outcome nobody chose and whose effects are unknown. Rendering them the same way
|
|
1841
|
+
* tells an operator their own "no" was a malfunction. Maps onto the SDK's `output-denied` tool
|
|
1842
|
+
* part state, which a client reads without knowing any tool's name.
|
|
1853
1843
|
*/
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
enabled: boolean;
|
|
1859
|
-
};
|
|
1860
|
-
/** Sends are refused with `429` once `GET <base>/quota` reports `blocked`. */
|
|
1861
|
-
quota: {
|
|
1862
|
-
enforced: boolean;
|
|
1863
|
-
};
|
|
1864
|
-
/** No `actorResolver`: every browser is its own anonymous actor. */
|
|
1865
|
-
identity: {
|
|
1866
|
-
anonymous: boolean;
|
|
1867
|
-
};
|
|
1868
|
-
}
|
|
1869
|
-
/** The attachment rules in force — what the upload route enforces. */
|
|
1870
|
-
interface AgentAttachmentConfig {
|
|
1871
|
-
/** A staging store is bound, so uploads work at all. */
|
|
1872
|
-
enabled: boolean;
|
|
1873
|
-
/** How a client uploads (`null` when `enabled` is false). */
|
|
1874
|
-
upload: 'multipart' | 'resumable' | null;
|
|
1875
|
-
maxBytes: number;
|
|
1876
|
-
allowedContentTypes: readonly string[];
|
|
1877
|
-
/** How many attachments one message may name. */
|
|
1878
|
-
maxPerMessage: number;
|
|
1844
|
+
| {
|
|
1845
|
+
kind: 'tool-output-denied';
|
|
1846
|
+
id: string;
|
|
1847
|
+
reason?: string;
|
|
1879
1848
|
}
|
|
1880
|
-
|
|
1881
1849
|
/**
|
|
1882
|
-
*
|
|
1883
|
-
*
|
|
1884
|
-
*
|
|
1850
|
+
* The run has put a question set to the user and is parked until someone answers it (or skips).
|
|
1851
|
+
* Written by the LOOP for both elicitation surfaces — the configured intake and the model's `ask`
|
|
1852
|
+
* tool — so a client renders one form either way rather than learning to recognise a tool name.
|
|
1853
|
+
* The matching `tool-output` frame, under the same `id`, carries the settled answers.
|
|
1885
1854
|
*/
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
requestId: string;
|
|
1891
|
-
/** The name of the agent running this turn — provenance a tool can scope on (e.g. capability sets). */
|
|
1892
|
-
agentName?: string;
|
|
1893
|
-
pageContext?: PageContext;
|
|
1894
|
-
/** Optional host handle (e.g. an ORM EntityManager) the app threads through options. */
|
|
1895
|
-
host?: unknown;
|
|
1896
|
-
/**
|
|
1897
|
-
* Push a component into the assistant message: streamed live as a `ui` frame and persisted on
|
|
1898
|
-
* the message, so a reload shows it where the live stream did. Resolves to the component's id.
|
|
1899
|
-
*
|
|
1900
|
-
* `id` defaults to `<toolCallId>:ui:<n>` (the n-th push without an `id` in this invocation), so a retried or
|
|
1901
|
-
* re-executed call REPLACES what it pushed before instead of adding a second copy; pass your own
|
|
1902
|
-
* `id` to update one component across pushes (streaming rows into a table). `props` must be
|
|
1903
|
-
* JSON; it is snapshotted when pushed.
|
|
1904
|
-
*
|
|
1905
|
-
* Replay-safe under the durable runner: the pushed components ride the tool step's journaled
|
|
1906
|
-
* result, so a replay neither streams nor persists them again.
|
|
1907
|
-
*
|
|
1908
|
-
* Always present. On a surface with no conversation to push into (the MCP server, a direct
|
|
1909
|
-
* `registry.invoke` without one) it is a no-op that still resolves to an id, so a tool calls
|
|
1910
|
-
* `ctx.emitUi(…)` unconditionally.
|
|
1911
|
-
*/
|
|
1912
|
-
emitUi(component: string, props: Record<string, unknown>, options?: {
|
|
1913
|
-
id?: string;
|
|
1914
|
-
version?: number;
|
|
1915
|
-
}): Promise<{
|
|
1916
|
-
id: string;
|
|
1917
|
-
}>;
|
|
1855
|
+
| {
|
|
1856
|
+
kind: 'elicitation';
|
|
1857
|
+
id: string;
|
|
1858
|
+
request: ElicitationRequest;
|
|
1918
1859
|
}
|
|
1919
|
-
/**
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
* depend on who is asking (a per-tenant component catalog, a per-plan list of options). Called
|
|
1952
|
-
* when the turn's tool list is built, after every gate has passed; whatever it returns replaces
|
|
1953
|
-
* the registered spec's `description` / `inputSchema` in the definition the model sees. Omit, or
|
|
1954
|
-
* return `undefined`, to use the registered spec as is.
|
|
1955
|
-
*
|
|
1956
|
-
* It shapes what the model is SHOWN only: the registry still validates a call against the
|
|
1957
|
-
* registered `inputSchema`, so a tool whose accepted input varies per turn registers a permissive
|
|
1958
|
-
* schema and validates in `execute`.
|
|
1959
|
-
*/
|
|
1960
|
-
describe?(scope: ToolDescribeScope): ToolDescription | undefined | Promise<ToolDescription | undefined>;
|
|
1860
|
+
/**
|
|
1861
|
+
* An action tool call (already announced by `tool-input-start`/`tool-input-available` under the
|
|
1862
|
+
* same `id`) is parked on a person. Optional: a client still treats an `action` call stuck at
|
|
1863
|
+
* its input as pending — this frame adds WHO has to decide and UNTIL WHEN, and moves the call
|
|
1864
|
+
* into the AI SDK's native `approval-requested` state.
|
|
1865
|
+
*/
|
|
1866
|
+
| ({
|
|
1867
|
+
kind: 'approval-requested';
|
|
1868
|
+
} & AgentApprovalRequest)
|
|
1869
|
+
/**
|
|
1870
|
+
* A parked action call was decided (or lapsed). Optional, like `approval-requested`: it adds WHO
|
|
1871
|
+
* decided, THROUGH WHAT and whether the approval is REMEMBERED; the outcome itself rides the
|
|
1872
|
+
* call's own output frame. See {@link AgentApprovalSettlement}.
|
|
1873
|
+
*/
|
|
1874
|
+
| ({
|
|
1875
|
+
kind: 'approval-settled';
|
|
1876
|
+
} & AgentApprovalSettlement)
|
|
1877
|
+
/**
|
|
1878
|
+
* Server-pushed generative UI, positioned in the message where it arrives. Not tied to a tool
|
|
1879
|
+
* call. See {@link AgentUiComponent}.
|
|
1880
|
+
*/
|
|
1881
|
+
| ({
|
|
1882
|
+
kind: 'ui';
|
|
1883
|
+
} & AgentUiComponent)
|
|
1884
|
+
/**
|
|
1885
|
+
* The thread's title was set or changed while this run streamed (typically derived from the
|
|
1886
|
+
* first exchange). Thread-level, not message content: a client updates its header/sidebar and
|
|
1887
|
+
* does not render it in the transcript.
|
|
1888
|
+
*/
|
|
1889
|
+
| {
|
|
1890
|
+
kind: 'title';
|
|
1891
|
+
title: string;
|
|
1961
1892
|
}
|
|
1962
|
-
/**
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1893
|
+
/**
|
|
1894
|
+
* Host-defined facts about the message being streamed (the model that answered, how long it took,
|
|
1895
|
+
* the error it ended with), merged into the client message's `metadata`. The persisted
|
|
1896
|
+
* counterpart is `StoredMessage.metadata`, so a reload reads the same values. The library's own
|
|
1897
|
+
* loop never writes it; a runner that is not this library's loop uses it for what its store keeps
|
|
1898
|
+
* per message.
|
|
1899
|
+
*/
|
|
1900
|
+
| {
|
|
1901
|
+
kind: 'message-metadata';
|
|
1902
|
+
metadata: Record<string, unknown>;
|
|
1968
1903
|
}
|
|
1969
|
-
/**
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1904
|
+
/**
|
|
1905
|
+
* Someone stopped this run. The stream's LAST frame, written by the runner that settled the
|
|
1906
|
+
* cancel, immediately before a normal `end()` — never a `fail()`, because a cancel is not an
|
|
1907
|
+
* error and a client that retries on a failed stream must not retry this.
|
|
1908
|
+
*
|
|
1909
|
+
* A run that simply ends wrote everything it had; one that ends after this frame did not, and the
|
|
1910
|
+
* difference is the whole point: without it a reader cannot tell a truncated answer from a
|
|
1911
|
+
* complete one. Consumers that predate the frame ignore it and see the `end()` they always saw.
|
|
1912
|
+
*/
|
|
1913
|
+
| {
|
|
1914
|
+
kind: 'cancelled';
|
|
1973
1915
|
}
|
|
1916
|
+
/**
|
|
1917
|
+
* The thread's message queue changed — a snapshot of the whole queue, never a delta, so a client
|
|
1918
|
+
* that missed one frame is corrected by the next. Written into the stream of the run that is
|
|
1919
|
+
* holding the thread: when someone queues, edits, reorders or removes a waiting message, and, just
|
|
1920
|
+
* before this run's own terminal frame, with what happens next — `started` names the queued
|
|
1921
|
+
* message that became the next turn and that turn's run id (attach to it with
|
|
1922
|
+
* `GET <base>/chat/:runId/stream`), `queue.paused` says why nothing starts.
|
|
1923
|
+
*/
|
|
1924
|
+
| {
|
|
1925
|
+
kind: 'queue';
|
|
1926
|
+
queue: ChatQueueState;
|
|
1927
|
+
started?: {
|
|
1928
|
+
messageId: string;
|
|
1929
|
+
runId: string;
|
|
1930
|
+
};
|
|
1931
|
+
};
|
|
1932
|
+
/** Encode one event as an NDJSON line (`{...}\n`) for {@link SinkWriter.write}. */
|
|
1933
|
+
declare function encodeStreamEvent(event: AgentStreamEvent): Uint8Array;
|
|
1934
|
+
/**
|
|
1935
|
+
* Read one NDJSON line back, or `null` when the line is not a stream event at all.
|
|
1936
|
+
*
|
|
1937
|
+
* `null` covers a genuinely opaque chunk, not just malformed JSON: the sink is a byte channel, so a
|
|
1938
|
+
* model provider is free to write anything into it and some write bare text. A caller that has to
|
|
1939
|
+
* CLASSIFY a chunk — the output gate, which may only forward what it can prove is not the answer —
|
|
1940
|
+
* treats an unreadable frame as unclassifiable rather than guessing.
|
|
1941
|
+
*/
|
|
1942
|
+
declare function decodeStreamEvent(line: string): AgentStreamEvent | null;
|
|
1943
|
+
/**
|
|
1944
|
+
* The `code` of a failed run's `event: error` frame — what a client branches on, and translates.
|
|
1945
|
+
* `quota_exceeded`, `output_rejected` and `structured_output_invalid` are outcomes the library words
|
|
1946
|
+
* itself (their `message` is safe to show as it is); the rest are crashes, whose `message` is a
|
|
1947
|
+
* generic sentence in production. Open-ended on purpose: a host's own runner may send other codes.
|
|
1948
|
+
*/
|
|
1949
|
+
type AgentStreamErrorCode = 'quota_exceeded' | 'output_rejected' | 'structured_output_invalid'
|
|
1950
|
+
/** The durable runtime refused a checkpoint position: the run's journal and its code disagree. */
|
|
1951
|
+
| 'replay_diverged'
|
|
1952
|
+
/** A model call ended without producing anything. */
|
|
1953
|
+
| 'model_no_output' | 'run_failed';
|
|
1974
1954
|
|
|
1975
|
-
export {
|
|
1955
|
+
export { type AgentApprovalSettlement as $, type AgentStreamEvent as A, type QueuedMessage as B, type ChatQueueStore as C, type DetachedDelivery as D, type ElicitationRequest as E, type QueuedMessagePatch as F, type QueuePause as G, type HumanReply as H, type AppendMessageInput as I, type ToolResult as J, type MessageFeedback as K, type LlmStepEnvelope as L, type ModelMessage as M, type RecordToolCallInput as N, type ToolCallOutcome as O, type PageContext as P, type QuotaState as Q, type RecordRunStartInput as R, type StoredMessage as S, type ToolSpec as T, type UpdateThreadInput as U, type UpdateToolCallInput as V, type RecordUsageInput as W, ALL_AGENTS as X, ASK_TOOL_DESCRIPTION as Y, ASK_TOOL_NAME as Z, type AgentApprovalRequest as _, type Actor as a, settleDanglingToolCalls as a$, type AgentAttachmentConfig as a0, type AgentCatalogEntry as a1, type AgentClientConfig as a2, type AgentHistoryWindow as a3, type AgentStreamErrorCode as a4, type AskToolInput as a5, type ChatQueueState as a6, DEFAULT_INTAKE_PREAMBLE as a7, DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS as a8, DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS as a9, type ToolConfirmation as aA, type ToolPresentationTone as aB, type ToolResultField as aC, type ToolResultView as aD, type ToolStepCtx as aE, type ToolTransientRetryNumbers as aF, type ToolTransientRetryOptions as aG, UNFINISHED_TOOL_CALL as aH, type UsagePurpose as aI, askInputSchema as aJ, askToolDefinition as aK, danglingToolCallIds as aL, decodeStreamEvent as aM, encodeStreamEvent as aN, invokeWithTransientRetry as aO, isChatQueueStore as aP, isTransientToolError as aQ, isTypedQuestion as aR, normalizeElicitationReply as aS, questionOptions as aT, queuedMessageView as aU, readElicitationInput as aV, readElicitationQuestions as aW, releaseThreadRun as aX, renderElicitationAnswers as aY, resolveElicitation as aZ, resolveToolTransientRetryNumbers as a_, ELICITATION_INPUT_TYPES as aa, type ElicitationInput as ab, type ElicitationInputType as ac, type ElicitationOption as ad, type ElicitationOutcome as ae, type ElicitationQuestion as af, type ElicitationReply as ag, type ElicitationResult as ah, type HistoryPolicyContext as ai, type HistorySelection as aj, type HistorySummary as ak, type InvokeWithTransientRetryOptions as al, MAX_ASK_QUESTIONS as am, type MessageFeedbackValue as an, type MessageRole as ao, type PromptContext as ap, type QueuePauseReason as aq, type QueuedMessageView as ar, type QuotaView as as, RUN_ENDED_BEFORE_TOOL_CALL as at, type RecordRunEndInput as au, type ThreadTurnPage as av, type ThreadTurnQuery as aw, type ThreadTurnReader as ax, type ToolCallApprovalStatus as ay, type ToolCatalogEntry as az, type ToolPresentation as b, settleElicitation as b0, validateElicitationAnswer as b1, validateElicitationValue as b2, type ToolDefinition as c, type ToolCallRequest as d, type MessageUsage as e, type AgentUiComponent as f, type AgentRunInput as g, type MessageAttachment as h, type ToolKind as i, type ToolCallStatus as j, type ToolCallApproval as k, type HistoryPolicy as l, type AgentDefinition as m, type AgentStore as n, type AgentDelegation as o, type PromptBuilder as p, type PromptContributor as q, type ToolTransientRetrySetting as r, type AgentIntake as s, type Decision as t, type ToolStepEnvelope as u, type CreateThreadInput as v, type ThreadSummary as w, type ThreadDetail as x, type ToolCallApprovalState as y, type EnqueueMessageInput as z };
|