@dudousxd/nestjs-agent 1.27.0 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,14 +1,17 @@
1
1
  import { DynamicModule, Type, OnApplicationBootstrap, OnModuleInit, ConflictException, OnModuleDestroy } from '@nestjs/common';
2
- import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, A as AgentDepsFactory, C as ChatQueueService, d as AgentProtocolAdapter, e as AgentSkillsOptions } from './chat-queue.service-Cu2nJpXz.cjs';
3
- export { f as AGENT_TOOL_BRAND, g as AgentAttachmentsOptions, h as AgentDeps, i as AgentEngine, j as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, L as LedgerQuotaProvider, Q as QueuePlan, k as QueueSettleOutcome, l as QueuedTurn, m as QueuedTurnStarter, n as QuotaLimits, o as QuotaWindowLimits, p as defineTool, q as delegateToolName, r as provideAgentTool, s as provideAgentTools, u as utcDay } from './chat-queue.service-Cu2nJpXz.cjs';
2
+ import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, d as AgentProtocolAdapter, A as AgentDepsFactory, e as AgentSkillsOptions, C as ChatQueueService } from './chat-queue.service-Bk7DCKGV.cjs';
3
+ export { f as AGENT_TOOL_BRAND, g as AgentAttachmentsOptions, h as AgentDeps, i as AgentEngine, j as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, L as LedgerQuotaProvider, Q as QueuePlan, k as QueueSettleOutcome, l as QueuedTurn, m as QueuedTurnStarter, n as QuotaLimits, o as QuotaWindowLimits, p as defineTool, q as delegateToolName, r as provideAgentTool, s as provideAgentTools, u as utcDay } from './chat-queue.service-Bk7DCKGV.cjs';
4
4
  import * as _dudousxd_nestjs_agent_core from '@dudousxd/nestjs-agent-core';
5
- import { AgentAttachmentConfig, AttachmentStagingStore, AgentStore, Actor, ListActionProposals, ActionProposalView, ActionProposalMutationView, TextActionProposalVocabulary, AgentRunner, ModelCatalog, QuotaProvider, ActionApprovalMode, MessageAttachment, QuotaReport, ModelCatalogView, AgentHostContext, AttachmentRef, PageContext, ChatQueueState, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, MessageFeedbackValue, MessageFeedback, StagedAttachment, ActorResolver, AgentApprovalPort, AgentClientConfig, ToolSpec, ToolPresentation, AgentHistoryWindow, AgentIntake, Persona, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunInput, AgentRunStartOptions, TokenStreamSink, SinkWriter, ActionProposal } from '@dudousxd/nestjs-agent-core';
5
+ import { ActorResolver, AttachmentStagingStore, AgentApprovalPort, Actor, AgentStore, StagedAttachment, MessageAttachment, ModelCatalog, AgentClientConfig, ToolSpec, ToolPresentation, AgentHistoryWindow, AgentIntake, Persona, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, MessageFeedbackValue, MessageFeedback, ModelCatalogView, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunner, AgentRunInput, AgentRunStartOptions, HumanReply, ChatQueueState, TokenStreamSink, SinkWriter, ActionProposal } from '@dudousxd/nestjs-agent-core';
6
6
  export * from '@dudousxd/nestjs-agent-core';
7
7
  export { FunctionalToolDefinition } from '@dudousxd/nestjs-agent-core';
8
+ import { A2uiOptions } from '@dudousxd/nestjs-agent-core/a2ui';
8
9
  import { Request, Response } from 'express';
9
- import { UiCapabilities } from '@dudousxd/nestjs-agent-core/genui';
10
+ import { A as AgentService, a as ActionProposalService } from './index-C-Zo8rTL.cjs';
11
+ export { b as A2uiAdapterOptions, c as A2uiRunHandler, d as AttachmentLimits, C as ChatParams, e as ChatSendMode, f as ChatSendResult, D as DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, g as DEFAULT_MAX_ATTACHMENT_BYTES, M as MAX_ATTACHMENTS_PER_MESSAGE, N as NO_USER_MESSAGE_CODE, Q as QueuedSend, T as ThreadDefaultAgentReader, h as ThreadModelReader, i as a2uiAdapter, j as attachmentLimits } from './index-C-Zo8rTL.cjs';
10
12
  import { StandardSchemaV1 } from '@standard-schema/spec';
11
13
  import { DiscoveryService, MetadataScanner } from '@nestjs/core';
14
+ import '@dudousxd/nestjs-agent-core/genui';
12
15
  import './genui/index.cjs';
13
16
 
14
17
  declare class AgentModule {
@@ -16,562 +19,6 @@ declare class AgentModule {
16
19
  static forRootAsync(options: AgentModuleAsyncOptions): DynamicModule;
17
20
  }
18
21
 
19
- /** Default per-file size cap (20 MiB). */
20
- declare const DEFAULT_MAX_ATTACHMENT_BYTES: number;
21
- /** Default allowlist: what multimodal model providers commonly accept as native image/file parts. */
22
- declare const DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES: readonly string[];
23
- /** How many attachments one message may name. */
24
- declare const MAX_ATTACHMENTS_PER_MESSAGE = 10;
25
- /** The attachment rules in force — `AgentAttachmentConfig`, as `GET <base>/config` serves it. */
26
- type AttachmentLimits = AgentAttachmentConfig;
27
- /**
28
- * One source for attachment limits: the staging store's own `describe()` when it declares them
29
- * (`AgentMediaAttachmentsModule` does), else `AgentModule`'s `attachments` option, else the defaults.
30
- */
31
- declare function attachmentLimits(options: Pick<AgentModuleOptions, 'attachments'> | undefined, staging: AttachmentStagingStore | undefined): AttachmentLimits;
32
-
33
- declare class ActionProposalService {
34
- private readonly store;
35
- private readonly options;
36
- constructor(store: AgentStore, options: Pick<AgentModuleOptions, 'approvalPolicy' | 'actionProposalText'>);
37
- /** The words a text decision is made of, and what the agent answers — English unless configured. */
38
- private readonly vocabulary;
39
- private readonly replies;
40
- private hasCapability;
41
- private capability;
42
- private scope;
43
- private mayDecide;
44
- /**
45
- * One page of a thread's proposals this actor may see. A store without the proposal capability
46
- * can only run blocking approvals, so a thread there has no proposals: an empty, final page rather
47
- * than `501` — a client that lists by default (React's `useAgentChat`) needs no flag. Deciding one
48
- * still answers `501`.
49
- */
50
- listPage(threadId: string, actor: Actor, query?: Pick<ListActionProposals, 'after'>): Promise<{
51
- items: ActionProposalView[];
52
- next?: NonNullable<ListActionProposals['after']>;
53
- }>;
54
- list(threadId: string, actor: Actor): Promise<ActionProposalView[]>;
55
- decide(threadId: string, proposalId: string, actor: Actor, command: {
56
- decision: 'approved' | 'rejected';
57
- remember?: boolean;
58
- reason?: string;
59
- via?: string;
60
- }): Promise<ActionProposalMutationView>;
61
- handleTextDecision(threadId: string, actor: Actor, text: string): Promise<{
62
- status: "unmatched";
63
- } | {
64
- threadId: string;
65
- proposalDecision: ActionProposalMutationView;
66
- text: string;
67
- } | {
68
- threadId: string;
69
- proposalDecision: {
70
- status: "ambiguous";
71
- proposalIds: string[];
72
- };
73
- text: string;
74
- }>;
75
- private textReceipt;
76
- /** The configured words a text decision is made of (`actionProposalText.vocabulary`). */
77
- textVocabulary(): TextActionProposalVocabulary;
78
- /** The configured reply to a decision the store answered with `result`. */
79
- reply(result: Pick<ActionProposalMutationView, 'status' | 'proposal'>, decision: 'approved' | 'rejected'): string;
80
- }
81
-
82
- /**
83
- * What a send does when its thread already has a turn running:
84
- * - `'auto'` (default) — run now when the thread is idle, else wait in the thread's queue.
85
- * - `'queue'` — always wait in the queue, behind whatever is already waiting (it still starts at
86
- * once when nothing is running and nothing is ahead of it).
87
- * - `'interrupt'` — cancel the running turn and run this one next, ahead of the queue.
88
- */
89
- type ChatSendMode = 'auto' | 'queue' | 'interrupt';
90
- /** A send that is waiting in its thread's queue instead of running. */
91
- interface QueuedSend {
92
- threadId: string;
93
- queued: true;
94
- /** The queued message's id — also the run id it will start under. */
95
- messageId: string;
96
- /** 0-based place in the queue at the time it was queued (`0` → next). */
97
- position: number;
98
- queue: ChatQueueState;
99
- /** The turn it started under, when it started straight away (an idle thread). */
100
- runId?: string;
101
- /** The run an interrupt cancelled to make room for it. */
102
- interrupting?: string;
103
- }
104
- /** What {@link AgentService.send} did: started a turn, or queued the message. */
105
- type ChatSendResult = {
106
- runId: string;
107
- threadId: string;
108
- queued?: undefined;
109
- } | QueuedSend | {
110
- threadId: string;
111
- proposalDecision: unknown;
112
- text: string;
113
- queued?: undefined;
114
- };
115
- /** The code of the `400` a send with nothing to answer gets (also AG-UI's, for the same case). */
116
- declare const NO_USER_MESSAGE_CODE = "no_user_message";
117
- interface ChatParams {
118
- uiCapabilities?: UiCapabilities;
119
- actor: Actor;
120
- message: string;
121
- /** The host's own facts about this send — see `AgentRunInput.hostContext`. */
122
- hostContext?: AgentHostContext;
123
- /**
124
- * The HOST already decided this actor may write this thread — a chat-app thread several people
125
- * share, where anyone in it may continue the conversation. Skips the thread-ownership check of a
126
- * send; never set it from a request's own claim.
127
- */
128
- authorized?: boolean;
129
- threadId?: string;
130
- agentName?: string;
131
- /**
132
- * Run this turn under one of the agent's personas (`@Agent({ personas })`), and pin it on the
133
- * thread so later sends that name none keep it. Refused (400 `persona_not_found`) unless the agent
134
- * declares it. Omitted → the thread's pinned persona, else the agent's `defaultPersona`, else none.
135
- */
136
- personaId?: string;
137
- /**
138
- * Files attached to this message (image/PDF) for a vision-capable model, named by the `mediaId`
139
- * the upload returned. Only the id is read: the url, content type and name are resolved from the
140
- * bound {@link AttachmentStagingStore}, so nothing a caller says decides what the model fetches.
141
- */
142
- attachments?: AttachmentRef[];
143
- pageContext?: PageContext;
144
- /** Re-run the last exchange instead of adding a new message. Requires an existing `threadId`. */
145
- regenerate?: boolean;
146
- /**
147
- * Run this turn on a catalog model instead of the thread's pinned one (or the provider default).
148
- * Refused (400) unless the bound `ModelCatalog` lists it as available to this actor and agent.
149
- */
150
- model?: string;
151
- /**
152
- * When creating a thread (no `threadId`), start it transient — a scratch conversation hidden from
153
- * the thread list until the caller promotes it. Ignored when `threadId` is set.
154
- */
155
- transient?: boolean;
156
- /** What to do when the thread already has a turn running — see {@link ChatSendMode}. */
157
- mode?: ChatSendMode;
158
- /**
159
- * When creating a thread (no `threadId`), ask the store to create it under this id — the id an
160
- * AG-UI consumer named the conversation with. A store may ignore it (`CreateThreadInput.id`); the
161
- * thread's real id is always the one on the result.
162
- */
163
- newThreadId?: string;
164
- }
165
- /**
166
- * A store that can answer a thread's default agent on its own, without materializing the thread.
167
- *
168
- * Probed STRUCTURALLY rather than declared on `AgentStore`: the projection is an optimization a
169
- * store either offers or doesn't, and a store that predates it still answers correctly through
170
- * `getThread`. Every adapter in this repo implements it.
171
- */
172
- interface ThreadDefaultAgentReader {
173
- defaultAgentForThread(threadId: string): Promise<string | null>;
174
- }
175
- /** Like {@link ThreadDefaultAgentReader}, for the thread's pinned model. */
176
- interface ThreadModelReader {
177
- modelForThread(threadId: string): Promise<string | null>;
178
- }
179
- /** The orchestration facade the controllers call. */
180
- declare class AgentService {
181
- private readonly runner;
182
- private readonly store;
183
- private readonly deps;
184
- private readonly staging?;
185
- private readonly models?;
186
- private readonly quotaProvider?;
187
- private readonly options?;
188
- private readonly queue?;
189
- private readonly proposals?;
190
- constructor(runner: AgentRunner, store: AgentStore, deps: AgentDepsFactory, staging?: AttachmentStagingStore | undefined, models?: ModelCatalog | undefined, quotaProvider?: QuotaProvider | undefined, options?: Pick<AgentModuleOptions, "quota" | "actionApprovalMode" | "attachments"> | undefined, queue?: ChatQueueService | undefined, proposals?: ActionProposalService | undefined);
191
- /**
192
- * Decide one independent proposal through the scoped proposal service — what the native
193
- * `action-proposals/:id/approve|reject` routes call — and the reply to show for it. `404` when
194
- * proposals are not on, `403`/`404` when this actor may not decide it.
195
- */
196
- decideActionProposal(actor: Actor, threadId: string, proposalId: string, command: {
197
- decision: 'approved' | 'rejected';
198
- remember?: boolean;
199
- reason?: string;
200
- via: string;
201
- }): Promise<{
202
- proposalDecision: ActionProposalMutationView;
203
- text: string;
204
- }>;
205
- handleTextDecision(threadId: string, actor: Actor, text: string): Promise<{
206
- status: "unmatched";
207
- } | {
208
- threadId: string;
209
- proposalDecision: ActionProposalMutationView;
210
- text: string;
211
- } | {
212
- threadId: string;
213
- proposalDecision: {
214
- status: "ambiguous";
215
- proposalIds: string[];
216
- };
217
- text: string;
218
- }>;
219
- /** How `action` tools wait for their approval here — `AgentModule.forRoot({ actionApprovalMode })`. */
220
- actionApprovalMode(): ActionApprovalMode;
221
- /**
222
- * The words a text decision is made of (`actionProposalText.vocabulary`, English by default) — what
223
- * a surface without buttons tells the person to reply.
224
- */
225
- actionProposalVocabulary(): TextActionProposalVocabulary;
226
- /** The configured reply (`actionProposalText.replies`) to a decision the store answered with `result`. */
227
- actionProposalReply(result: Pick<ActionProposalMutationView, 'status' | 'proposal'>, decision: 'approved' | 'rejected'): string;
228
- /**
229
- * The proposals of a thread this actor may see — the thread's own actor, or one the policy lets
230
- * decide them; what `GET <base>/threads/:threadId/action-proposals` serves (its first page).
231
- * Without independent approvals (the default blocking mode) there are none, so the read answers an
232
- * empty list rather than refusing; deciding one still refuses.
233
- */
234
- listActionProposals(actor: Actor, threadId: string): Promise<ActionProposalView[]>;
235
- /**
236
- * The attachment rules in force — what `GET <base>/config` serves and `POST <base>/attachments`
237
- * enforces. `enabled: false` when no `AGENT_ATTACHMENT_STAGING` provider is bound.
238
- */
239
- attachmentLimits(): AttachmentLimits;
240
- /**
241
- * Stage a file for a later turn, held to the same limits as `POST <base>/attachments` — for a
242
- * surface that receives files itself (a messaging webhook). Refuses with `501` when attachments are
243
- * off, `415` for a type outside the allowlist and `413` past the size cap; send the returned
244
- * `mediaId` as `attachments: [{ mediaId }]`.
245
- */
246
- stageAttachment(actor: Actor, file: {
247
- data: Buffer;
248
- contentType: string;
249
- filename: string;
250
- }): Promise<MessageAttachment>;
251
- /** The queue service when the bound store can hold a queue, else `undefined`. */
252
- private queueing;
253
- /** The queue service, or a 501 naming what is missing. */
254
- private requireQueue;
255
- /**
256
- * The actor's budget across windows (`GET <base>/quota`) — the bound {@link QuotaProvider}'s
257
- * report. Without one (a service built outside the module), a day window from the ledger.
258
- */
259
- quotaReport(actor: Actor): Promise<QuotaReport>;
260
- /**
261
- * Refuse a turn the actor's budget no longer covers — only when the host configured one
262
- * (`quota`): the default report is informational.
263
- */
264
- private assertWithinQuota;
265
- /**
266
- * The models `actor` may pick for `agent` (`GET <base>/models`). An empty catalog when none is
267
- * bound, so a picker simply has nothing to offer.
268
- */
269
- listModels(actor: Actor, agent?: string): Promise<ModelCatalogView>;
270
- /**
271
- * `model` if the catalog offers it to this actor and agent right now, else a 400 naming why. The
272
- * one gate every model choice passes — a send's own, and a thread's pinned one at the moment it is
273
- * pinned and again at every turn that runs on it (availability can change in between).
274
- */
275
- private assertModelAllowed;
276
- /**
277
- * The model a turn runs on: the send's own (that turn only — it is never stored on the thread),
278
- * else the thread's pinned one, else none. An agent the catalog locks to one model runs on it
279
- * whatever was sent or pinned, and a send naming another model is refused.
280
- */
281
- private resolveModel;
282
- private threadModel;
283
- /**
284
- * Start a turn — for in-process callers that need a run id back. A send that would have to wait
285
- * (its thread already has a turn running) is refused with `409` instead; {@link send} is the form
286
- * that queues it.
287
- */
288
- chat(params: ChatParams): Promise<{
289
- runId: string;
290
- threadId: string;
291
- }>;
292
- /**
293
- * Send a message: start a turn, or — when its thread already has one running — queue the message
294
- * to run after it (see {@link ChatSendMode}). What `POST <base>/chat` calls.
295
- */
296
- send(claimed: ChatParams, sendOptions?: {
297
- textDecisions?: boolean;
298
- }): Promise<ChatSendResult>;
299
- /**
300
- * Start a turn the thread is already claimed for, under the claimed id. A runner that minted an
301
- * id of its own anyway gets the thread re-pointed at it; one that fails to start frees the thread.
302
- */
303
- private startClaimed;
304
- /** Put a send in its thread's queue, then drain it if the thread turned out to be free. */
305
- private enqueue;
306
- /** `GET <base>/threads/:id/queue` — the thread's waiting messages and whether it drains. */
307
- getQueue(actor: Actor, threadId: string): Promise<ChatQueueState>;
308
- /**
309
- * `PATCH <base>/queue/:messageId` — change a waiting message's text and/or attachments, and/or move
310
- * it to `position` in the queue. Answers the thread's queue.
311
- */
312
- updateQueuedMessage(actor: Actor, messageId: string, patch: {
313
- message?: string;
314
- attachments?: AttachmentRef[] | null;
315
- position?: number;
316
- }): Promise<ChatQueueState>;
317
- /** `DELETE <base>/queue/:messageId` — drop a waiting message. Answers the thread's queue. */
318
- removeQueuedMessage(actor: Actor, messageId: string): Promise<ChatQueueState>;
319
- /**
320
- * `POST <base>/queue/:messageId/interrupt` — run a message that is already waiting NOW: it moves
321
- * to the head of its queue marked as an interrupt, any pause is lifted, and the running turn is
322
- * cancelled (its settle starts this message — the same path a `mode: 'interrupt'` send takes).
323
- * With nothing running, the message starts at once.
324
- *
325
- * One request, and the message never leaves the queue: a client that removed it and sent it again
326
- * would lose it if the second call failed, and would run it twice if another tab's drain started
327
- * it in between. Answers the queue, plus `interrupting` (the run that was cancelled) or `runId`
328
- * (the run this message started).
329
- */
330
- interruptQueuedMessage(actor: Actor, messageId: string): Promise<ChatQueueState & {
331
- runId?: string;
332
- interrupting?: string;
333
- }>;
334
- /** `DELETE <base>/threads/:id/queue` — drop every waiting message (and any pause). */
335
- clearQueue(actor: Actor, threadId: string): Promise<ChatQueueState>;
336
- /**
337
- * `POST <base>/threads/:id/queue/resume` — lift a pause and start the head when nothing is
338
- * running. Answers the queue, and the run the head started under, if it did.
339
- */
340
- resumeQueue(actor: Actor, threadId: string): Promise<ChatQueueState & {
341
- runId?: string;
342
- }>;
343
- /** The thread a queued message waits on, once the caller is shown to own it. */
344
- private assertOwnsQueuedMessage;
345
- /**
346
- * Rebuild this turn's attachments from the staging store, keyed by the ids the caller claimed.
347
- * The model provider fetches {@link MessageAttachment.url} verbatim, so that url is only ever
348
- * allowed to come from the host's own store — a caller that could name it could point the server
349
- * at anything it can reach (cloud metadata, internal services) and read the response back out of
350
- * the model's answer.
351
- *
352
- * No store bound means no way to tell an id the actor owns from one they don't, so a turn simply
353
- * cannot carry attachments: 501 rather than trusting the request.
354
- */
355
- private resolveAttachments;
356
- /**
357
- * The run's live token stream, WITHOUT an ownership check — for in-process callers that are
358
- * already authorized (the MCP bridge, a host's own job code), the same split as
359
- * {@link signalToolCall} vs {@link approve}. Anything reachable from a request must go through
360
- * {@link subscribeAs} instead: a runId is the only thing standing between a caller and another
361
- * actor's answer, tool arguments and tool results.
362
- */
363
- subscribe(runId: string): AsyncIterable<Uint8Array>;
364
- /**
365
- * {@link subscribe}, gated the way {@link cancel} is: only the actor whose thread is currently
366
- * streaming this run may read it. A run that has already finished is reported missing rather than
367
- * replayed — the runner clears the thread's active stream as the run ends, and that field is what
368
- * ownership is derived from.
369
- */
370
- subscribeAs(actor: Actor, runId: string): Promise<AsyncIterable<Uint8Array>>;
371
- /**
372
- * Approve a parked action call as `actor`. Who may is the call's recorded approver's business
373
- * (see {@link assertMayDecide}); `remember` approves later calls of the same tool in the same
374
- * thread, and `via` names the surface the decision came through (`'web'`, `'slack'`, …) — both
375
- * persisted with the call.
376
- */
377
- approve(actor: Actor, toolCallId: string, opts?: {
378
- remember?: boolean;
379
- via?: string;
380
- }): Promise<void>;
381
- reject(actor: Actor, toolCallId: string, reason?: string, opts?: {
382
- via?: string;
383
- }): Promise<void>;
384
- /**
385
- * Answer a parked question set. An omitted question takes the request's own pre-picked default —
386
- * resolved by the LOOP against the request it already holds, so "the user just submitted" and
387
- * "the user picked exactly the defaults" persist as the same answers, and no client has to
388
- * re-send what it was shown.
389
- */
390
- answer(actor: Actor, toolCallId: string, answers?: Record<string, string[]>, opts?: {
391
- via?: string;
392
- }): Promise<void>;
393
- /**
394
- * Decline to answer and let the agent proceed on its own assumptions. NOT the same as confirming
395
- * them: the run records this as a rejection, so a reader auditing what the agent was told can
396
- * tell a choice the user made from one they refused to make.
397
- */
398
- skip(actor: Actor, toolCallId: string, opts?: {
399
- via?: string;
400
- }): Promise<void>;
401
- /**
402
- * The decision core behind approve/reject, WITHOUT an ownership check: resolves the run
403
- * currently awaiting `toolCallId` and signals it. `approve`/`reject` above call this after their
404
- * own `assertOwnsToolCall` gate; the console `AgentApprovalPort` adapter (bound by `AgentModule`)
405
- * calls it directly — a console caller is already authorized by the dashboard's own guards, and
406
- * re-deriving thread ownership here would reject a legitimate operator decision (an operator is
407
- * not the thread's own actor).
408
- */
409
- signalToolCall(toolCallId: string, reply: HumanReply): Promise<void>;
410
- /**
411
- * Refuse a decision addressed at a run that is over (`409 run_not_active`). The runtime would take
412
- * the signal and buffer it for a run that never comes back — the person's "yes" is accepted, the
413
- * card says so, and nothing runs. Settles the calls the dead run left awaiting a decision on the
414
- * way out, so the card stops asking. A runner that cannot say whether a run is alive is taken at
415
- * its word that it is, and so is one that fails to answer.
416
- */
417
- private assertRunWaiting;
418
- /** Who owns the thread, or `null` when there is no such (live) thread. */
419
- threadOwner(threadId: string): Promise<string | null>;
420
- /**
421
- * May `actor` continue the run whose stream is `runId` — the AG-UI resume? Only the actor whose
422
- * thread is streaming it (`403` otherwise), and only while it still is: a run that is over is
423
- * waiting on no one (`409 run_not_active`), whatever an old interrupt id says.
424
- */
425
- assertResumable(actor: Actor, runId: string): Promise<void>;
426
- /**
427
- * Everything {@link approve} / {@link reject} check before they signal — who may decide, and
428
- * that the request has not lapsed — without signalling. For a caller that settles several
429
- * decisions at once and must refuse all of them if one is refused (an AG-UI resume).
430
- */
431
- checkDecision(actor: Actor, toolCallId: string): Promise<void>;
432
- /** Everything {@link answer} checks before it signals, without signalling — see {@link checkDecision}. */
433
- checkAnswer(actor: Actor, toolCallId: string, answers: Record<string, string[]>): Promise<void>;
434
- cancel(actor: Actor, runId: string): Promise<void>;
435
- listThreads(actorRef: string): Promise<ThreadSummary[]>;
436
- getThread(actor: Actor, threadId: string): Promise<ThreadDetail | null>;
437
- deleteThread(actor: Actor, threadId: string): Promise<void>;
438
- forkThread(actor: Actor, threadId: string, fromMessageId: string): Promise<ThreadSummary>;
439
- /** Kept for source compatibility — a thin wrapper over the more general `updateThread`. */
440
- renameThread(actor: Actor, threadId: string, title: string): Promise<void>;
441
- /**
442
- * Rename a thread and/or set its default agent. Title-only patches work against ANY store (via
443
- * the required `setTitle`); a `defaultAgent` change requires the bound store to implement the
444
- * optional `updateThread` — 501 with a clear message when it doesn't, rather than silently
445
- * dropping the field.
446
- */
447
- updateThread(actor: Actor, threadId: string, patch: UpdateThreadInput): Promise<void>;
448
- /**
449
- * The persona this send runs under — see {@link AgentDepsFactory.resolvePersona}. The thread's
450
- * pinned persona is read only when the send names none AND the agent has personas to pick from.
451
- */
452
- private resolvePersona;
453
- /** Pin `persona` on the thread when it is not already — a no-op on a store that cannot. */
454
- private pinThreadPersona;
455
- private validateTitle;
456
- /**
457
- * Agent-selection precedence for a `chat()` call with no explicit `agentName`: the thread's own
458
- * `defaultAgent` (if the thread exists and one is set) wins over the module's configured default.
459
- * Skips the store entirely when the caller already named an agent or there's no thread yet.
460
- *
461
- * What this needs is one nullable scalar, so it asks for one. `getThread` returns the entire
462
- * transcript — every message, every persisted tool output — and this discards all of it; on a
463
- * long thread with large tool results that is hundreds of kilobytes read per turn, outside the
464
- * workflow, for a string. A store predating {@link ThreadDefaultAgentReader} still answers, via
465
- * the full read.
466
- */
467
- private resolveAgentName;
468
- private threadDefaultAgent;
469
- private toSummaryView;
470
- private toDetailView;
471
- /**
472
- * Re-mint each replayed attachment's url from the staging store, by `mediaId`: the url persisted
473
- * with the message was minted for THAT turn (a presigned url with an expiry), so an old turn would
474
- * otherwise show a dead link. The store's own access check applies — an attachment it will not
475
- * resolve for this actor keeps the url it was stored with.
476
- */
477
- private freshAttachmentUrls;
478
- promoteThread(actor: Actor, threadId: string): Promise<void>;
479
- /**
480
- * Drop a message and everything after it — the "edit and resend" / "delete from here" primitive.
481
- * The client then sends a fresh turn on the truncated thread. Ownership-gated like the other
482
- * thread-scoped mutations.
483
- */
484
- truncateThreadFrom(actor: Actor, threadId: string, messageId: string): Promise<void>;
485
- /**
486
- * Rate a message in one of `actor`'s threads: `'up'`/`'down'` with an optional comment, or `null`
487
- * to clear the rating. Answers `404` for an unknown message, `403` for another actor's, and `501`
488
- * on a store without `threadOfMessage` + `setMessageFeedback`.
489
- */
490
- setMessageFeedback(actor: Actor, messageId: string, input: {
491
- value: MessageFeedbackValue | null;
492
- comment?: string;
493
- }): Promise<MessageFeedback | null>;
494
- /**
495
- * Every file this actor has staged, newest first — across threads, including uploads that were
496
- * never sent, which nothing else on this surface can see.
497
- *
498
- * Metadata only. A url is minted per turn by `resolve` so it can be short-lived, and handing one
499
- * back per entry would undo that for the price of rendering a list.
500
- */
501
- listAttachments(actor: Actor, options?: {
502
- limit?: number;
503
- }): Promise<StagedAttachment[]>;
504
- /**
505
- * This actor's staged media that no live message references and that is old enough not to be an
506
- * upload in flight — the candidate set for a sweep. Returns them; never deletes them. The bytes
507
- * are the host's, and so is the decision.
508
- *
509
- * `olderThan` is required and has no default. How long a composer may sit open with a file
510
- * attached is the host's knowledge, and a library-chosen grace period would eventually delete a
511
- * file someone was about to send. The staging store is asked to apply it, and the result is
512
- * filtered again here: a store that ignores the hint would otherwise turn this into exactly that
513
- * bug, silently.
514
- *
515
- * Both halves must be answerable or this refuses. An unanswerable reference query means "cannot
516
- * tell", and treating it as "nothing is referenced" would hand back every attachment the actor
517
- * ever sent, marked safe to delete.
518
- */
519
- collectableAttachments(actor: Actor, options: {
520
- olderThan: Date;
521
- limit?: number;
522
- }): Promise<StagedAttachment[]>;
523
- /**
524
- * The bound staging store's `list`, or a refusal. Absence is never answered with an empty list:
525
- * "this host keeps no inventory" and "this actor has no files" are opposite facts that would
526
- * otherwise render identically — as a clean file list, and as a sweep that collects nothing.
527
- */
528
- private attachmentInventory;
529
- /**
530
- * Authorization seam for thread-scoped endpoints: the caller must own the thread. A missing
531
- * thread is a 404; someone else's thread is a 403 — one actor never touches another's thread.
532
- */
533
- private assertOwnsThread;
534
- /**
535
- * Authorization seam for HITL approve/reject: the caller must own the tool call's thread. Split
536
- * out from run resolution (see {@link resolveRunForToolCall}) so the console `AgentApprovalPort`
537
- * adapter can resolve+signal a decision WITHOUT this check — it is authorized upstream instead.
538
- */
539
- private assertOwnsToolCall;
540
- /**
541
- * Check a reply against the questions it answers before it is signalled: every submitted value a
542
- * question's own rules accept (a typed input's type, bounds and pattern; a pick from the offered
543
- * options), and every `required` question answered — by the reply, or by the defaults an omitted
544
- * question falls back to. A reply the loop would have to drop parts of is refused here instead, so
545
- * the person who typed it hears why. Needs the store to hand back the call's recorded questions;
546
- * without that seam the loop's own filtering is the only check.
547
- */
548
- private assertAnswersFit;
549
- /**
550
- * Authorization seam for approve/reject. The call's recorded approver decides who may settle it:
551
- * the requester (the default, and every call recorded before approvers existed) is the thread's
552
- * own actor — the ownership check this always was; any other approver goes through the policy's
553
- * `canDecide`, which by default asks whether the actor holds that role.
554
- */
555
- private assertMayDecide;
556
- /**
557
- * Refuse a decision on a request that has already lapsed — recorded `expired`, or past its
558
- * `expiresAt` with the run's own timer about to fire. Signalling it anyway would race the timeout:
559
- * a durable runtime buffers a signal nobody is waiting on, and a late approval must not be what a
560
- * later replay of the run reads back.
561
- */
562
- private assertNotExpired;
563
- /**
564
- * Resolve the run awaiting `toolCallId`. Derived server-side from the tool call alone — the client
565
- * never knows or supplies a runId, and that run is the sub-agent's own child run when the pending
566
- * call belongs to a delegated agent. The store answers from the CALL'S OWN `runId` where it has
567
- * one; see `AgentStore.runForToolCall` for why that matters once a thread can hold more than one
568
- * live run.
569
- */
570
- private resolveRunForToolCall;
571
- /** Authorization seam for `cancel`: the caller must own the thread currently streaming this run. */
572
- private assertOwnsActiveStream;
573
- }
574
-
575
22
  interface AgUiAdapterOptions {
576
23
  /** Where the route mounts, under the agent's path. Default `'ag-ui'` → `POST <path>/ag-ui`. */
577
24
  path?: string;
@@ -580,6 +27,14 @@ interface AgUiAdapterOptions {
580
27
  * before it is reported interrupted. Default 750 ms.
581
28
  */
582
29
  quietMs?: number;
30
+ /**
31
+ * A2UI over AG-UI: every generative-UI frame is also sent as an `ACTIVITY_SNAPSHOT` of type
32
+ * `a2ui-surface` (the A2UI messages under `a2ui_operations`), so CopilotKit's A2UI renderer — or
33
+ * any client of AG-UI's A2UI binding — draws it. `true` maps the library builtins onto A2UI's
34
+ * basic catalog; pass {@link A2uiOptions} for the app's own mappings or catalog. Inbound,
35
+ * `forwardedProps.a2uiAction.userAction` is always read: the action becomes the user's turn.
36
+ */
37
+ a2ui?: boolean | A2uiOptions;
583
38
  }
584
39
  /**
585
40
  * Serves the agent over AG-UI 1.0. Not mounted directly — {@link agUiAdapter} mounts a subclass at
@@ -591,7 +46,11 @@ declare class AgUiRunHandler {
591
46
  protected readonly options: AgentModuleOptions;
592
47
  protected readonly staging: AttachmentStagingStore | undefined;
593
48
  protected readonly quietMs: number | undefined;
594
- constructor(agent: AgentService, actorResolver: ActorResolver, options: AgentModuleOptions, staging: AttachmentStagingStore | undefined, quietMs: number | undefined);
49
+ /** A2UI over AG-UI ({@link AgUiAdapterOptions.a2ui}), its catalog already filled in. Off when undefined. */
50
+ protected readonly a2ui: A2uiOptions | undefined;
51
+ constructor(agent: AgentService, actorResolver: ActorResolver, options: AgentModuleOptions, staging: AttachmentStagingStore | undefined, quietMs: number | undefined,
52
+ /** A2UI over AG-UI ({@link AgUiAdapterOptions.a2ui}), its catalog already filled in. Off when undefined. */
53
+ a2ui?: A2uiOptions | undefined);
595
54
  handle(req: Request, res: Response, body: unknown): Promise<void>;
596
55
  /**
597
56
  * The turn's attachments: the ones already staged (by mediaId), then its inline media staged
@@ -1387,6 +846,15 @@ declare class QueueController {
1387
846
  */
1388
847
  declare const AGENT_CHAT_QUEUE: unique symbol;
1389
848
 
849
+ /**
850
+ * `AgentService`, as the controllers of a secondary entry (`@dudousxd/nestjs-agent/a2ui`) ask for it.
851
+ *
852
+ * A token rather than the class: every entry is its own bundle with its own copy of every class it
853
+ * imports, so the class used as an injection token there is not the one `AgentModule` provides.
854
+ * `Symbol.for` is the same value in every bundle.
855
+ */
856
+ declare const AGENT_SERVICE: unique symbol;
857
+
1390
858
  /**
1391
859
  * The default `TokenStreamSink`: buffers chunks per run in-process so a reconnecting subscriber
1392
860
  * replays everything so far, then follows live.
@@ -1538,4 +1006,4 @@ declare class ActionProposalWorkerService implements OnModuleInit, OnModuleDestr
1538
1006
  onModuleDestroy(): Promise<void>;
1539
1007
  }
1540
1008
 
1541
- export { AGENT_CHAT_QUEUE, AGENT_METADATA, AI_TOOL_METADATA, ATTACHMENT_PAGE_SIZE, ActionProposalController, ActionProposalService, type ActionProposalSettledListener, ActionProposalWorkerService, type AgUiAdapterOptions, AgUiRunHandler, Agent, AgentApprovalPortAdapter, AgentDepsFactory, AgentDiscoveryService, AgentModule, AgentModuleAsyncOptions, AgentModuleOptions, type AgentOptions, AgentProtocolAdapter, AgentService, AgentSkillsOptions, AiTool, AiToolDiscoveryService, type AiToolOptions, type AnonymousActorOptions, AnonymousActorResolver, type AttachmentLimits, AttachmentsController, type ChatParams, ChatQueueService, type ChatSendMode, type ChatSendResult, ConfigController, DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, DEFAULT_MAX_ATTACHMENT_BYTES, type DeclaredSkill, HeaderActorResolver, InProcessTokenStreamSink, InlineAgentRunner, MAX_ATTACHMENTS_PER_MESSAGE, MemoriesController, MessagesController, ModelsController, NO_USER_MESSAGE_CODE, QueueController, type QueuedSend, type RequestUserMapper, type ResolvedAiToolOptions, RunNotActiveException, SKILL_METADATA, SYSTEM_PROMPT_CONTRIBUTOR_METADATA, SYSTEM_PROMPT_METADATA, Skill, type SkillBody, SkillDiscoveryService, type SkillOptions, SkillsController, SystemPrompt, SystemPromptContributor, type ThreadDefaultAgentReader, type ThreadModelReader, type ThreadPersonaReader, ToolsController, agUiAdapter, attachmentLimits, declaredSkillProvider, defaultRequestUserMapper, readAgentMetadata, readAiToolMetadata, readSkillMetadata, requestUserActorResolver, resolveSkillsConfig, skillScope, threadPersona, toolNameFromClass };
1009
+ export { AGENT_CHAT_QUEUE, AGENT_METADATA, AGENT_SERVICE, AI_TOOL_METADATA, ATTACHMENT_PAGE_SIZE, ActionProposalController, ActionProposalService, type ActionProposalSettledListener, ActionProposalWorkerService, type AgUiAdapterOptions, AgUiRunHandler, Agent, AgentApprovalPortAdapter, AgentDepsFactory, AgentDiscoveryService, AgentModule, AgentModuleAsyncOptions, AgentModuleOptions, type AgentOptions, AgentProtocolAdapter, AgentService, AgentSkillsOptions, AiTool, AiToolDiscoveryService, type AiToolOptions, type AnonymousActorOptions, AnonymousActorResolver, AttachmentsController, ChatQueueService, ConfigController, type DeclaredSkill, HeaderActorResolver, InProcessTokenStreamSink, InlineAgentRunner, MemoriesController, MessagesController, ModelsController, QueueController, type RequestUserMapper, type ResolvedAiToolOptions, RunNotActiveException, SKILL_METADATA, SYSTEM_PROMPT_CONTRIBUTOR_METADATA, SYSTEM_PROMPT_METADATA, Skill, type SkillBody, SkillDiscoveryService, type SkillOptions, SkillsController, SystemPrompt, SystemPromptContributor, type ThreadPersonaReader, ToolsController, agUiAdapter, declaredSkillProvider, defaultRequestUserMapper, readAgentMetadata, readAiToolMetadata, readSkillMetadata, requestUserActorResolver, resolveSkillsConfig, skillScope, threadPersona, toolNameFromClass };