@dudousxd/nestjs-agent 1.10.0 → 1.12.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,7 +1,7 @@
1
- import { DynamicModule, OnModuleInit, Type, OnApplicationBootstrap } from '@nestjs/common';
2
- import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, A as AgentDepsFactory, d as AgentSkillsOptions } from './agent-deps.factory-DuTy1UyU.cjs';
3
- export { e as AGENT_TOOL_BRAND, f as AgentAttachmentsOptions, g as AgentDeps, h as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, L as LedgerQuotaProvider, Q as QuotaLimits, i as QuotaWindowLimits, j as delegateToolName, p as provideAgentTool, k as provideAgentTools, u as utcDay } from './agent-deps.factory-DuTy1UyU.cjs';
4
- import { AgentRunner, AgentStore, QuotaStore, AttachmentStagingStore, ModelCatalog, QuotaProvider, Actor, QuotaReport, ModelCatalogView, AttachmentRef, PageContext, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, MessageFeedbackValue, MessageFeedback, StagedAttachment, QuotaView, AgentApprovalPort, ActorResolver, MessageAttachment, ToolPresentation, AgentHistoryWindow, AgentIntake, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunInput, TokenStreamSink, SinkWriter, QuotaState } from '@dudousxd/nestjs-agent-core';
1
+ import { DynamicModule, Type, OnApplicationBootstrap, OnModuleInit } from '@nestjs/common';
2
+ import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, A as AgentDepsFactory, d as AgentSkillsOptions } from './agent-deps.factory-pqv_qTmJ.cjs';
3
+ export { e as AGENT_TOOL_BRAND, f as AgentAttachmentsOptions, g as AgentDeps, h as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, L as LedgerQuotaProvider, Q as QuotaLimits, i as QuotaWindowLimits, j as delegateToolName, p as provideAgentTool, k as provideAgentTools, u as utcDay } from './agent-deps.factory-pqv_qTmJ.cjs';
4
+ import { AgentRunner, AgentStore, AttachmentStagingStore, ModelCatalog, QuotaProvider, Actor, QuotaReport, ModelCatalogView, AttachmentRef, PageContext, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, MessageFeedbackValue, MessageFeedback, StagedAttachment, AgentApprovalPort, ActorResolver, MessageAttachment, AgentAttachmentConfig, AgentClientConfig, ToolPresentation, AgentHistoryWindow, AgentIntake, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunInput, TokenStreamSink, SinkWriter } from '@dudousxd/nestjs-agent-core';
5
5
  export * from '@dudousxd/nestjs-agent-core';
6
6
  import { Request } from 'express';
7
7
  import { StandardSchemaV1 } from '@standard-schema/spec';
@@ -56,12 +56,11 @@ declare class AgentService {
56
56
  private readonly runner;
57
57
  private readonly store;
58
58
  private readonly deps;
59
- private readonly quota;
60
59
  private readonly staging?;
61
60
  private readonly models?;
62
61
  private readonly quotaProvider?;
63
62
  private readonly options?;
64
- constructor(runner: AgentRunner, store: AgentStore, deps: AgentDepsFactory, quota: QuotaStore | undefined, staging?: AttachmentStagingStore | undefined, models?: ModelCatalog | undefined, quotaProvider?: QuotaProvider | undefined, options?: Pick<AgentModuleOptions, "quotaProvider" | "quotaLimits"> | undefined);
63
+ constructor(runner: AgentRunner, store: AgentStore, deps: AgentDepsFactory, staging?: AttachmentStagingStore | undefined, models?: ModelCatalog | undefined, quotaProvider?: QuotaProvider | undefined, options?: Pick<AgentModuleOptions, "quota"> | undefined);
65
64
  /**
66
65
  * The actor's budget across windows (`GET <base>/quota`) — the bound {@link QuotaProvider}'s
67
66
  * report. Without one (a service built outside the module), a day window from the ledger.
@@ -69,8 +68,7 @@ declare class AgentService {
69
68
  quotaReport(actor: Actor): Promise<QuotaReport>;
70
69
  /**
71
70
  * Refuse a turn the actor's budget no longer covers — only when the host configured one
72
- * (`quotaProvider` or `quotaLimits`): the default report is informational, and the daily token
73
- * quota the loop enforces keeps working as it always has.
71
+ * (`quota`): the default report is informational.
74
72
  */
75
73
  private assertWithinQuota;
76
74
  /**
@@ -182,6 +180,13 @@ declare class AgentService {
182
180
  private threadDefaultAgent;
183
181
  private toSummaryView;
184
182
  private toDetailView;
183
+ /**
184
+ * Re-mint each replayed attachment's url from the staging store, by `mediaId`: the url persisted
185
+ * with the message was minted for THAT turn (a presigned url with an expiry), so an old turn would
186
+ * otherwise show a dead link. The store's own access check applies — an attachment it will not
187
+ * resolve for this actor keeps the url it was stored with.
188
+ */
189
+ private freshAttachmentUrls;
185
190
  promoteThread(actor: Actor, threadId: string): Promise<void>;
186
191
  /**
187
192
  * Drop a message and everything after it — the "edit and resend" / "delete from here" primitive.
@@ -233,12 +238,6 @@ declare class AgentService {
233
238
  * otherwise render identically — as a clean file list, and as a sweep that collects nothing.
234
239
  */
235
240
  private attachmentInventory;
236
- /**
237
- * The day's usage for the badge: tokens + summed USD cost from the store, and the configured
238
- * limit from the quota store (null → unlimited). `withinLimit` comes from the quota store so it
239
- * can never drift from what enforcement uses.
240
- */
241
- quotaToday(actorRef: string): Promise<QuotaView>;
242
241
  /**
243
242
  * Authorization seam for thread-scoped endpoints: the caller must own the thread. A missing
244
243
  * thread is a 404; someone else's thread is a 403 — one actor never touches another's thread.
@@ -313,8 +312,6 @@ declare class AgentApprovalPortAdapter implements AgentApprovalPort {
313
312
  }): Promise<void>;
314
313
  }
315
314
 
316
- /** Default per-file size cap when `AgentModuleOptions.attachments.maxBytes` is omitted (20 MiB). */
317
- declare const DEFAULT_MAX_ATTACHMENT_BYTES: number;
318
315
  /**
319
316
  * How many entries `GET /agent/attachments` returns. Fixed rather than caller-supplied: this route
320
317
  * exists so a composer can show the files someone has uploaded, and a host that needs real paging
@@ -322,13 +319,8 @@ declare const DEFAULT_MAX_ATTACHMENT_BYTES: number;
322
319
  */
323
320
  declare const ATTACHMENT_PAGE_SIZE = 50;
324
321
  /**
325
- * Default allowlist: what multimodal model providers commonly accept as native image/file parts.
326
- */
327
- declare const DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES: readonly string[];
328
- /**
329
- * Optional `POST /agent/attachments` upload surface — mounted only when `AgentModuleOptions
330
- * .attachments.upload` (or the async config's `attachmentsUpload`) is `true` (see `agent.module.ts`).
331
- * Multipart single file field `'file'`, buffered in memory (no disk write — `FileInterceptor`'s
322
+ * `POST <base>/attachments` — the multipart upload route, live whenever an
323
+ * `AGENT_ATTACHMENT_STAGING` provider is bound (`501` otherwise). Multipart single file field `'file'`, buffered in memory (no disk write — `FileInterceptor`'s
332
324
  * default storage) up to {@link HARD_MAX_ATTACHMENT_BYTES}, then validated against the configured
333
325
  * size cap + content-type allowlist and handed to the bound {@link AttachmentStagingStore} to
334
326
  * persist and turn into a model-fetchable URL. Both size gates answer `413`: the ceiling aborts the
@@ -339,19 +331,12 @@ declare const DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES: readonly string[];
339
331
  * host deleting bytes, so it stays an in-process call (`AgentService.collectableAttachments`)
340
332
  * rather than something reachable with a session cookie.
341
333
  */
342
- declare class AttachmentsController implements OnModuleInit {
334
+ declare class AttachmentsController {
343
335
  private readonly options;
344
336
  private readonly actorResolver;
345
337
  private readonly agents;
346
338
  private readonly staging;
347
339
  constructor(options: AgentModuleOptions, actorResolver: ActorResolver, agents: AgentService, staging: AttachmentStagingStore | undefined);
348
- /**
349
- * `attachments.upload: true` is what mounts this controller at all (see `agent.module.ts`), so by
350
- * the time an instance exists the host clearly intends uploads to work. Fail boot loudly rather
351
- * than mounting a controller that would 501 on every single request because nobody bound
352
- * `AGENT_ATTACHMENT_STAGING`.
353
- */
354
- onModuleInit(): void;
355
340
  /**
356
341
  * The caller's own staged files, newest first — the one question `GET /agent/threads/:id` cannot
357
342
  * answer, since an upload that was never sent belongs to no thread.
@@ -365,21 +350,52 @@ declare class AttachmentsController implements OnModuleInit {
365
350
  upload(req: Request, file: Express.Multer.File | undefined): Promise<MessageAttachment>;
366
351
  }
367
352
 
353
+ /** Default per-file size cap (20 MiB). */
354
+ declare const DEFAULT_MAX_ATTACHMENT_BYTES: number;
355
+ /** Default allowlist: what multimodal model providers commonly accept as native image/file parts. */
356
+ declare const DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES: readonly string[];
357
+ /** How many attachments one message may name. */
358
+ declare const MAX_ATTACHMENTS_PER_MESSAGE = 10;
359
+ /** The attachment rules in force — `AgentAttachmentConfig`, as `GET <base>/config` serves it. */
360
+ type AttachmentLimits = AgentAttachmentConfig;
361
+ /**
362
+ * One source for attachment limits: the staging store's own `describe()` when it declares them
363
+ * (`AgentMediaAttachmentsModule` does), else `AgentModule`'s `attachments` option, else the defaults.
364
+ */
365
+ declare function attachmentLimits(options: Pick<AgentModuleOptions, 'attachments'> | undefined, staging: AttachmentStagingStore | undefined): AttachmentLimits;
366
+
367
+ declare class ConfigController {
368
+ private readonly options;
369
+ private readonly actorResolver;
370
+ private readonly staging;
371
+ private readonly models;
372
+ constructor(options: AgentModuleOptions, actorResolver: ActorResolver, staging: AttachmentStagingStore | undefined, models: ModelCatalog | undefined);
373
+ config(req: Request): Promise<AgentClientConfig>;
374
+ }
375
+
368
376
  declare const AI_TOOL_METADATA: unique symbol;
369
377
  interface AiToolOptions {
370
- name: string;
371
378
  /**
372
- * `read` auto-executes; `action` requires HITL approval. (Core's `ToolKind` also has `agent`
373
- * for delegation, but that kind is synthesized from `delegatesTo` — never authored here.)
379
+ * What the model calls it. Omit → the class name, camelCased, with a trailing `Tool` dropped
380
+ * (`GetWeatherTool` → `getWeather`).
374
381
  */
375
- kind: 'read' | 'action';
382
+ name?: string;
383
+ /**
384
+ * `read` (the default) auto-executes; `action` requires HITL approval. (Core's `ToolKind` also
385
+ * has `agent` for delegation, but that kind is synthesized from `delegatesTo` — never authored
386
+ * here.)
387
+ */
388
+ kind?: 'read' | 'action';
376
389
  description: string;
377
390
  /**
378
391
  * Input schema as a [Standard Schema](https://standardschema.dev) — Zod, Valibot, or ArkType.
379
392
  * Validated before the handler runs.
380
393
  */
381
394
  input: StandardSchemaV1;
382
- /** Roles allowed to invoke. Omit to inherit the module's default roles. */
395
+ /**
396
+ * Roles allowed to invoke. Omit → the module's `defaultRoles`, which by default restrict nobody:
397
+ * any resolved actor — an anonymous visitor included, when no `actorResolver` is configured.
398
+ */
383
399
  roles?: string[];
384
400
  /**
385
401
  * Whether this tool exists in this deployment. `false` — or a predicate returning `false`, which
@@ -420,13 +436,21 @@ interface AiToolOptions {
420
436
  */
421
437
  terminal?: boolean;
422
438
  }
439
+ /** {@link AiToolOptions} with the defaults applied — what discovery reads. */
440
+ interface ResolvedAiToolOptions extends AiToolOptions {
441
+ name: string;
442
+ kind: 'read' | 'action';
443
+ }
444
+ /** `GetWeatherTool` → `getWeather`; `SQLQueryTool` → `sqlQuery`. */
445
+ declare function toolNameFromClass(className: string): string;
423
446
  /**
424
447
  * Marks a provider class as an AI tool. The class must implement `execute(input, ctx)`.
425
448
  * `AiToolDiscoveryService` registers every `@AiTool` provider into the `ToolRegistry` at boot.
449
+ * Only `description` and `input` are required:
426
450
  *
427
451
  * ```ts
428
- * @AiTool({ name: 'getWeather', kind: 'read', description: '...', input: z.object({ city: z.string() }) })
429
- * class GetWeatherTool implements ToolHandler<{ city: string }> {
452
+ * @AiTool({ description: 'Current weather for a city', input: z.object({ city: z.string() }) })
453
+ * class GetWeatherTool implements ToolHandler<{ city: string }> { // name: getWeather, kind: read
430
454
  * async execute(input, ctx) { return { tempC: 21 }; }
431
455
  * }
432
456
  * ```
@@ -444,7 +468,7 @@ interface AiToolOptions {
444
468
  * ```
445
469
  */
446
470
  declare function AiTool(options: AiToolOptions): ClassDecorator;
447
- declare function readAiToolMetadata(target: object): AiToolOptions | undefined;
471
+ declare function readAiToolMetadata(target: object): ResolvedAiToolOptions | undefined;
448
472
 
449
473
  /**
450
474
  * Declares a class as an agent. The class is discovered at boot (via `DiscoveryService`) and
@@ -915,21 +939,6 @@ declare class InProcessTokenStreamSink implements TokenStreamSink {
915
939
  close(runId: string): void;
916
940
  }
917
941
 
918
- /**
919
- * A production {@link QuotaStore} backed by the persisted usage ledger. `check` reads the actor's
920
- * tokens-so-far for the day straight from {@link AgentStore.quotaToday} and compares it to a fixed
921
- * daily limit; `bump` is a deliberate no-op because the loop's `recordUsage` already writes every
922
- * turn's tokens to that same ledger. One source of truth — so enforcement and what the dashboard
923
- * reports can never drift, and it works across replicas with no extra shared state.
924
- */
925
- declare class LedgerQuotaStore implements QuotaStore {
926
- private readonly store;
927
- private readonly limitTokens;
928
- constructor(store: AgentStore, limitTokens: number);
929
- check(actorRef: string, day: string): Promise<QuotaState>;
930
- bump(_actorRef: string, _day: string, _tokens: number): Promise<void>;
931
- }
932
-
933
942
  /**
934
943
  * A development / gateway {@link ActorResolver} that trusts request headers:
935
944
  * `x-actor-id` (required), `x-actor-role` (comma-separated → `roles`), `x-tenant-ref`.
@@ -946,4 +955,77 @@ declare class HeaderActorResolver implements ActorResolver {
946
955
  resolve(req: unknown): Actor;
947
956
  }
948
957
 
949
- export { AGENT_METADATA, AI_TOOL_METADATA, ATTACHMENT_PAGE_SIZE, Agent, AgentApprovalPortAdapter, AgentDepsFactory, AgentDiscoveryService, AgentModule, AgentModuleAsyncOptions, AgentModuleOptions, type AgentOptions, AgentService, AgentSkillsOptions, AiTool, AiToolDiscoveryService, type AiToolOptions, AttachmentsController, type ChatParams, DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, DEFAULT_MAX_ATTACHMENT_BYTES, type DeclaredSkill, HeaderActorResolver, InProcessTokenStreamSink, InlineAgentRunner, LedgerQuotaStore, MemoriesController, MessagesController, ModelsController, SKILL_METADATA, SYSTEM_PROMPT_CONTRIBUTOR_METADATA, SYSTEM_PROMPT_METADATA, Skill, type SkillBody, SkillDiscoveryService, type SkillOptions, SkillsController, SystemPrompt, SystemPromptContributor, type ThreadDefaultAgentReader, type ThreadModelReader, ToolsController, declaredSkillProvider, readAgentMetadata, readAiToolMetadata, readSkillMetadata, resolveSkillsConfig, skillScope };
958
+ interface AnonymousActorOptions {
959
+ /** The cookie holding the browser's anonymous token. Default `agent_anon`. */
960
+ cookieName?: string;
961
+ /** How long a browser keeps its identity (and so its threads). Default 365 days. */
962
+ maxAgeDays?: number;
963
+ /**
964
+ * `SameSite` of the cookie. Default `'lax'` — sent on same-site requests, not on cross-site
965
+ * POSTs. A SPA on another SITE than the API needs `'none'` (which forces `Secure`) plus
966
+ * `credentials: 'include'` on the client.
967
+ */
968
+ sameSite?: 'lax' | 'strict' | 'none';
969
+ /**
970
+ * `Secure` flag. Default `'auto'`: set when the request arrived over HTTPS (`req.secure`, or an
971
+ * `x-forwarded-proto: https` from a proxy).
972
+ */
973
+ secure?: boolean | 'auto';
974
+ /** Cookie path. Default `/`. */
975
+ path?: string;
976
+ /** Roles every anonymous actor carries. Default `['anonymous']`. */
977
+ roles?: string[];
978
+ }
979
+ /**
980
+ * The zero-config identity: every browser is its own anonymous actor, so a login-less chat still
981
+ * keeps each visitor's threads, quota and attachments apart from everyone else's.
982
+ *
983
+ * On a browser's first request the resolver mints 32 random bytes and sets them as an `HttpOnly`
984
+ * cookie (`SameSite=Lax`, `Secure` over HTTPS). The actor id is a SHA-256 digest of that token —
985
+ * `anon:<digest>` — never the token itself, so an id that surfaces anywhere (a thread row, a log, an
986
+ * approval's `decidedBy`) cannot be replayed as the cookie.
987
+ *
988
+ * Why a server-minted cookie and not an id the client makes up and sends in a header: an
989
+ * `HttpOnly` cookie is out of reach of page scripts (an XSS cannot lift it), the server — not the
990
+ * client — decides the value, and it needs no client code at all. There is no signing secret to
991
+ * configure: the token is unguessable on its own, and the digest works the same on every pod and
992
+ * after a restart.
993
+ *
994
+ * Anonymous identities are per browser, not per person: clearing cookies starts over, and there is
995
+ * nothing to recover them with. Require login with an `ActorResolver` that reads your session —
996
+ * `requestUserActorResolver()` for `req.user` (Passport / cookie-session apps).
997
+ */
998
+ declare class AnonymousActorResolver implements ActorResolver {
999
+ private readonly options;
1000
+ private readonly cookieName;
1001
+ private readonly roles;
1002
+ constructor(options?: AnonymousActorOptions);
1003
+ resolve(request: unknown): Actor;
1004
+ private setCookie;
1005
+ }
1006
+
1007
+ /** Turns whatever your auth put on `req.user` into the agent's {@link Actor}. */
1008
+ type RequestUserMapper<TUser = Record<string, unknown>> = (user: TUser, req: unknown) => Actor | Promise<Actor>;
1009
+ /**
1010
+ * The default mapping: `id` from `id` / `sub` / `_id` / `userId`, `roles` from `roles` (an array)
1011
+ * or `role` (a string), `tenantRef` from `tenantRef` / `tenantId` / `orgId`.
1012
+ */
1013
+ declare function defaultRequestUserMapper(user: Record<string, unknown>): Actor;
1014
+ /**
1015
+ * Authenticated mode in one line, for apps whose auth already puts the principal on `req.user` —
1016
+ * Passport strategies, `express-session` / cookie-session middleware, a Nest guard that assigns it:
1017
+ *
1018
+ * ```ts
1019
+ * AgentModule.forRoot({ model, actorResolver: requestUserActorResolver() })
1020
+ * ```
1021
+ *
1022
+ * A request without `req.user` is refused with `401` — the agent never invents a caller. Pass a
1023
+ * mapper when your user object is shaped differently:
1024
+ *
1025
+ * ```ts
1026
+ * requestUserActorResolver((user: SessionUser) => ({ id: user.uuid, roles: user.permissions }))
1027
+ * ```
1028
+ */
1029
+ declare function requestUserActorResolver<TUser = Record<string, unknown>>(map?: RequestUserMapper<TUser>): ActorResolver;
1030
+
1031
+ export { AGENT_METADATA, AI_TOOL_METADATA, ATTACHMENT_PAGE_SIZE, Agent, AgentApprovalPortAdapter, AgentDepsFactory, AgentDiscoveryService, AgentModule, AgentModuleAsyncOptions, AgentModuleOptions, type AgentOptions, AgentService, AgentSkillsOptions, AiTool, AiToolDiscoveryService, type AiToolOptions, type AnonymousActorOptions, AnonymousActorResolver, type AttachmentLimits, AttachmentsController, type ChatParams, ConfigController, DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, DEFAULT_MAX_ATTACHMENT_BYTES, type DeclaredSkill, HeaderActorResolver, InProcessTokenStreamSink, InlineAgentRunner, MAX_ATTACHMENTS_PER_MESSAGE, MemoriesController, MessagesController, ModelsController, type RequestUserMapper, type ResolvedAiToolOptions, SKILL_METADATA, SYSTEM_PROMPT_CONTRIBUTOR_METADATA, SYSTEM_PROMPT_METADATA, Skill, type SkillBody, SkillDiscoveryService, type SkillOptions, SkillsController, SystemPrompt, SystemPromptContributor, type ThreadDefaultAgentReader, type ThreadModelReader, ToolsController, attachmentLimits, declaredSkillProvider, defaultRequestUserMapper, readAgentMetadata, readAiToolMetadata, readSkillMetadata, requestUserActorResolver, resolveSkillsConfig, skillScope, toolNameFromClass };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { DynamicModule, OnModuleInit, Type, OnApplicationBootstrap } from '@nestjs/common';
2
- import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, A as AgentDepsFactory, d as AgentSkillsOptions } from './agent-deps.factory-DuTy1UyU.js';
3
- export { e as AGENT_TOOL_BRAND, f as AgentAttachmentsOptions, g as AgentDeps, h as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, L as LedgerQuotaProvider, Q as QuotaLimits, i as QuotaWindowLimits, j as delegateToolName, p as provideAgentTool, k as provideAgentTools, u as utcDay } from './agent-deps.factory-DuTy1UyU.js';
4
- import { AgentRunner, AgentStore, QuotaStore, AttachmentStagingStore, ModelCatalog, QuotaProvider, Actor, QuotaReport, ModelCatalogView, AttachmentRef, PageContext, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, MessageFeedbackValue, MessageFeedback, StagedAttachment, QuotaView, AgentApprovalPort, ActorResolver, MessageAttachment, ToolPresentation, AgentHistoryWindow, AgentIntake, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunInput, TokenStreamSink, SinkWriter, QuotaState } from '@dudousxd/nestjs-agent-core';
1
+ import { DynamicModule, Type, OnApplicationBootstrap, OnModuleInit } from '@nestjs/common';
2
+ import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, A as AgentDepsFactory, d as AgentSkillsOptions } from './agent-deps.factory-pqv_qTmJ.js';
3
+ export { e as AGENT_TOOL_BRAND, f as AgentAttachmentsOptions, g as AgentDeps, h as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, L as LedgerQuotaProvider, Q as QuotaLimits, i as QuotaWindowLimits, j as delegateToolName, p as provideAgentTool, k as provideAgentTools, u as utcDay } from './agent-deps.factory-pqv_qTmJ.js';
4
+ import { AgentRunner, AgentStore, AttachmentStagingStore, ModelCatalog, QuotaProvider, Actor, QuotaReport, ModelCatalogView, AttachmentRef, PageContext, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, MessageFeedbackValue, MessageFeedback, StagedAttachment, AgentApprovalPort, ActorResolver, MessageAttachment, AgentAttachmentConfig, AgentClientConfig, ToolPresentation, AgentHistoryWindow, AgentIntake, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, ToolCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunInput, TokenStreamSink, SinkWriter } from '@dudousxd/nestjs-agent-core';
5
5
  export * from '@dudousxd/nestjs-agent-core';
6
6
  import { Request } from 'express';
7
7
  import { StandardSchemaV1 } from '@standard-schema/spec';
@@ -56,12 +56,11 @@ declare class AgentService {
56
56
  private readonly runner;
57
57
  private readonly store;
58
58
  private readonly deps;
59
- private readonly quota;
60
59
  private readonly staging?;
61
60
  private readonly models?;
62
61
  private readonly quotaProvider?;
63
62
  private readonly options?;
64
- constructor(runner: AgentRunner, store: AgentStore, deps: AgentDepsFactory, quota: QuotaStore | undefined, staging?: AttachmentStagingStore | undefined, models?: ModelCatalog | undefined, quotaProvider?: QuotaProvider | undefined, options?: Pick<AgentModuleOptions, "quotaProvider" | "quotaLimits"> | undefined);
63
+ constructor(runner: AgentRunner, store: AgentStore, deps: AgentDepsFactory, staging?: AttachmentStagingStore | undefined, models?: ModelCatalog | undefined, quotaProvider?: QuotaProvider | undefined, options?: Pick<AgentModuleOptions, "quota"> | undefined);
65
64
  /**
66
65
  * The actor's budget across windows (`GET <base>/quota`) — the bound {@link QuotaProvider}'s
67
66
  * report. Without one (a service built outside the module), a day window from the ledger.
@@ -69,8 +68,7 @@ declare class AgentService {
69
68
  quotaReport(actor: Actor): Promise<QuotaReport>;
70
69
  /**
71
70
  * Refuse a turn the actor's budget no longer covers — only when the host configured one
72
- * (`quotaProvider` or `quotaLimits`): the default report is informational, and the daily token
73
- * quota the loop enforces keeps working as it always has.
71
+ * (`quota`): the default report is informational.
74
72
  */
75
73
  private assertWithinQuota;
76
74
  /**
@@ -182,6 +180,13 @@ declare class AgentService {
182
180
  private threadDefaultAgent;
183
181
  private toSummaryView;
184
182
  private toDetailView;
183
+ /**
184
+ * Re-mint each replayed attachment's url from the staging store, by `mediaId`: the url persisted
185
+ * with the message was minted for THAT turn (a presigned url with an expiry), so an old turn would
186
+ * otherwise show a dead link. The store's own access check applies — an attachment it will not
187
+ * resolve for this actor keeps the url it was stored with.
188
+ */
189
+ private freshAttachmentUrls;
185
190
  promoteThread(actor: Actor, threadId: string): Promise<void>;
186
191
  /**
187
192
  * Drop a message and everything after it — the "edit and resend" / "delete from here" primitive.
@@ -233,12 +238,6 @@ declare class AgentService {
233
238
  * otherwise render identically — as a clean file list, and as a sweep that collects nothing.
234
239
  */
235
240
  private attachmentInventory;
236
- /**
237
- * The day's usage for the badge: tokens + summed USD cost from the store, and the configured
238
- * limit from the quota store (null → unlimited). `withinLimit` comes from the quota store so it
239
- * can never drift from what enforcement uses.
240
- */
241
- quotaToday(actorRef: string): Promise<QuotaView>;
242
241
  /**
243
242
  * Authorization seam for thread-scoped endpoints: the caller must own the thread. A missing
244
243
  * thread is a 404; someone else's thread is a 403 — one actor never touches another's thread.
@@ -313,8 +312,6 @@ declare class AgentApprovalPortAdapter implements AgentApprovalPort {
313
312
  }): Promise<void>;
314
313
  }
315
314
 
316
- /** Default per-file size cap when `AgentModuleOptions.attachments.maxBytes` is omitted (20 MiB). */
317
- declare const DEFAULT_MAX_ATTACHMENT_BYTES: number;
318
315
  /**
319
316
  * How many entries `GET /agent/attachments` returns. Fixed rather than caller-supplied: this route
320
317
  * exists so a composer can show the files someone has uploaded, and a host that needs real paging
@@ -322,13 +319,8 @@ declare const DEFAULT_MAX_ATTACHMENT_BYTES: number;
322
319
  */
323
320
  declare const ATTACHMENT_PAGE_SIZE = 50;
324
321
  /**
325
- * Default allowlist: what multimodal model providers commonly accept as native image/file parts.
326
- */
327
- declare const DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES: readonly string[];
328
- /**
329
- * Optional `POST /agent/attachments` upload surface — mounted only when `AgentModuleOptions
330
- * .attachments.upload` (or the async config's `attachmentsUpload`) is `true` (see `agent.module.ts`).
331
- * Multipart single file field `'file'`, buffered in memory (no disk write — `FileInterceptor`'s
322
+ * `POST <base>/attachments` — the multipart upload route, live whenever an
323
+ * `AGENT_ATTACHMENT_STAGING` provider is bound (`501` otherwise). Multipart single file field `'file'`, buffered in memory (no disk write — `FileInterceptor`'s
332
324
  * default storage) up to {@link HARD_MAX_ATTACHMENT_BYTES}, then validated against the configured
333
325
  * size cap + content-type allowlist and handed to the bound {@link AttachmentStagingStore} to
334
326
  * persist and turn into a model-fetchable URL. Both size gates answer `413`: the ceiling aborts the
@@ -339,19 +331,12 @@ declare const DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES: readonly string[];
339
331
  * host deleting bytes, so it stays an in-process call (`AgentService.collectableAttachments`)
340
332
  * rather than something reachable with a session cookie.
341
333
  */
342
- declare class AttachmentsController implements OnModuleInit {
334
+ declare class AttachmentsController {
343
335
  private readonly options;
344
336
  private readonly actorResolver;
345
337
  private readonly agents;
346
338
  private readonly staging;
347
339
  constructor(options: AgentModuleOptions, actorResolver: ActorResolver, agents: AgentService, staging: AttachmentStagingStore | undefined);
348
- /**
349
- * `attachments.upload: true` is what mounts this controller at all (see `agent.module.ts`), so by
350
- * the time an instance exists the host clearly intends uploads to work. Fail boot loudly rather
351
- * than mounting a controller that would 501 on every single request because nobody bound
352
- * `AGENT_ATTACHMENT_STAGING`.
353
- */
354
- onModuleInit(): void;
355
340
  /**
356
341
  * The caller's own staged files, newest first — the one question `GET /agent/threads/:id` cannot
357
342
  * answer, since an upload that was never sent belongs to no thread.
@@ -365,21 +350,52 @@ declare class AttachmentsController implements OnModuleInit {
365
350
  upload(req: Request, file: Express.Multer.File | undefined): Promise<MessageAttachment>;
366
351
  }
367
352
 
353
+ /** Default per-file size cap (20 MiB). */
354
+ declare const DEFAULT_MAX_ATTACHMENT_BYTES: number;
355
+ /** Default allowlist: what multimodal model providers commonly accept as native image/file parts. */
356
+ declare const DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES: readonly string[];
357
+ /** How many attachments one message may name. */
358
+ declare const MAX_ATTACHMENTS_PER_MESSAGE = 10;
359
+ /** The attachment rules in force — `AgentAttachmentConfig`, as `GET <base>/config` serves it. */
360
+ type AttachmentLimits = AgentAttachmentConfig;
361
+ /**
362
+ * One source for attachment limits: the staging store's own `describe()` when it declares them
363
+ * (`AgentMediaAttachmentsModule` does), else `AgentModule`'s `attachments` option, else the defaults.
364
+ */
365
+ declare function attachmentLimits(options: Pick<AgentModuleOptions, 'attachments'> | undefined, staging: AttachmentStagingStore | undefined): AttachmentLimits;
366
+
367
+ declare class ConfigController {
368
+ private readonly options;
369
+ private readonly actorResolver;
370
+ private readonly staging;
371
+ private readonly models;
372
+ constructor(options: AgentModuleOptions, actorResolver: ActorResolver, staging: AttachmentStagingStore | undefined, models: ModelCatalog | undefined);
373
+ config(req: Request): Promise<AgentClientConfig>;
374
+ }
375
+
368
376
  declare const AI_TOOL_METADATA: unique symbol;
369
377
  interface AiToolOptions {
370
- name: string;
371
378
  /**
372
- * `read` auto-executes; `action` requires HITL approval. (Core's `ToolKind` also has `agent`
373
- * for delegation, but that kind is synthesized from `delegatesTo` — never authored here.)
379
+ * What the model calls it. Omit → the class name, camelCased, with a trailing `Tool` dropped
380
+ * (`GetWeatherTool` → `getWeather`).
374
381
  */
375
- kind: 'read' | 'action';
382
+ name?: string;
383
+ /**
384
+ * `read` (the default) auto-executes; `action` requires HITL approval. (Core's `ToolKind` also
385
+ * has `agent` for delegation, but that kind is synthesized from `delegatesTo` — never authored
386
+ * here.)
387
+ */
388
+ kind?: 'read' | 'action';
376
389
  description: string;
377
390
  /**
378
391
  * Input schema as a [Standard Schema](https://standardschema.dev) — Zod, Valibot, or ArkType.
379
392
  * Validated before the handler runs.
380
393
  */
381
394
  input: StandardSchemaV1;
382
- /** Roles allowed to invoke. Omit to inherit the module's default roles. */
395
+ /**
396
+ * Roles allowed to invoke. Omit → the module's `defaultRoles`, which by default restrict nobody:
397
+ * any resolved actor — an anonymous visitor included, when no `actorResolver` is configured.
398
+ */
383
399
  roles?: string[];
384
400
  /**
385
401
  * Whether this tool exists in this deployment. `false` — or a predicate returning `false`, which
@@ -420,13 +436,21 @@ interface AiToolOptions {
420
436
  */
421
437
  terminal?: boolean;
422
438
  }
439
+ /** {@link AiToolOptions} with the defaults applied — what discovery reads. */
440
+ interface ResolvedAiToolOptions extends AiToolOptions {
441
+ name: string;
442
+ kind: 'read' | 'action';
443
+ }
444
+ /** `GetWeatherTool` → `getWeather`; `SQLQueryTool` → `sqlQuery`. */
445
+ declare function toolNameFromClass(className: string): string;
423
446
  /**
424
447
  * Marks a provider class as an AI tool. The class must implement `execute(input, ctx)`.
425
448
  * `AiToolDiscoveryService` registers every `@AiTool` provider into the `ToolRegistry` at boot.
449
+ * Only `description` and `input` are required:
426
450
  *
427
451
  * ```ts
428
- * @AiTool({ name: 'getWeather', kind: 'read', description: '...', input: z.object({ city: z.string() }) })
429
- * class GetWeatherTool implements ToolHandler<{ city: string }> {
452
+ * @AiTool({ description: 'Current weather for a city', input: z.object({ city: z.string() }) })
453
+ * class GetWeatherTool implements ToolHandler<{ city: string }> { // name: getWeather, kind: read
430
454
  * async execute(input, ctx) { return { tempC: 21 }; }
431
455
  * }
432
456
  * ```
@@ -444,7 +468,7 @@ interface AiToolOptions {
444
468
  * ```
445
469
  */
446
470
  declare function AiTool(options: AiToolOptions): ClassDecorator;
447
- declare function readAiToolMetadata(target: object): AiToolOptions | undefined;
471
+ declare function readAiToolMetadata(target: object): ResolvedAiToolOptions | undefined;
448
472
 
449
473
  /**
450
474
  * Declares a class as an agent. The class is discovered at boot (via `DiscoveryService`) and
@@ -915,21 +939,6 @@ declare class InProcessTokenStreamSink implements TokenStreamSink {
915
939
  close(runId: string): void;
916
940
  }
917
941
 
918
- /**
919
- * A production {@link QuotaStore} backed by the persisted usage ledger. `check` reads the actor's
920
- * tokens-so-far for the day straight from {@link AgentStore.quotaToday} and compares it to a fixed
921
- * daily limit; `bump` is a deliberate no-op because the loop's `recordUsage` already writes every
922
- * turn's tokens to that same ledger. One source of truth — so enforcement and what the dashboard
923
- * reports can never drift, and it works across replicas with no extra shared state.
924
- */
925
- declare class LedgerQuotaStore implements QuotaStore {
926
- private readonly store;
927
- private readonly limitTokens;
928
- constructor(store: AgentStore, limitTokens: number);
929
- check(actorRef: string, day: string): Promise<QuotaState>;
930
- bump(_actorRef: string, _day: string, _tokens: number): Promise<void>;
931
- }
932
-
933
942
  /**
934
943
  * A development / gateway {@link ActorResolver} that trusts request headers:
935
944
  * `x-actor-id` (required), `x-actor-role` (comma-separated → `roles`), `x-tenant-ref`.
@@ -946,4 +955,77 @@ declare class HeaderActorResolver implements ActorResolver {
946
955
  resolve(req: unknown): Actor;
947
956
  }
948
957
 
949
- export { AGENT_METADATA, AI_TOOL_METADATA, ATTACHMENT_PAGE_SIZE, Agent, AgentApprovalPortAdapter, AgentDepsFactory, AgentDiscoveryService, AgentModule, AgentModuleAsyncOptions, AgentModuleOptions, type AgentOptions, AgentService, AgentSkillsOptions, AiTool, AiToolDiscoveryService, type AiToolOptions, AttachmentsController, type ChatParams, DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, DEFAULT_MAX_ATTACHMENT_BYTES, type DeclaredSkill, HeaderActorResolver, InProcessTokenStreamSink, InlineAgentRunner, LedgerQuotaStore, MemoriesController, MessagesController, ModelsController, SKILL_METADATA, SYSTEM_PROMPT_CONTRIBUTOR_METADATA, SYSTEM_PROMPT_METADATA, Skill, type SkillBody, SkillDiscoveryService, type SkillOptions, SkillsController, SystemPrompt, SystemPromptContributor, type ThreadDefaultAgentReader, type ThreadModelReader, ToolsController, declaredSkillProvider, readAgentMetadata, readAiToolMetadata, readSkillMetadata, resolveSkillsConfig, skillScope };
958
+ interface AnonymousActorOptions {
959
+ /** The cookie holding the browser's anonymous token. Default `agent_anon`. */
960
+ cookieName?: string;
961
+ /** How long a browser keeps its identity (and so its threads). Default 365 days. */
962
+ maxAgeDays?: number;
963
+ /**
964
+ * `SameSite` of the cookie. Default `'lax'` — sent on same-site requests, not on cross-site
965
+ * POSTs. A SPA on another SITE than the API needs `'none'` (which forces `Secure`) plus
966
+ * `credentials: 'include'` on the client.
967
+ */
968
+ sameSite?: 'lax' | 'strict' | 'none';
969
+ /**
970
+ * `Secure` flag. Default `'auto'`: set when the request arrived over HTTPS (`req.secure`, or an
971
+ * `x-forwarded-proto: https` from a proxy).
972
+ */
973
+ secure?: boolean | 'auto';
974
+ /** Cookie path. Default `/`. */
975
+ path?: string;
976
+ /** Roles every anonymous actor carries. Default `['anonymous']`. */
977
+ roles?: string[];
978
+ }
979
+ /**
980
+ * The zero-config identity: every browser is its own anonymous actor, so a login-less chat still
981
+ * keeps each visitor's threads, quota and attachments apart from everyone else's.
982
+ *
983
+ * On a browser's first request the resolver mints 32 random bytes and sets them as an `HttpOnly`
984
+ * cookie (`SameSite=Lax`, `Secure` over HTTPS). The actor id is a SHA-256 digest of that token —
985
+ * `anon:<digest>` — never the token itself, so an id that surfaces anywhere (a thread row, a log, an
986
+ * approval's `decidedBy`) cannot be replayed as the cookie.
987
+ *
988
+ * Why a server-minted cookie and not an id the client makes up and sends in a header: an
989
+ * `HttpOnly` cookie is out of reach of page scripts (an XSS cannot lift it), the server — not the
990
+ * client — decides the value, and it needs no client code at all. There is no signing secret to
991
+ * configure: the token is unguessable on its own, and the digest works the same on every pod and
992
+ * after a restart.
993
+ *
994
+ * Anonymous identities are per browser, not per person: clearing cookies starts over, and there is
995
+ * nothing to recover them with. Require login with an `ActorResolver` that reads your session —
996
+ * `requestUserActorResolver()` for `req.user` (Passport / cookie-session apps).
997
+ */
998
+ declare class AnonymousActorResolver implements ActorResolver {
999
+ private readonly options;
1000
+ private readonly cookieName;
1001
+ private readonly roles;
1002
+ constructor(options?: AnonymousActorOptions);
1003
+ resolve(request: unknown): Actor;
1004
+ private setCookie;
1005
+ }
1006
+
1007
+ /** Turns whatever your auth put on `req.user` into the agent's {@link Actor}. */
1008
+ type RequestUserMapper<TUser = Record<string, unknown>> = (user: TUser, req: unknown) => Actor | Promise<Actor>;
1009
+ /**
1010
+ * The default mapping: `id` from `id` / `sub` / `_id` / `userId`, `roles` from `roles` (an array)
1011
+ * or `role` (a string), `tenantRef` from `tenantRef` / `tenantId` / `orgId`.
1012
+ */
1013
+ declare function defaultRequestUserMapper(user: Record<string, unknown>): Actor;
1014
+ /**
1015
+ * Authenticated mode in one line, for apps whose auth already puts the principal on `req.user` —
1016
+ * Passport strategies, `express-session` / cookie-session middleware, a Nest guard that assigns it:
1017
+ *
1018
+ * ```ts
1019
+ * AgentModule.forRoot({ model, actorResolver: requestUserActorResolver() })
1020
+ * ```
1021
+ *
1022
+ * A request without `req.user` is refused with `401` — the agent never invents a caller. Pass a
1023
+ * mapper when your user object is shaped differently:
1024
+ *
1025
+ * ```ts
1026
+ * requestUserActorResolver((user: SessionUser) => ({ id: user.uuid, roles: user.permissions }))
1027
+ * ```
1028
+ */
1029
+ declare function requestUserActorResolver<TUser = Record<string, unknown>>(map?: RequestUserMapper<TUser>): ActorResolver;
1030
+
1031
+ export { AGENT_METADATA, AI_TOOL_METADATA, ATTACHMENT_PAGE_SIZE, Agent, AgentApprovalPortAdapter, AgentDepsFactory, AgentDiscoveryService, AgentModule, AgentModuleAsyncOptions, AgentModuleOptions, type AgentOptions, AgentService, AgentSkillsOptions, AiTool, AiToolDiscoveryService, type AiToolOptions, type AnonymousActorOptions, AnonymousActorResolver, type AttachmentLimits, AttachmentsController, type ChatParams, ConfigController, DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, DEFAULT_MAX_ATTACHMENT_BYTES, type DeclaredSkill, HeaderActorResolver, InProcessTokenStreamSink, InlineAgentRunner, MAX_ATTACHMENTS_PER_MESSAGE, MemoriesController, MessagesController, ModelsController, type RequestUserMapper, type ResolvedAiToolOptions, SKILL_METADATA, SYSTEM_PROMPT_CONTRIBUTOR_METADATA, SYSTEM_PROMPT_METADATA, Skill, type SkillBody, SkillDiscoveryService, type SkillOptions, SkillsController, SystemPrompt, SystemPromptContributor, type ThreadDefaultAgentReader, type ThreadModelReader, ToolsController, attachmentLimits, declaredSkillProvider, defaultRequestUserMapper, readAgentMetadata, readAiToolMetadata, readSkillMetadata, requestUserActorResolver, resolveSkillsConfig, skillScope, toolNameFromClass };