@dudousxd/nestjs-agent-react 0.24.0 → 0.25.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 CHANGED
@@ -38,7 +38,7 @@ handlers passed. On top of the AI SDK chat it returns:
38
38
  |---|---|
39
39
  | `transcript` | `useChatTranscript` already bound to the chat — approve/reject/answer/skip, stop, fork, regenerate, the tool catalog, and timestamps/usage read from message metadata. Override any of it with `useAgentChat({ transcript: { … } })`. |
40
40
  | `composer` | `{ text, setText, files, canSend, blockedBy, submit() }` — `files` is `useAttachments` on the chat's backend; `submit()` sends the draft with the ready files attached and clears both; `blockedBy` is `'empty' \| 'busy' \| 'uploading' \| 'quota'`. |
41
- | `models` | `{ list, providers, selected, select(id), pinToThread(id) }` — loaded the first time `list` is read. |
41
+ | `models` | `{ list, providers, selected, pinned, locked, select(id), pinToThread(id) }` — loaded the first time `list` is read. |
42
42
  | `quota` / `blocked` | `useQuota`'s state, and the window blocking sends (the `blocked` option overrides it). |
43
43
  | `approve` / `reject` / `answer` / `skip` | `({ toolCallId, … })` — the same object shape the transcript's handlers take. |
44
44
  | `fork` / `truncateFrom` / `promote` | `({ messageId, threadId? })` / `({ threadId? })`, defaulting to this chat's thread. |
@@ -619,9 +619,12 @@ const { providers, selected, select } = chat.models; // loaded on first read
619
619
 
620
620
  `chat.models` reads `GET <base>/models?agent=` (models grouped by provider, with badges and
621
621
  availability; `list` is the same flattened) the first time `list`/`providers` is read. `select(id)`
622
- runs the following turns on it (sent as the body's `model`); `selected` is the pick, else the
623
- thread's pin, else the server default. `pinToThread(id)` pins it on the thread (`null` unpins) so
624
- it survives reloads — on a chat with no thread yet, the pin lands when the first send creates one.
622
+ runs this chat's following sends on it (sent as the body's `model`, which the server applies to
623
+ that turn only; switching threads drops the pick); `selected` is the pick, else the thread's pin
624
+ (`pinned`), else the server default. `pinToThread(id)` pins it on the thread (`null` unpins) so it
625
+ survives reloads, replacing the pick — on a chat with no thread yet, the pin lands when the first
626
+ send creates one. `locked` (`{ model, reason? }`) is set when the agent always runs on one model:
627
+ `selected` is then that model and `select` does nothing.
625
628
  `useAgentChat({ model })` controls the model yourself; a single send can override it with
626
629
  `sendMessage(msg, { body: { model } })`. `useModels()` / `useAgents()` are the standalone hooks
627
630
  (an agent picker: `useAgents().agents`, sent as `useAgentChat({ agent })`). The server refuses a
@@ -1,5 +1,32 @@
1
1
  import { ThreadSummary, ThreadDetail, MessageAttachment, ToolCatalogEntry, SkillCatalogEntry, QuotaReport, ModelCatalogView, AgentCatalogEntry, AgentClientConfig, MessageFeedbackValue, MessageFeedback } from '@dudousxd/nestjs-agent-core';
2
2
 
3
+ /**
4
+ * What a server said when it refused a request, read once so every error class of this package
5
+ * reports it the same way. Bundled into each entry that throws (the root and `/media`), which is
6
+ * why it holds no class of its own.
7
+ */
8
+ interface ErrorAnswer {
9
+ /** The body, parsed as JSON when it is JSON, else the raw text; `undefined` when empty. */
10
+ body: unknown;
11
+ /** `body.message` — a string, or NestJS's list of validation messages joined with `; `. */
12
+ message: string | undefined;
13
+ /** `body.code`, the machine-readable reason (`quota_exceeded`, …), when the server sent one. */
14
+ code: string | undefined;
15
+ }
16
+ /**
17
+ * The shape both {@link import('./client.js').AgentHttpError} and `MediaUploadError` share, for
18
+ * code that handles either without an `instanceof` (the two live in separate bundles).
19
+ */
20
+ interface AgentRequestError extends Error {
21
+ status: number;
22
+ method: string;
23
+ path: string;
24
+ body: unknown;
25
+ code: string | undefined;
26
+ }
27
+ /** Called with every error answer before it is thrown — for app-wide reactions (401, 402, …). */
28
+ type HttpErrorListener = (error: AgentRequestError) => void;
29
+
3
30
  /**
4
31
  * Partial update accepted by `PATCH <base>/threads/:threadId`. `defaultAgent: null` clears a
5
32
  * previously-set default back to the module's own default; omitting it leaves the thread's
@@ -62,6 +89,8 @@ interface AgentConnection {
62
89
  headers: () => Promise<Record<string, string>>;
63
90
  credentials?: RequestCredentials;
64
91
  fetch: typeof fetch;
92
+ /** The client's `onHttpError` — call it with an upload's error answer before throwing it. */
93
+ onHttpError?: HttpErrorListener;
65
94
  }
66
95
  /**
67
96
  * Replaces {@link AgentClient}'s own `uploadAttachment` (`POST <path>/attachments`) — e.g.
@@ -110,9 +139,11 @@ interface AgentBackend {
110
139
  answerToolCall?(input: {
111
140
  toolCallId: string;
112
141
  answers?: Record<string, string[]>;
142
+ via?: string;
113
143
  }): Promise<unknown>;
114
144
  skipToolCall?(input: {
115
145
  toolCallId: string;
146
+ via?: string;
116
147
  }): Promise<unknown>;
117
148
  uploadAttachment?(file: File, options?: UploadAttachmentOptions): Promise<MessageAttachment>;
118
149
  listTools?(agent?: string): Promise<ToolCatalogEntry[]>;
@@ -145,13 +176,22 @@ declare function requireBackendMethod<K extends OptionalMethod>(backend: AgentBa
145
176
 
146
177
  /**
147
178
  * Thrown by {@link AgentClient} on a non-2xx response. Carries the HTTP `status` so callers can
148
- * branch (e.g. 403 → "not your thread", 429 → quota) instead of string-matching a generic Error.
179
+ * branch (e.g. 403 → "not your thread", 429 → quota) instead of string-matching a generic Error,
180
+ * and what the server said: `message` is the body's `message` when it sent one (so a hook that
181
+ * shows `error.message` shows the server's words), `code` its machine-readable `code`, and `body`
182
+ * the whole answer, parsed when it is JSON.
149
183
  */
150
- declare class AgentHttpError extends Error {
184
+ declare class AgentHttpError extends Error implements AgentRequestError {
151
185
  readonly status: number;
152
186
  readonly method: string;
153
187
  readonly path: string;
154
- constructor(status: number, method: string, path: string, statusText: string);
188
+ /** The answer's body: parsed JSON, else its text; `undefined` when empty. */
189
+ readonly body: unknown;
190
+ /** The body's `code` (`quota_exceeded`, …), when the server sent one. */
191
+ readonly code: string | undefined;
192
+ constructor(status: number, method: string, path: string, statusText: string, answer?: ErrorAnswer);
193
+ /** Read a refused `response`'s body into an error. */
194
+ static from(response: Response, method: string, path: string): Promise<AgentHttpError>;
155
195
  }
156
196
  interface CancelResult {
157
197
  aborted: boolean;
@@ -185,6 +225,13 @@ interface AgentClientOptions {
185
225
  credentials?: RequestCredentials;
186
226
  /** Injectable for tests / non-browser runtimes. */
187
227
  fetch?: typeof fetch;
228
+ /**
229
+ * Called with every error answer (an {@link AgentHttpError}, or a `MediaUploadError` from a
230
+ * `mediaAttachments()` upload) right before it is thrown — the place for app-wide reactions such
231
+ * as "401 → sign in again". The error still reaches the caller. Not called for the `404` a
232
+ * resume answers when nothing is streaming, which is an answer, not a failure.
233
+ */
234
+ onHttpError?: HttpErrorListener;
188
235
  /** Attachment uploads. */
189
236
  attachments?: {
190
237
  /**
@@ -272,10 +319,12 @@ declare class AgentClient implements AgentBackend {
272
319
  * out takes the pre-picked default the request carried, resolved server-side against the request
273
320
  * the run already holds. Omit the whole object and the user has confirmed every pre-picked
274
321
  * answer — which is the point of the surface, so it is a valid submission rather than a blank.
322
+ * `via` names the surface the answer came through (the server records `'web'` when omitted).
275
323
  */
276
324
  answerToolCall(input: {
277
325
  toolCallId: string;
278
326
  answers?: Record<string, string[]>;
327
+ via?: string;
279
328
  }): Promise<void>;
280
329
  /**
281
330
  * Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
@@ -284,6 +333,7 @@ declare class AgentClient implements AgentBackend {
284
333
  */
285
334
  skipToolCall(input: {
286
335
  toolCallId: string;
336
+ via?: string;
287
337
  }): Promise<void>;
288
338
  private fetchImpl;
289
339
  /** This client's connection, for an {@link AttachmentUploadStrategy}. */
@@ -295,7 +345,9 @@ declare class AgentClient implements AgentBackend {
295
345
  private resolveHeaders;
296
346
  private credentials;
297
347
  private request;
348
+ /** The error for a refused `response`, already reported to `onHttpError`. */
349
+ private failure;
298
350
  private handleResponse;
299
351
  }
300
352
 
301
- export { type AgentBackend as A, type CancelResult as C, type MessageFeedbackInput as M, type ResumeStreamRequest as R, type ThreadPatch as T, type UploadAttachmentOptions as U, type AttachmentUploadStrategy as a, AgentBackendUnsupportedError as b, AgentClient as c, type AgentClientOptions as d, type AgentConnection as e, AgentHttpError as f, type ChatStreamRequest as g, type ChatStreamResponse as h, requireBackendMethod as r };
353
+ export { type AgentBackend as A, type CancelResult as C, type ErrorAnswer as E, type HttpErrorListener as H, type MessageFeedbackInput as M, type ResumeStreamRequest as R, type ThreadPatch as T, type UploadAttachmentOptions as U, type AttachmentUploadStrategy as a, AgentBackendUnsupportedError as b, AgentClient as c, type AgentClientOptions as d, type AgentConnection as e, AgentHttpError as f, type AgentRequestError as g, type ChatStreamRequest as h, type ChatStreamResponse as i, requireBackendMethod as r };
@@ -1,5 +1,32 @@
1
1
  import { ThreadSummary, ThreadDetail, MessageAttachment, ToolCatalogEntry, SkillCatalogEntry, QuotaReport, ModelCatalogView, AgentCatalogEntry, AgentClientConfig, MessageFeedbackValue, MessageFeedback } from '@dudousxd/nestjs-agent-core';
2
2
 
3
+ /**
4
+ * What a server said when it refused a request, read once so every error class of this package
5
+ * reports it the same way. Bundled into each entry that throws (the root and `/media`), which is
6
+ * why it holds no class of its own.
7
+ */
8
+ interface ErrorAnswer {
9
+ /** The body, parsed as JSON when it is JSON, else the raw text; `undefined` when empty. */
10
+ body: unknown;
11
+ /** `body.message` — a string, or NestJS's list of validation messages joined with `; `. */
12
+ message: string | undefined;
13
+ /** `body.code`, the machine-readable reason (`quota_exceeded`, …), when the server sent one. */
14
+ code: string | undefined;
15
+ }
16
+ /**
17
+ * The shape both {@link import('./client.js').AgentHttpError} and `MediaUploadError` share, for
18
+ * code that handles either without an `instanceof` (the two live in separate bundles).
19
+ */
20
+ interface AgentRequestError extends Error {
21
+ status: number;
22
+ method: string;
23
+ path: string;
24
+ body: unknown;
25
+ code: string | undefined;
26
+ }
27
+ /** Called with every error answer before it is thrown — for app-wide reactions (401, 402, …). */
28
+ type HttpErrorListener = (error: AgentRequestError) => void;
29
+
3
30
  /**
4
31
  * Partial update accepted by `PATCH <base>/threads/:threadId`. `defaultAgent: null` clears a
5
32
  * previously-set default back to the module's own default; omitting it leaves the thread's
@@ -62,6 +89,8 @@ interface AgentConnection {
62
89
  headers: () => Promise<Record<string, string>>;
63
90
  credentials?: RequestCredentials;
64
91
  fetch: typeof fetch;
92
+ /** The client's `onHttpError` — call it with an upload's error answer before throwing it. */
93
+ onHttpError?: HttpErrorListener;
65
94
  }
66
95
  /**
67
96
  * Replaces {@link AgentClient}'s own `uploadAttachment` (`POST <path>/attachments`) — e.g.
@@ -110,9 +139,11 @@ interface AgentBackend {
110
139
  answerToolCall?(input: {
111
140
  toolCallId: string;
112
141
  answers?: Record<string, string[]>;
142
+ via?: string;
113
143
  }): Promise<unknown>;
114
144
  skipToolCall?(input: {
115
145
  toolCallId: string;
146
+ via?: string;
116
147
  }): Promise<unknown>;
117
148
  uploadAttachment?(file: File, options?: UploadAttachmentOptions): Promise<MessageAttachment>;
118
149
  listTools?(agent?: string): Promise<ToolCatalogEntry[]>;
@@ -145,13 +176,22 @@ declare function requireBackendMethod<K extends OptionalMethod>(backend: AgentBa
145
176
 
146
177
  /**
147
178
  * Thrown by {@link AgentClient} on a non-2xx response. Carries the HTTP `status` so callers can
148
- * branch (e.g. 403 → "not your thread", 429 → quota) instead of string-matching a generic Error.
179
+ * branch (e.g. 403 → "not your thread", 429 → quota) instead of string-matching a generic Error,
180
+ * and what the server said: `message` is the body's `message` when it sent one (so a hook that
181
+ * shows `error.message` shows the server's words), `code` its machine-readable `code`, and `body`
182
+ * the whole answer, parsed when it is JSON.
149
183
  */
150
- declare class AgentHttpError extends Error {
184
+ declare class AgentHttpError extends Error implements AgentRequestError {
151
185
  readonly status: number;
152
186
  readonly method: string;
153
187
  readonly path: string;
154
- constructor(status: number, method: string, path: string, statusText: string);
188
+ /** The answer's body: parsed JSON, else its text; `undefined` when empty. */
189
+ readonly body: unknown;
190
+ /** The body's `code` (`quota_exceeded`, …), when the server sent one. */
191
+ readonly code: string | undefined;
192
+ constructor(status: number, method: string, path: string, statusText: string, answer?: ErrorAnswer);
193
+ /** Read a refused `response`'s body into an error. */
194
+ static from(response: Response, method: string, path: string): Promise<AgentHttpError>;
155
195
  }
156
196
  interface CancelResult {
157
197
  aborted: boolean;
@@ -185,6 +225,13 @@ interface AgentClientOptions {
185
225
  credentials?: RequestCredentials;
186
226
  /** Injectable for tests / non-browser runtimes. */
187
227
  fetch?: typeof fetch;
228
+ /**
229
+ * Called with every error answer (an {@link AgentHttpError}, or a `MediaUploadError` from a
230
+ * `mediaAttachments()` upload) right before it is thrown — the place for app-wide reactions such
231
+ * as "401 → sign in again". The error still reaches the caller. Not called for the `404` a
232
+ * resume answers when nothing is streaming, which is an answer, not a failure.
233
+ */
234
+ onHttpError?: HttpErrorListener;
188
235
  /** Attachment uploads. */
189
236
  attachments?: {
190
237
  /**
@@ -272,10 +319,12 @@ declare class AgentClient implements AgentBackend {
272
319
  * out takes the pre-picked default the request carried, resolved server-side against the request
273
320
  * the run already holds. Omit the whole object and the user has confirmed every pre-picked
274
321
  * answer — which is the point of the surface, so it is a valid submission rather than a blank.
322
+ * `via` names the surface the answer came through (the server records `'web'` when omitted).
275
323
  */
276
324
  answerToolCall(input: {
277
325
  toolCallId: string;
278
326
  answers?: Record<string, string[]>;
327
+ via?: string;
279
328
  }): Promise<void>;
280
329
  /**
281
330
  * Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
@@ -284,6 +333,7 @@ declare class AgentClient implements AgentBackend {
284
333
  */
285
334
  skipToolCall(input: {
286
335
  toolCallId: string;
336
+ via?: string;
287
337
  }): Promise<void>;
288
338
  private fetchImpl;
289
339
  /** This client's connection, for an {@link AttachmentUploadStrategy}. */
@@ -295,7 +345,9 @@ declare class AgentClient implements AgentBackend {
295
345
  private resolveHeaders;
296
346
  private credentials;
297
347
  private request;
348
+ /** The error for a refused `response`, already reported to `onHttpError`. */
349
+ private failure;
298
350
  private handleResponse;
299
351
  }
300
352
 
301
- export { type AgentBackend as A, type CancelResult as C, type MessageFeedbackInput as M, type ResumeStreamRequest as R, type ThreadPatch as T, type UploadAttachmentOptions as U, type AttachmentUploadStrategy as a, AgentBackendUnsupportedError as b, AgentClient as c, type AgentClientOptions as d, type AgentConnection as e, AgentHttpError as f, type ChatStreamRequest as g, type ChatStreamResponse as h, requireBackendMethod as r };
353
+ export { type AgentBackend as A, type CancelResult as C, type ErrorAnswer as E, type HttpErrorListener as H, type MessageFeedbackInput as M, type ResumeStreamRequest as R, type ThreadPatch as T, type UploadAttachmentOptions as U, type AttachmentUploadStrategy as a, AgentBackendUnsupportedError as b, AgentClient as c, type AgentClientOptions as d, type AgentConnection as e, AgentHttpError as f, type AgentRequestError as g, type ChatStreamRequest as h, type ChatStreamResponse as i, requireBackendMethod as r };
@@ -427,6 +427,10 @@ interface TranscriptElicitationOutcome {
427
427
  defaulted: string[];
428
428
  /** The questions against the chosen labels, as the model read them back. */
429
429
  summary: string | null;
430
+ /** Who answered (or skipped) — an approval's `decidedBy`. `null` when the run did not record it. */
431
+ answeredBy: string | null;
432
+ /** The surface it came through (`'web'`, `'slack'`, …) — an approval's `decidedVia`. */
433
+ answeredVia: string | null;
430
434
  }
431
435
  /**
432
436
  * A question set the run put to the user, parked until someone settles it — the intake an agent
@@ -427,6 +427,10 @@ interface TranscriptElicitationOutcome {
427
427
  defaulted: string[];
428
428
  /** The questions against the chosen labels, as the model read them back. */
429
429
  summary: string | null;
430
+ /** Who answered (or skipped) — an approval's `decidedBy`. `null` when the run did not record it. */
431
+ answeredBy: string | null;
432
+ /** The surface it came through (`'web'`, `'slack'`, …) — an approval's `decidedVia`. */
433
+ answeredVia: string | null;
430
434
  }
431
435
  /**
432
436
  * A question set the run put to the user, parked until someone settles it — the intake an agent
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { ComponentRegistry } from '@json-render/react';
3
- import { n as GenuiProviderProps$1, c as GenerativeUIElement, q as GenuiRenderer, p as GenuiRegistry } from './generative-ui-9AJNVKBZ.cjs';
3
+ import { n as GenuiProviderProps$1, c as GenerativeUIElement, q as GenuiRenderer, p as GenuiRegistry } from './generative-ui-BejHxxbR.cjs';
4
4
  import '@dudousxd/nestjs-agent-core';
5
5
  import 'ai';
6
6
 
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { ComponentRegistry } from '@json-render/react';
3
- import { n as GenuiProviderProps$1, c as GenerativeUIElement, q as GenuiRenderer, p as GenuiRegistry } from './generative-ui-9AJNVKBZ.js';
3
+ import { n as GenuiProviderProps$1, c as GenerativeUIElement, q as GenuiRenderer, p as GenuiRegistry } from './generative-ui-BejHxxbR.js';
4
4
  import '@dudousxd/nestjs-agent-core';
5
5
  import 'ai';
6
6
 
package/dist/genui.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { G as GenerativeUIItem } from './generative-ui-9AJNVKBZ.cjs';
2
- export { a as GENUI_TREE_COMPONENT, b as GenerativeUI, c as GenerativeUIElement, d as GenerativeUIFallback, e as GenerativeUIOptions, f as GenerativeUIProblem, g as GenerativeUIProps, h as GenerativeUIScope, i as GenerativeUIScopeProps, j as GenerativeUIState, k as GenuiCatalogLike, l as GenuiIssueLike, m as GenuiProvider, n as GenuiProviderProps, o as GenuiProviderValue, p as GenuiRegistry, q as GenuiRenderer, r as GenuiTree, R as ResolveComponent, u as useGenerativeUI, s as useGenuiProvider } from './generative-ui-9AJNVKBZ.cjs';
1
+ import { G as GenerativeUIItem } from './generative-ui-BejHxxbR.cjs';
2
+ export { a as GENUI_TREE_COMPONENT, b as GenerativeUI, c as GenerativeUIElement, d as GenerativeUIFallback, e as GenerativeUIOptions, f as GenerativeUIProblem, g as GenerativeUIProps, h as GenerativeUIScope, i as GenerativeUIScopeProps, j as GenerativeUIState, k as GenuiCatalogLike, l as GenuiIssueLike, m as GenuiProvider, n as GenuiProviderProps, o as GenuiProviderValue, p as GenuiRegistry, q as GenuiRenderer, r as GenuiTree, R as ResolveComponent, u as useGenerativeUI, s as useGenuiProvider } from './generative-ui-BejHxxbR.cjs';
3
3
  import 'react';
4
4
  import '@dudousxd/nestjs-agent-core';
5
5
  import 'ai';
package/dist/genui.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { G as GenerativeUIItem } from './generative-ui-9AJNVKBZ.js';
2
- export { a as GENUI_TREE_COMPONENT, b as GenerativeUI, c as GenerativeUIElement, d as GenerativeUIFallback, e as GenerativeUIOptions, f as GenerativeUIProblem, g as GenerativeUIProps, h as GenerativeUIScope, i as GenerativeUIScopeProps, j as GenerativeUIState, k as GenuiCatalogLike, l as GenuiIssueLike, m as GenuiProvider, n as GenuiProviderProps, o as GenuiProviderValue, p as GenuiRegistry, q as GenuiRenderer, r as GenuiTree, R as ResolveComponent, u as useGenerativeUI, s as useGenuiProvider } from './generative-ui-9AJNVKBZ.js';
1
+ import { G as GenerativeUIItem } from './generative-ui-BejHxxbR.js';
2
+ export { a as GENUI_TREE_COMPONENT, b as GenerativeUI, c as GenerativeUIElement, d as GenerativeUIFallback, e as GenerativeUIOptions, f as GenerativeUIProblem, g as GenerativeUIProps, h as GenerativeUIScope, i as GenerativeUIScopeProps, j as GenerativeUIState, k as GenuiCatalogLike, l as GenuiIssueLike, m as GenuiProvider, n as GenuiProviderProps, o as GenuiProviderValue, p as GenuiRegistry, q as GenuiRenderer, r as GenuiTree, R as ResolveComponent, u as useGenerativeUI, s as useGenuiProvider } from './generative-ui-BejHxxbR.js';
3
3
  import 'react';
4
4
  import '@dudousxd/nestjs-agent-core';
5
5
  import 'ai';