@dudousxd/nestjs-agent 1.23.1 → 1.24.1

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.
@@ -1,4 +1,4 @@
1
- import { ResolveToolUiCatalog, ModelProvider, AgentStore, ToolRegistry, RolesPolicy, ApprovalPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, Persona, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, createFunctionalTool, QuotaProvider, QuotaQuery, QuotaReport, ActionApprovalMode, BackgroundActorResolver, TextActionProposalConfig, ModelCatalog, EmptyRoles, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry, PersonaCatalogEntry, ActionProposal, Actor, AttachmentStagingStore, ChatQueueStore, ChatQueueState, AgentRunner, AgentRunInput, AgentStreamEvent, QueuePauseReason } from '@dudousxd/nestjs-agent-core';
1
+ import { ResolveToolUiCatalog, ModelProvider, AgentStore, ToolRegistry, RolesPolicy, ApprovalPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, Persona, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, createFunctionalTool, QuotaProvider, QuotaQuery, QuotaReport, ActionApprovalMode, BackgroundActorResolver, ActionProposal, TextActionProposalConfig, ModelCatalog, EmptyRoles, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry, PersonaCatalogEntry, Actor, AttachmentStagingStore, ChatQueueStore, ChatQueueState, AgentRunner, AgentRunInput, AgentStreamEvent, QueuePauseReason } from '@dudousxd/nestjs-agent-core';
2
2
  import { Catalog } from '@dudousxd/nestjs-agent-core/genui';
3
3
  import { StandardSchemaV1 } from '@standard-schema/spec';
4
4
  import { Provider, InjectionToken, Type, FactoryProvider, CanActivate, ModuleMetadata, OptionalFactoryDependency } from '@nestjs/common';
@@ -296,12 +296,16 @@ interface AgentModuleOptions {
296
296
  * (started with the module unless `surface: 'http'`). `pollIntervalMs` — how often it looks for
297
297
  * queued work, default 1000; `leaseMs` — how long a claim holds before another worker may recover
298
298
  * it, default 30000; `maxConcurrency` — proposals claimed per poll, default 1. All positive
299
- * integers, and `pollIntervalMs` must stay below a third of `leaseMs`.
299
+ * integers, and `pollIntervalMs` must stay below a third of `leaseMs`. `onSettled` runs after each
300
+ * proposal the worker settles (executed or failed), with the stored proposal — its `outcome` and
301
+ * `executionContext.pageContext` included; a module can subscribe the same way through
302
+ * `ActionProposalWorkerService.onSettled`.
300
303
  */
301
304
  actionProposalWorker?: {
302
305
  pollIntervalMs?: number;
303
306
  leaseMs?: number;
304
307
  maxConcurrency?: number;
308
+ onSettled?(proposal: ActionProposal): void | Promise<void>;
305
309
  };
306
310
  /**
307
311
  * The words a chat message must consist of to approve or reject a proposal by text, and what the
@@ -1,4 +1,4 @@
1
- import { ResolveToolUiCatalog, ModelProvider, AgentStore, ToolRegistry, RolesPolicy, ApprovalPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, Persona, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, createFunctionalTool, QuotaProvider, QuotaQuery, QuotaReport, ActionApprovalMode, BackgroundActorResolver, TextActionProposalConfig, ModelCatalog, EmptyRoles, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry, PersonaCatalogEntry, ActionProposal, Actor, AttachmentStagingStore, ChatQueueStore, ChatQueueState, AgentRunner, AgentRunInput, AgentStreamEvent, QueuePauseReason } from '@dudousxd/nestjs-agent-core';
1
+ import { ResolveToolUiCatalog, ModelProvider, AgentStore, ToolRegistry, RolesPolicy, ApprovalPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, Persona, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, createFunctionalTool, QuotaProvider, QuotaQuery, QuotaReport, ActionApprovalMode, BackgroundActorResolver, ActionProposal, TextActionProposalConfig, ModelCatalog, EmptyRoles, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry, PersonaCatalogEntry, Actor, AttachmentStagingStore, ChatQueueStore, ChatQueueState, AgentRunner, AgentRunInput, AgentStreamEvent, QueuePauseReason } from '@dudousxd/nestjs-agent-core';
2
2
  import { Catalog } from '@dudousxd/nestjs-agent-core/genui';
3
3
  import { StandardSchemaV1 } from '@standard-schema/spec';
4
4
  import { Provider, InjectionToken, Type, FactoryProvider, CanActivate, ModuleMetadata, OptionalFactoryDependency } from '@nestjs/common';
@@ -296,12 +296,16 @@ interface AgentModuleOptions {
296
296
  * (started with the module unless `surface: 'http'`). `pollIntervalMs` — how often it looks for
297
297
  * queued work, default 1000; `leaseMs` — how long a claim holds before another worker may recover
298
298
  * it, default 30000; `maxConcurrency` — proposals claimed per poll, default 1. All positive
299
- * integers, and `pollIntervalMs` must stay below a third of `leaseMs`.
299
+ * integers, and `pollIntervalMs` must stay below a third of `leaseMs`. `onSettled` runs after each
300
+ * proposal the worker settles (executed or failed), with the stored proposal — its `outcome` and
301
+ * `executionContext.pageContext` included; a module can subscribe the same way through
302
+ * `ActionProposalWorkerService.onSettled`.
300
303
  */
301
304
  actionProposalWorker?: {
302
305
  pollIntervalMs?: number;
303
306
  leaseMs?: number;
304
307
  maxConcurrency?: number;
308
+ onSettled?(proposal: ActionProposal): void | Promise<void>;
305
309
  };
306
310
  /**
307
311
  * The words a chat message must consist of to approve or reject a proposal by text, and what the
@@ -1749,6 +1749,31 @@ AgentDepsFactory2 = _ts_decorate6([
1749
1749
  var import_nestjs_agent_core16 = require("@dudousxd/nestjs-agent-core");
1750
1750
  var import_common10 = require("@nestjs/common");
1751
1751
 
1752
+ // src/attachment-limits.ts
1753
+ var DEFAULT_MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
1754
+ var DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES = [
1755
+ "image/png",
1756
+ "image/jpeg",
1757
+ "image/gif",
1758
+ "image/webp",
1759
+ "application/pdf",
1760
+ "text/plain",
1761
+ "text/csv"
1762
+ ];
1763
+ var MAX_ATTACHMENTS_PER_MESSAGE = 10;
1764
+ function attachmentLimits(options, staging) {
1765
+ const declared = staging?.describe?.();
1766
+ const own = options?.attachments;
1767
+ return {
1768
+ enabled: staging !== void 0,
1769
+ upload: staging === void 0 ? null : declared?.upload ?? "multipart",
1770
+ maxBytes: declared?.maxBytes ?? own?.maxBytes ?? DEFAULT_MAX_ATTACHMENT_BYTES,
1771
+ allowedContentTypes: declared?.allowedContentTypes ?? own?.allowedContentTypes ?? DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES,
1772
+ maxPerMessage: MAX_ATTACHMENTS_PER_MESSAGE
1773
+ };
1774
+ }
1775
+ __name(attachmentLimits, "attachmentLimits");
1776
+
1752
1777
  // src/proposals/action-proposal.service.ts
1753
1778
  var import_nestjs_agent_core12 = require("@dudousxd/nestjs-agent-core");
1754
1779
  var import_common7 = require("@nestjs/common");
@@ -1905,6 +1930,10 @@ var ActionProposalService = class {
1905
1930
  text: this.reply(result, decision)
1906
1931
  };
1907
1932
  }
1933
+ /** The configured words a text decision is made of (`actionProposalText.vocabulary`). */
1934
+ textVocabulary() {
1935
+ return this.vocabulary;
1936
+ }
1908
1937
  /** The configured reply to a decision the store answered with `result`. */
1909
1938
  reply(result, decision) {
1910
1939
  return (0, import_nestjs_agent_core12.textActionProposalReply)(result, decision, this.replies);
@@ -2531,6 +2560,65 @@ var AgentService = class {
2531
2560
  };
2532
2561
  return this.proposals.handleTextDecision(threadId, actor, text);
2533
2562
  }
2563
+ /** How `action` tools wait for their approval here — `AgentModule.forRoot({ actionApprovalMode })`. */
2564
+ actionApprovalMode() {
2565
+ return this.options?.actionApprovalMode ?? "blocking";
2566
+ }
2567
+ /**
2568
+ * The words a text decision is made of (`actionProposalText.vocabulary`, English by default) — what
2569
+ * a surface without buttons tells the person to reply.
2570
+ */
2571
+ actionProposalVocabulary() {
2572
+ return this.proposals?.textVocabulary() ?? import_nestjs_agent_core16.DEFAULT_TEXT_ACTION_PROPOSAL_VOCABULARY;
2573
+ }
2574
+ /** The configured reply (`actionProposalText.replies`) to a decision the store answered with `result`. */
2575
+ actionProposalReply(result, decision) {
2576
+ return this.proposals?.reply(result, decision) ?? (0, import_nestjs_agent_core16.textActionProposalReply)(result, decision);
2577
+ }
2578
+ /**
2579
+ * The proposals of a thread this actor may see — the thread's own actor, or one the policy lets
2580
+ * decide them; what `GET <base>/threads/:threadId/action-proposals` serves (its first page). `404`
2581
+ * when independent approvals are not on.
2582
+ */
2583
+ async listActionProposals(actor, threadId) {
2584
+ if (this.options?.actionApprovalMode !== "independent" || !this.proposals) {
2585
+ throw new import_common10.NotFoundException("Proposal not found");
2586
+ }
2587
+ return this.proposals.list(threadId, actor);
2588
+ }
2589
+ /**
2590
+ * The attachment rules in force — what `GET <base>/config` serves and `POST <base>/attachments`
2591
+ * enforces. `enabled: false` when no `AGENT_ATTACHMENT_STAGING` provider is bound.
2592
+ */
2593
+ attachmentLimits() {
2594
+ return attachmentLimits(this.options, this.staging);
2595
+ }
2596
+ /**
2597
+ * Stage a file for a later turn, held to the same limits as `POST <base>/attachments` — for a
2598
+ * surface that receives files itself (a messaging webhook). Refuses with `501` when attachments are
2599
+ * off, `415` for a type outside the allowlist and `413` past the size cap; send the returned
2600
+ * `mediaId` as `attachments: [{ mediaId }]`.
2601
+ */
2602
+ async stageAttachment(actor, file) {
2603
+ if (this.staging === void 0) {
2604
+ throw new import_common10.NotImplementedException("Attachments are not configured on this server: bind AGENT_ATTACHMENT_STAGING (e.g. import AgentMediaAttachmentsModule).");
2605
+ }
2606
+ const { allowedContentTypes, maxBytes } = this.attachmentLimits();
2607
+ const contentType = file.contentType.split(";")[0]?.trim().toLowerCase() ?? "";
2608
+ if (!allowedContentTypes.includes(contentType)) {
2609
+ throw new import_common10.UnsupportedMediaTypeException(`content type "${contentType}" is not allowed (allowed: ${allowedContentTypes.join(", ")})`);
2610
+ }
2611
+ if (file.data.byteLength > maxBytes) {
2612
+ throw new import_common10.PayloadTooLargeException(`file exceeds the ${maxBytes}-byte limit`);
2613
+ }
2614
+ return this.staging.stage({
2615
+ data: file.data,
2616
+ filename: file.filename,
2617
+ contentType,
2618
+ sizeBytes: file.data.byteLength,
2619
+ actor
2620
+ });
2621
+ }
2534
2622
  /** The queue service when the bound store can hold a queue, else `undefined`. */
2535
2623
  queueing() {
2536
2624
  return this.queue?.supported === true ? this.queue : void 0;
@@ -3814,33 +3902,6 @@ AgentsController = _ts_decorate11([
3814
3902
  var import_nestjs_agent_core18 = require("@dudousxd/nestjs-agent-core");
3815
3903
  var import_common13 = require("@nestjs/common");
3816
3904
  var import_platform_express = require("@nestjs/platform-express");
3817
-
3818
- // src/attachment-limits.ts
3819
- var DEFAULT_MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
3820
- var DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES = [
3821
- "image/png",
3822
- "image/jpeg",
3823
- "image/gif",
3824
- "image/webp",
3825
- "application/pdf",
3826
- "text/plain",
3827
- "text/csv"
3828
- ];
3829
- var MAX_ATTACHMENTS_PER_MESSAGE = 10;
3830
- function attachmentLimits(options, staging) {
3831
- const declared = staging?.describe?.();
3832
- const own = options?.attachments;
3833
- return {
3834
- enabled: staging !== void 0,
3835
- upload: staging === void 0 ? null : declared?.upload ?? "multipart",
3836
- maxBytes: declared?.maxBytes ?? own?.maxBytes ?? DEFAULT_MAX_ATTACHMENT_BYTES,
3837
- allowedContentTypes: declared?.allowedContentTypes ?? own?.allowedContentTypes ?? DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES,
3838
- maxPerMessage: MAX_ATTACHMENTS_PER_MESSAGE
3839
- };
3840
- }
3841
- __name(attachmentLimits, "attachmentLimits");
3842
-
3843
- // src/controller/attachments.controller.ts
3844
3905
  function _ts_decorate12(decorators, target, key, desc) {
3845
3906
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3846
3907
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -6131,6 +6192,34 @@ var ActionProposalWorkerService = class _ActionProposalWorkerService {
6131
6192
  deps;
6132
6193
  worker;
6133
6194
  logger = new import_common28.Logger(_ActionProposalWorkerService.name);
6195
+ settledListeners = /* @__PURE__ */ new Set();
6196
+ /**
6197
+ * Run `listener` after every proposal this process's worker settles — how a module relays an
6198
+ * outcome somewhere the turn's stream no longer reaches (a text channel's conversation). Next to
6199
+ * `actionProposalWorker.onSettled`; a listener that throws is logged and does not stop the others.
6200
+ * Returns the unsubscribe. Only the replicas that run the worker (not `surface: 'http'`) call it.
6201
+ */
6202
+ onSettled(listener) {
6203
+ this.settledListeners.add(listener);
6204
+ return () => {
6205
+ this.settledListeners.delete(listener);
6206
+ };
6207
+ }
6208
+ async settled(proposal) {
6209
+ const configured = this.options.actionProposalWorker?.onSettled;
6210
+ for (const listener of [
6211
+ ...configured ? [
6212
+ configured
6213
+ ] : [],
6214
+ ...this.settledListeners
6215
+ ]) {
6216
+ try {
6217
+ await listener(proposal);
6218
+ } catch (error) {
6219
+ this.logger.error(error);
6220
+ }
6221
+ }
6222
+ }
6134
6223
  constructor(store, options, deps) {
6135
6224
  this.store = store;
6136
6225
  this.options = options;
@@ -6146,6 +6235,7 @@ var ActionProposalWorkerService = class _ActionProposalWorkerService {
6146
6235
  store: this.store,
6147
6236
  workerId: (0, import_node_crypto3.randomUUID)(),
6148
6237
  ...this.options.actionProposalWorker,
6238
+ onSettled: /* @__PURE__ */ __name((proposal) => this.settled(proposal), "onSettled"),
6149
6239
  executor: new import_nestjs_agent_core35.ActionProposalExecutor({
6150
6240
  resolver,
6151
6241
  resolveExecution: /* @__PURE__ */ __name(async (proposal, actor) => {