@dudousxd/nestjs-agent 0.11.0 → 0.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.js CHANGED
@@ -2,12 +2,12 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // src/agent.module.ts
5
- import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER6, AGENT_APPROVAL_PORT, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY4, AGENT_DURABLE_RUNNER, AGENT_MODEL as AGENT_MODEL2, AGENT_OPTIONS as AGENT_OPTIONS4, AGENT_PROMPT_CONTRIBUTORS as AGENT_PROMPT_CONTRIBUTORS3, AGENT_QUOTA_STORE as AGENT_QUOTA_STORE3, AGENT_REGISTRY as AGENT_REGISTRY5, AGENT_ROLES_POLICY as AGENT_ROLES_POLICY2, AGENT_RUNNER as AGENT_RUNNER2, AGENT_SINK as AGENT_SINK2, AGENT_STORE as AGENT_STORE4, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY3, AgentRegistry as AgentRegistry5, DefaultRolesPolicy, ToolRegistry as ToolRegistry3 } from "@dudousxd/nestjs-agent-core";
5
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER8, AGENT_APPROVAL_PORT, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY4, AGENT_DURABLE_RUNNER, AGENT_MEMORY as AGENT_MEMORY3, AGENT_MODEL as AGENT_MODEL2, AGENT_OPTIONS as AGENT_OPTIONS4, AGENT_PROMPT_CONTRIBUTORS as AGENT_PROMPT_CONTRIBUTORS3, AGENT_QUOTA_STORE as AGENT_QUOTA_STORE3, AGENT_REGISTRY as AGENT_REGISTRY5, AGENT_ROLES_POLICY as AGENT_ROLES_POLICY2, AGENT_RUNNER as AGENT_RUNNER2, AGENT_SINK as AGENT_SINK2, AGENT_SKILLS as AGENT_SKILLS3, AGENT_SKILL_SOURCES as AGENT_SKILL_SOURCES2, AGENT_STORE as AGENT_STORE4, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY3, AgentRegistry as AgentRegistry5, DefaultRolesPolicy, ToolRegistry as ToolRegistry3 } from "@dudousxd/nestjs-agent-core";
6
6
  import { Global, Module } from "@nestjs/common";
7
7
  import { DiscoveryModule, RouterModule } from "@nestjs/core";
8
8
 
9
9
  // src/agent-deps.factory.ts
10
- import { AGENT_MODEL, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_QUOTA_STORE, AGENT_REGISTRY, AGENT_ROLES_POLICY, AGENT_SINK, AGENT_STORE, AGENT_TOOL_REGISTRY, AgentRegistry as AgentRegistry2, ToolRegistry as ToolRegistry2 } from "@dudousxd/nestjs-agent-core";
10
+ import { AGENT_MEMORY, AGENT_MODEL, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_QUOTA_STORE, AGENT_REGISTRY, AGENT_ROLES_POLICY, AGENT_SINK, AGENT_SKILLS, AGENT_STORE, AGENT_TOOL_REGISTRY, AgentRegistry as AgentRegistry2, ToolRegistry as ToolRegistry2, normalizeDelegation, summarizeWithModel, windowHistory } from "@dudousxd/nestjs-agent-core";
11
11
  import { AGENT_OPTIONS } from "@dudousxd/nestjs-agent-core";
12
12
  import { Inject, Injectable, Optional } from "@nestjs/common";
13
13
  function _ts_decorate(decorators, target, key, desc) {
@@ -27,8 +27,9 @@ function _ts_param(paramIndex, decorator) {
27
27
  };
28
28
  }
29
29
  __name(_ts_param, "_ts_param");
30
- function delegateToolName(target) {
31
- return `ask_${target.replace(/[^a-zA-Z0-9]+/g, "_")}`;
30
+ function delegateToolName(args) {
31
+ const slug = args.target.replace(/[^a-zA-Z0-9]+/g, "_");
32
+ return args.detached === true ? `start_${slug}` : `ask_${slug}`;
32
33
  }
33
34
  __name(delegateToolName, "delegateToolName");
34
35
  var AgentDepsFactory2 = class {
@@ -45,7 +46,9 @@ var AgentDepsFactory2 = class {
45
46
  promptContributors;
46
47
  quota;
47
48
  pricingStore;
48
- constructor(options, model, store, sink, rolesPolicy, registry, agents, promptContributors, quota, pricingStore) {
49
+ skills;
50
+ memory;
51
+ constructor(options, model, store, sink, rolesPolicy, registry, agents, promptContributors, quota, pricingStore, skills, memory) {
49
52
  this.options = options;
50
53
  this.model = model;
51
54
  this.store = store;
@@ -56,6 +59,16 @@ var AgentDepsFactory2 = class {
56
59
  this.promptContributors = promptContributors;
57
60
  this.quota = quota;
58
61
  this.pricingStore = pricingStore;
62
+ this.skills = skills;
63
+ this.memory = memory;
64
+ }
65
+ /** The turn's memory seam, shared with the read-back endpoint. Undefined → memory is not configured. */
66
+ memoryConfig() {
67
+ return this.memory;
68
+ }
69
+ /** The turn's skills seam, shared with the listing endpoint. Undefined → skills are not configured. */
70
+ skillsConfig() {
71
+ return this.skills;
59
72
  }
60
73
  /**
61
74
  * The agent a turn uses when the caller names none: the explicit `defaultAgent` option, else the
@@ -72,7 +85,13 @@ var AgentDepsFactory2 = class {
72
85
  if (definition === void 0) {
73
86
  return void 0;
74
87
  }
75
- const delegated = (definition.delegatesTo ?? []).map(delegateToolName);
88
+ const delegated = (definition.delegatesTo ?? []).map((edge) => {
89
+ const { agent, detached } = normalizeDelegation(edge);
90
+ return delegateToolName({
91
+ target: agent,
92
+ detached
93
+ });
94
+ });
76
95
  if (definition.tools === void 0 && delegated.length === 0) {
77
96
  return void 0;
78
97
  }
@@ -87,6 +106,8 @@ var AgentDepsFactory2 = class {
87
106
  const toolAllowList = this.effectiveTools(definition);
88
107
  const followUpsCount = this.followUpsCount();
89
108
  const retrieval = this.options.retrieval;
109
+ const historyPolicy = this.historyPolicy(definition);
110
+ const ask = definition?.ask ?? this.options.ask;
90
111
  return {
91
112
  model: this.model,
92
113
  store: this.store,
@@ -96,6 +117,12 @@ var AgentDepsFactory2 = class {
96
117
  promptContributors: this.promptContributors,
97
118
  systemPrompt: definition?.systemPrompt ?? "You are a helpful assistant.",
98
119
  maxSteps: definition?.maxSteps ?? 8,
120
+ ...definition?.maxDelegationDepth !== void 0 ? {
121
+ maxDelegationDepth: definition.maxDelegationDepth
122
+ } : {},
123
+ ...definition?.maxAgentAppearances !== void 0 ? {
124
+ maxAgentAppearances: definition.maxAgentAppearances
125
+ } : {},
99
126
  ...definition?.modelId !== void 0 ? {
100
127
  modelId: definition.modelId
101
128
  } : {},
@@ -105,6 +132,37 @@ var AgentDepsFactory2 = class {
105
132
  ...this.pricingStore !== void 0 ? {
106
133
  pricingStore: this.pricingStore
107
134
  } : {},
135
+ ...historyPolicy !== void 0 ? {
136
+ historyPolicy
137
+ } : {},
138
+ inputProcessors: this.options.inputProcessors ?? [],
139
+ outputProcessors: this.options.outputProcessors ?? [],
140
+ ...definition?.outputSchema !== void 0 ? {
141
+ outputSchema: definition.outputSchema
142
+ } : {},
143
+ ...definition?.outputRepairAttempts !== void 0 ? {
144
+ outputRepairAttempts: definition.outputRepairAttempts
145
+ } : {},
146
+ ...definition?.intake !== void 0 ? {
147
+ intake: definition.intake
148
+ } : {},
149
+ // Most specific wins, exactly as `historyPolicy` resolves: the agent's own `@Agent({ ask })`,
150
+ // else the module-wide flag. Both are module config, so every process of a deployment agrees.
151
+ ...ask === true ? {
152
+ ask: true
153
+ } : {},
154
+ // Module-wide, never per-agent: a skill is a procedure, not a persona, and an agent that could
155
+ // opt out of one would make the catalog depend on which agent a turn happened to select — a
156
+ // per-turn fact the worker re-deriving the tool list cannot see.
157
+ ...this.skills !== void 0 ? {
158
+ skills: this.skills
159
+ } : {},
160
+ // Module-wide, never per-agent, for the same reason skills are: which memories a turn carries
161
+ // cannot depend on which persona answered it, or the worker re-deriving a dispatched turn's
162
+ // tool list would disagree with the loop about whether `remember` was offered.
163
+ ...this.memory !== void 0 ? {
164
+ memory: this.memory
165
+ } : {},
108
166
  ...toolAllowList !== void 0 ? {
109
167
  toolAllowList
110
168
  } : {},
@@ -125,6 +183,36 @@ var AgentDepsFactory2 = class {
125
183
  } : {}
126
184
  };
127
185
  }
186
+ /**
187
+ * The ceiling on how much of a thread this agent's turns carry, most specific wins: the agent's
188
+ * own `@Agent({ history })`, else a module-wide custom `historyPolicy`, else the module-wide
189
+ * `history` window. Undefined → unbounded, the behaviour of a module that configures none.
190
+ */
191
+ historyPolicy(definition) {
192
+ if (definition?.history !== void 0) {
193
+ return this.window(definition.history);
194
+ }
195
+ if (this.options.historyPolicy !== void 0) {
196
+ return this.options.historyPolicy;
197
+ }
198
+ return this.options.history === void 0 ? void 0 : this.window(this.options.history);
199
+ }
200
+ /** Build the built-in window policy from the declarative `{ maxMessages, maxTokens, summarize }`. */
201
+ window(config) {
202
+ return windowHistory({
203
+ ...config.maxMessages !== void 0 ? {
204
+ maxMessages: config.maxMessages
205
+ } : {},
206
+ ...config.maxTokens !== void 0 ? {
207
+ maxTokens: config.maxTokens
208
+ } : {},
209
+ // The shared module model, not a per-agent one: an agent's `modelId` is an accounting label,
210
+ // and only one provider is ever bound.
211
+ ...config.summarize === true ? {
212
+ summarize: summarizeWithModel(this.model)
213
+ } : {}
214
+ });
215
+ }
128
216
  /** Normalize the `followUps` option (`true` → 3, `{ count }` → count) to a number, or undefined. */
129
217
  followUpsCount() {
130
218
  const followUps = this.options.followUps;
@@ -150,6 +238,10 @@ AgentDepsFactory2 = _ts_decorate([
150
238
  _ts_param(8, Inject(AGENT_QUOTA_STORE)),
151
239
  _ts_param(9, Optional()),
152
240
  _ts_param(9, Inject(AGENT_PRICING_STORE)),
241
+ _ts_param(10, Optional()),
242
+ _ts_param(10, Inject(AGENT_SKILLS)),
243
+ _ts_param(11, Optional()),
244
+ _ts_param(11, Inject(AGENT_MEMORY)),
153
245
  _ts_metadata("design:type", Function),
154
246
  _ts_metadata("design:paramtypes", [
155
247
  typeof AgentModuleOptions === "undefined" ? Object : AgentModuleOptions,
@@ -161,13 +253,15 @@ AgentDepsFactory2 = _ts_decorate([
161
253
  typeof AgentRegistry2 === "undefined" ? Object : AgentRegistry2,
162
254
  Array,
163
255
  Object,
256
+ Object,
257
+ Object,
164
258
  Object
165
259
  ])
166
260
  ], AgentDepsFactory2);
167
261
 
168
262
  // src/agent.service.ts
169
- import { AGENT_DEPS_FACTORY, AGENT_QUOTA_STORE as AGENT_QUOTA_STORE2, AGENT_RUNNER, AGENT_STORE as AGENT_STORE2 } from "@dudousxd/nestjs-agent-core";
170
- import { BadRequestException, ForbiddenException, Inject as Inject2, Injectable as Injectable2, NotFoundException, NotImplementedException } from "@nestjs/common";
263
+ import { AGENT_ATTACHMENT_STAGING, AGENT_DEPS_FACTORY, AGENT_QUOTA_STORE as AGENT_QUOTA_STORE2, AGENT_RUNNER, AGENT_STORE as AGENT_STORE2 } from "@dudousxd/nestjs-agent-core";
264
+ import { BadRequestException, ForbiddenException, Inject as Inject2, Injectable as Injectable2, NotFoundException, NotImplementedException, Optional as Optional2 } from "@nestjs/common";
171
265
 
172
266
  // src/agent-deps.ts
173
267
  function utcDay(date = /* @__PURE__ */ new Date()) {
@@ -211,14 +305,17 @@ var AgentService = class {
211
305
  store;
212
306
  deps;
213
307
  quota;
214
- constructor(runner, store, deps, quota) {
308
+ staging;
309
+ constructor(runner, store, deps, quota, staging) {
215
310
  this.runner = runner;
216
311
  this.store = store;
217
312
  this.deps = deps;
218
313
  this.quota = quota;
314
+ this.staging = staging;
219
315
  }
220
316
  async chat(params) {
221
317
  const agentName = await this.resolveAgentName(params.agentName, params.threadId);
318
+ const attachments = await this.resolveAttachments(params.actor, params.attachments ?? []);
222
319
  let threadId = params.threadId;
223
320
  if (threadId === void 0) {
224
321
  if (params.regenerate === true) {
@@ -243,8 +340,8 @@ var AgentService = class {
243
340
  ...params.regenerate === true ? {
244
341
  regenerate: true
245
342
  } : {},
246
- ...params.attachments !== void 0 ? {
247
- attachments: params.attachments
343
+ ...attachments.length > 0 ? {
344
+ attachments
248
345
  } : {},
249
346
  ...params.pageContext !== void 0 ? {
250
347
  pageContext: params.pageContext
@@ -257,18 +354,66 @@ var AgentService = class {
257
354
  threadId
258
355
  };
259
356
  }
357
+ /**
358
+ * Rebuild this turn's attachments from the staging store, keyed by the ids the caller claimed.
359
+ * The model provider fetches {@link MessageAttachment.url} verbatim, so that url is only ever
360
+ * allowed to come from the host's own store — a caller that could name it could point the server
361
+ * at anything it can reach (cloud metadata, internal services) and read the response back out of
362
+ * the model's answer.
363
+ *
364
+ * No store bound means no way to tell an id the actor owns from one they don't, so a turn simply
365
+ * cannot carry attachments: 501 rather than trusting the request.
366
+ */
367
+ async resolveAttachments(actor, refs) {
368
+ if (refs.length === 0) {
369
+ return [];
370
+ }
371
+ const staging = this.staging;
372
+ if (staging === void 0 || typeof staging.resolve !== "function") {
373
+ throw new NotImplementedException("Message attachments require an AGENT_ATTACHMENT_STAGING provider implementing resolve(); attachment urls are never taken from the request.");
374
+ }
375
+ const resolved = [];
376
+ for (const ref of refs) {
377
+ const attachment = await staging.resolve({
378
+ mediaId: ref.mediaId,
379
+ actor
380
+ });
381
+ if (attachment === null) {
382
+ throw new ForbiddenException(`attachment ${ref.mediaId} is not available to this actor`);
383
+ }
384
+ resolved.push(attachment);
385
+ }
386
+ return resolved;
387
+ }
388
+ /**
389
+ * The run's live token stream, WITHOUT an ownership check — for in-process callers that are
390
+ * already authorized (the MCP bridge, a host's own job code), the same split as
391
+ * {@link signalToolCall} vs {@link approve}. Anything reachable from a request must go through
392
+ * {@link subscribeAs} instead: a runId is the only thing standing between a caller and another
393
+ * actor's answer, tool arguments and tool results.
394
+ */
260
395
  subscribe(runId) {
261
396
  return this.deps.forAgent().sink.subscribe(runId);
262
397
  }
263
- async approve(actor, toolCallId) {
264
- await this.assertOwnsToolCall(actor, toolCallId);
265
- return this.signalToolCall(toolCallId, {
398
+ /**
399
+ * {@link subscribe}, gated the way {@link cancel} is: only the actor whose thread is currently
400
+ * streaming this run may read it. A run that has already finished is reported missing rather than
401
+ * replayed — the runner clears the thread's active stream as the run ends, and that field is what
402
+ * ownership is derived from.
403
+ */
404
+ async subscribeAs(actor, runId) {
405
+ await this.assertOwnsActiveStream(actor, runId);
406
+ return this.subscribe(runId);
407
+ }
408
+ async approve(actor, toolCallId2) {
409
+ await this.assertOwnsToolCall(actor, toolCallId2);
410
+ return this.signalToolCall(toolCallId2, {
266
411
  approved: true
267
412
  });
268
413
  }
269
- async reject(actor, toolCallId, reason) {
270
- await this.assertOwnsToolCall(actor, toolCallId);
271
- return this.signalToolCall(toolCallId, {
414
+ async reject(actor, toolCallId2, reason) {
415
+ await this.assertOwnsToolCall(actor, toolCallId2);
416
+ return this.signalToolCall(toolCallId2, {
272
417
  approved: false,
273
418
  ...reason !== void 0 ? {
274
419
  reason
@@ -276,6 +421,34 @@ var AgentService = class {
276
421
  });
277
422
  }
278
423
  /**
424
+ * Answer a parked question set. An omitted question takes the request's own pre-picked default —
425
+ * resolved by the LOOP against the request it already holds, so "the user just submitted" and
426
+ * "the user picked exactly the defaults" persist as the same answers, and no client has to
427
+ * re-send what it was shown.
428
+ */
429
+ async answer(actor, toolCallId2, answers2 = {}) {
430
+ await this.assertOwnsToolCall(actor, toolCallId2);
431
+ const reply = {
432
+ answers: answers2,
433
+ answeredByRef: actor.id
434
+ };
435
+ return this.signalToolCall(toolCallId2, reply);
436
+ }
437
+ /**
438
+ * Decline to answer and let the agent proceed on its own assumptions. NOT the same as confirming
439
+ * them: the run records this as a rejection, so a reader auditing what the agent was told can
440
+ * tell a choice the user made from one they refused to make.
441
+ */
442
+ async skip(actor, toolCallId2) {
443
+ await this.assertOwnsToolCall(actor, toolCallId2);
444
+ const reply = {
445
+ answers: {},
446
+ skipped: true,
447
+ answeredByRef: actor.id
448
+ };
449
+ return this.signalToolCall(toolCallId2, reply);
450
+ }
451
+ /**
279
452
  * The decision core behind approve/reject, WITHOUT an ownership check: resolves the run
280
453
  * currently awaiting `toolCallId` and signals it. `approve`/`reject` above call this after their
281
454
  * own `assertOwnsToolCall` gate; the console `AgentApprovalPort` adapter (bound by `AgentModule`)
@@ -283,9 +456,9 @@ var AgentService = class {
283
456
  * re-deriving thread ownership here would reject a legitimate operator decision (an operator is
284
457
  * not the thread's own actor).
285
458
  */
286
- async signalToolCall(toolCallId, decision) {
287
- const runId = await this.resolveRunForToolCall(toolCallId);
288
- return this.runner.signal(runId, toolCallId, decision);
459
+ async signalToolCall(toolCallId2, reply) {
460
+ const runId = await this.resolveRunForToolCall(toolCallId2);
461
+ return this.runner.signal(runId, toolCallId2, reply);
289
462
  }
290
463
  async cancel(actor, runId) {
291
464
  await this.assertOwnsActiveStream(actor, runId);
@@ -352,20 +525,33 @@ var AgentService = class {
352
525
  /**
353
526
  * Agent-selection precedence for a `chat()` call with no explicit `agentName`: the thread's own
354
527
  * `defaultAgent` (if the thread exists and one is set) wins over the module's configured default.
355
- * Skips the thread lookup entirely when the caller already named an agent or there's no thread yet.
528
+ * Skips the store entirely when the caller already named an agent or there's no thread yet.
529
+ *
530
+ * What this needs is one nullable scalar, so it asks for one. `getThread` returns the entire
531
+ * transcript — every message, every persisted tool output — and this discards all of it; on a
532
+ * long thread with large tool results that is hundreds of kilobytes read per turn, outside the
533
+ * workflow, for a string. A store predating {@link ThreadDefaultAgentReader} still answers, via
534
+ * the full read.
356
535
  */
357
536
  async resolveAgentName(agentName, threadId) {
358
537
  if (agentName !== void 0) {
359
538
  return agentName;
360
539
  }
361
540
  if (threadId !== void 0) {
362
- const thread = await this.store.getThread(threadId);
363
- if (thread?.defaultAgent !== void 0 && thread.defaultAgent !== null) {
364
- return thread.defaultAgent;
541
+ const threadDefault = await this.threadDefaultAgent(threadId);
542
+ if (threadDefault !== null) {
543
+ return threadDefault;
365
544
  }
366
545
  }
367
546
  return this.deps.defaultAgentName();
368
547
  }
548
+ async threadDefaultAgent(threadId) {
549
+ const projecting = this.store;
550
+ if (typeof projecting.defaultAgentForThread === "function") {
551
+ return projecting.defaultAgentForThread(threadId);
552
+ }
553
+ return (await this.store.getThread(threadId))?.defaultAgent ?? null;
554
+ }
369
555
  async toSummaryView(thread) {
370
556
  return {
371
557
  ...thread,
@@ -397,6 +583,67 @@ var AgentService = class {
397
583
  return this.store.truncateFrom(threadId, messageId);
398
584
  }
399
585
  /**
586
+ * Every file this actor has staged, newest first — across threads, including uploads that were
587
+ * never sent, which nothing else on this surface can see.
588
+ *
589
+ * Metadata only. A url is minted per turn by `resolve` so it can be short-lived, and handing one
590
+ * back per entry would undo that for the price of rendering a list.
591
+ */
592
+ async listAttachments(actor, options = {}) {
593
+ const list = this.attachmentInventory();
594
+ return list({
595
+ actor,
596
+ ...options.limit !== void 0 ? {
597
+ limit: options.limit
598
+ } : {}
599
+ });
600
+ }
601
+ /**
602
+ * This actor's staged media that no live message references and that is old enough not to be an
603
+ * upload in flight — the candidate set for a sweep. Returns them; never deletes them. The bytes
604
+ * are the host's, and so is the decision.
605
+ *
606
+ * `olderThan` is required and has no default. How long a composer may sit open with a file
607
+ * attached is the host's knowledge, and a library-chosen grace period would eventually delete a
608
+ * file someone was about to send. The staging store is asked to apply it, and the result is
609
+ * filtered again here: a store that ignores the hint would otherwise turn this into exactly that
610
+ * bug, silently.
611
+ *
612
+ * Both halves must be answerable or this refuses. An unanswerable reference query means "cannot
613
+ * tell", and treating it as "nothing is referenced" would hand back every attachment the actor
614
+ * ever sent, marked safe to delete.
615
+ */
616
+ async collectableAttachments(actor, options) {
617
+ const list = this.attachmentInventory();
618
+ const referencedMediaIds = this.store.referencedMediaIds?.bind(this.store);
619
+ if (referencedMediaIds === void 0) {
620
+ throw new NotImplementedException("Collecting attachments requires an AgentStore that implements referencedMediaIds(); the bound store cannot say which media a message still references, and guessing would delete files that are in use.");
621
+ }
622
+ const stagedBefore = options.olderThan.toISOString();
623
+ const inventory = await list({
624
+ actor,
625
+ stagedBefore,
626
+ ...options.limit !== void 0 ? {
627
+ limit: options.limit
628
+ } : {}
629
+ });
630
+ const aged = inventory.filter((entry) => entry.createdAt < stagedBefore);
631
+ const referenced = new Set(await referencedMediaIds(actor.id, aged.map((entry) => entry.mediaId)));
632
+ return aged.filter((entry) => !referenced.has(entry.mediaId));
633
+ }
634
+ /**
635
+ * The bound staging store's `list`, or a refusal. Absence is never answered with an empty list:
636
+ * "this host keeps no inventory" and "this actor has no files" are opposite facts that would
637
+ * otherwise render identically — as a clean file list, and as a sweep that collects nothing.
638
+ */
639
+ attachmentInventory() {
640
+ const list = this.staging?.list?.bind(this.staging);
641
+ if (list === void 0) {
642
+ throw new NotImplementedException("Listing attachments requires an AGENT_ATTACHMENT_STAGING provider implementing list(); the host owns the staged bytes, so only it can enumerate them.");
643
+ }
644
+ return list;
645
+ }
646
+ /**
400
647
  * The day's usage for the badge: tokens + summed USD cost from the store, and the configured
401
648
  * limit from the quota store (null → unlimited). `withinLimit` comes from the quota store so it
402
649
  * can never drift from what enforcement uses.
@@ -438,24 +685,26 @@ var AgentService = class {
438
685
  * out from run resolution (see {@link resolveRunForToolCall}) so the console `AgentApprovalPort`
439
686
  * adapter can resolve+signal a decision WITHOUT this check — it is authorized upstream instead.
440
687
  */
441
- async assertOwnsToolCall(actor, toolCallId) {
442
- const owner = await this.store.ownerOfToolCall(toolCallId);
688
+ async assertOwnsToolCall(actor, toolCallId2) {
689
+ const owner = await this.store.ownerOfToolCall(toolCallId2);
443
690
  if (owner === null) {
444
- throw new NotFoundException(`tool call ${toolCallId} not found`);
691
+ throw new NotFoundException(`tool call ${toolCallId2} not found`);
445
692
  }
446
693
  if (owner !== actor.id) {
447
694
  throw new ForbiddenException("tool call belongs to another actor");
448
695
  }
449
696
  }
450
697
  /**
451
- * Resolve the run currently awaiting `toolCallId` (its thread's active stream). That run is the
452
- * sub-agent's own child run when the pending call belongs to a delegated agent — the client never
453
- * knows or supplies a runId; it is derived here from the tool call alone.
698
+ * Resolve the run awaiting `toolCallId`. Derived server-side from the tool call alone — the client
699
+ * never knows or supplies a runId, and that run is the sub-agent's own child run when the pending
700
+ * call belongs to a delegated agent. The store answers from the CALL'S OWN `runId` where it has
701
+ * one; see `AgentStore.runForToolCall` for why that matters once a thread can hold more than one
702
+ * live run.
454
703
  */
455
- async resolveRunForToolCall(toolCallId) {
456
- const runId = await this.store.runForToolCall(toolCallId);
704
+ async resolveRunForToolCall(toolCallId2) {
705
+ const runId = await this.store.runForToolCall(toolCallId2);
457
706
  if (runId === null) {
458
- throw new NotFoundException(`tool call ${toolCallId} has no active run to signal`);
707
+ throw new NotFoundException(`tool call ${toolCallId2} has no active run to signal`);
459
708
  }
460
709
  return runId;
461
710
  }
@@ -476,12 +725,15 @@ AgentService = _ts_decorate2([
476
725
  _ts_param2(1, Inject2(AGENT_STORE2)),
477
726
  _ts_param2(2, Inject2(AGENT_DEPS_FACTORY)),
478
727
  _ts_param2(3, Inject2(AGENT_QUOTA_STORE2)),
728
+ _ts_param2(4, Optional2()),
729
+ _ts_param2(4, Inject2(AGENT_ATTACHMENT_STAGING)),
479
730
  _ts_metadata2("design:type", Function),
480
731
  _ts_metadata2("design:paramtypes", [
481
732
  typeof AgentRunner === "undefined" ? Object : AgentRunner,
482
733
  typeof AgentStore === "undefined" ? Object : AgentStore,
483
734
  typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory,
484
- Object
735
+ Object,
736
+ typeof AttachmentStagingStore === "undefined" ? Object : AttachmentStagingStore
485
737
  ])
486
738
  ], AgentService);
487
739
 
@@ -506,16 +758,16 @@ var AgentApprovalPortAdapter = class {
506
758
  constructor(agent) {
507
759
  this.agent = agent;
508
760
  }
509
- async approve(toolCallId, opts) {
510
- await this.agent.signalToolCall(toolCallId, {
761
+ async approve(toolCallId2, opts) {
762
+ await this.agent.signalToolCall(toolCallId2, {
511
763
  approved: true,
512
764
  ...opts?.executedByRef !== void 0 ? {
513
765
  executedByRef: opts.executedByRef
514
766
  } : {}
515
767
  });
516
768
  }
517
- async reject(toolCallId, opts) {
518
- await this.agent.signalToolCall(toolCallId, {
769
+ async reject(toolCallId2, opts) {
770
+ await this.agent.signalToolCall(toolCallId2, {
519
771
  approved: false,
520
772
  ...opts?.executedByRef !== void 0 ? {
521
773
  executedByRef: opts.executedByRef
@@ -593,8 +845,8 @@ AgentsController = _ts_decorate4([
593
845
  ], AgentsController);
594
846
 
595
847
  // src/controller/attachments.controller.ts
596
- import { AGENT_ACTOR_RESOLVER, AGENT_ATTACHMENT_STAGING, AGENT_OPTIONS as AGENT_OPTIONS2 } from "@dudousxd/nestjs-agent-core";
597
- import { BadRequestException as BadRequestException2, Controller as Controller2, Inject as Inject4, NotImplementedException as NotImplementedException2, Optional as Optional2, PayloadTooLargeException, Post, Req, UnsupportedMediaTypeException, UploadedFile, UseInterceptors } from "@nestjs/common";
848
+ import { AGENT_ACTOR_RESOLVER, AGENT_ATTACHMENT_STAGING as AGENT_ATTACHMENT_STAGING2, AGENT_OPTIONS as AGENT_OPTIONS2 } from "@dudousxd/nestjs-agent-core";
849
+ import { BadRequestException as BadRequestException2, Controller as Controller2, Get as Get2, Inject as Inject4, NotImplementedException as NotImplementedException2, Optional as Optional3, PayloadTooLargeException, Post, Req, UnsupportedMediaTypeException, UploadedFile, UseInterceptors } from "@nestjs/common";
598
850
  import { FileInterceptor } from "@nestjs/platform-express";
599
851
  function _ts_decorate5(decorators, target, key, desc) {
600
852
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -614,6 +866,8 @@ function _ts_param4(paramIndex, decorator) {
614
866
  }
615
867
  __name(_ts_param4, "_ts_param");
616
868
  var DEFAULT_MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
869
+ var HARD_MAX_ATTACHMENT_BYTES = 32 * 1024 * 1024;
870
+ var ATTACHMENT_PAGE_SIZE = 50;
617
871
  var DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES = [
618
872
  "image/png",
619
873
  "image/jpeg",
@@ -629,10 +883,12 @@ var AttachmentsController = class {
629
883
  }
630
884
  options;
631
885
  actorResolver;
886
+ agents;
632
887
  staging;
633
- constructor(options, actorResolver, staging) {
888
+ constructor(options, actorResolver, agents, staging) {
634
889
  this.options = options;
635
890
  this.actorResolver = actorResolver;
891
+ this.agents = agents;
636
892
  this.staging = staging;
637
893
  }
638
894
  /**
@@ -646,6 +902,21 @@ var AttachmentsController = class {
646
902
  throw new Error("AgentModule: attachments.upload is true but no AGENT_ATTACHMENT_STAGING provider is bound. Bind one (e.g. `{ provide: AGENT_ATTACHMENT_STAGING, useClass: MyStagingStore }`) in a module imported alongside AgentModule, or set attachments.upload to false.");
647
903
  }
648
904
  }
905
+ /**
906
+ * The caller's own staged files, newest first — the one question `GET /agent/threads/:id` cannot
907
+ * answer, since an upload that was never sent belongs to no thread.
908
+ *
909
+ * Scoped by the RESOLVED actor and nothing else. There is deliberately no `threadId` filter: a
910
+ * thread's attachments already ride on its messages in the thread payload, and re-serving them
911
+ * from here would add a second ownership path to get wrong for information the client already
912
+ * holds. Metadata only — see {@link StagedAttachment}.
913
+ */
914
+ async list(req) {
915
+ const actor = await this.actorResolver.resolve(req);
916
+ return this.agents.listAttachments(actor, {
917
+ limit: ATTACHMENT_PAGE_SIZE
918
+ });
919
+ }
649
920
  async upload(req, file) {
650
921
  if (this.staging === void 0) {
651
922
  throw new NotImplementedException2("Attachment upload is not configured on this server.");
@@ -671,9 +942,22 @@ var AttachmentsController = class {
671
942
  });
672
943
  }
673
944
  };
945
+ _ts_decorate5([
946
+ Get2(),
947
+ _ts_param4(0, Req()),
948
+ _ts_metadata5("design:type", Function),
949
+ _ts_metadata5("design:paramtypes", [
950
+ typeof Request === "undefined" ? Object : Request
951
+ ]),
952
+ _ts_metadata5("design:returntype", Promise)
953
+ ], AttachmentsController.prototype, "list", null);
674
954
  _ts_decorate5([
675
955
  Post(),
676
- UseInterceptors(FileInterceptor("file")),
956
+ UseInterceptors(FileInterceptor("file", {
957
+ limits: {
958
+ fileSize: HARD_MAX_ATTACHMENT_BYTES
959
+ }
960
+ })),
677
961
  _ts_param4(0, Req()),
678
962
  _ts_param4(1, UploadedFile()),
679
963
  _ts_metadata5("design:type", Function),
@@ -687,19 +971,20 @@ AttachmentsController = _ts_decorate5([
687
971
  Controller2("attachments"),
688
972
  _ts_param4(0, Inject4(AGENT_OPTIONS2)),
689
973
  _ts_param4(1, Inject4(AGENT_ACTOR_RESOLVER)),
690
- _ts_param4(2, Optional2()),
691
- _ts_param4(2, Inject4(AGENT_ATTACHMENT_STAGING)),
974
+ _ts_param4(3, Optional3()),
975
+ _ts_param4(3, Inject4(AGENT_ATTACHMENT_STAGING2)),
692
976
  _ts_metadata5("design:type", Function),
693
977
  _ts_metadata5("design:paramtypes", [
694
978
  typeof AgentModuleOptions === "undefined" ? Object : AgentModuleOptions,
695
979
  typeof ActorResolver === "undefined" ? Object : ActorResolver,
980
+ typeof AgentService === "undefined" ? Object : AgentService,
696
981
  Object
697
982
  ])
698
983
  ], AttachmentsController);
699
984
 
700
985
  // src/controller/chat.controller.ts
701
986
  import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER2, AgentStreamError } from "@dudousxd/nestjs-agent-core";
702
- import { Body, Controller as Controller3, Get as Get2, Inject as Inject5, Param, Post as Post2, Req as Req2, Res } from "@nestjs/common";
987
+ import { BadRequestException as BadRequestException3, Body, Controller as Controller3, Get as Get3, Inject as Inject5, Param, Post as Post2, Req as Req2, Res } from "@nestjs/common";
703
988
  function _ts_decorate6(decorators, target, key, desc) {
704
989
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
705
990
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -717,6 +1002,31 @@ function _ts_param5(paramIndex, decorator) {
717
1002
  };
718
1003
  }
719
1004
  __name(_ts_param5, "_ts_param");
1005
+ var MAX_ATTACHMENTS_PER_MESSAGE = 10;
1006
+ function attachmentRefs(claimed) {
1007
+ if (claimed === void 0) {
1008
+ return [];
1009
+ }
1010
+ if (!Array.isArray(claimed)) {
1011
+ throw new BadRequestException3("attachments must be an array of { mediaId }");
1012
+ }
1013
+ if (claimed.length > MAX_ATTACHMENTS_PER_MESSAGE) {
1014
+ throw new BadRequestException3(`a message may carry at most ${MAX_ATTACHMENTS_PER_MESSAGE} attachments`);
1015
+ }
1016
+ return claimed.map((entry) => {
1017
+ if (typeof entry !== "object" || entry === null || !("mediaId" in entry)) {
1018
+ throw new BadRequestException3("each attachment must be an object with a mediaId");
1019
+ }
1020
+ const { mediaId } = entry;
1021
+ if (typeof mediaId !== "string" || mediaId.length === 0) {
1022
+ throw new BadRequestException3("each attachment must carry a non-empty string mediaId");
1023
+ }
1024
+ return {
1025
+ mediaId
1026
+ };
1027
+ });
1028
+ }
1029
+ __name(attachmentRefs, "attachmentRefs");
720
1030
  var ChatController = class {
721
1031
  static {
722
1032
  __name(this, "ChatController");
@@ -729,6 +1039,7 @@ var ChatController = class {
729
1039
  }
730
1040
  async chat(req, res, body) {
731
1041
  const actor = await this.actorResolver.resolve(req);
1042
+ const attachments = attachmentRefs(body.attachments);
732
1043
  const { runId, threadId } = await this.agent.chat({
733
1044
  actor,
734
1045
  message: body.message,
@@ -738,8 +1049,8 @@ var ChatController = class {
738
1049
  ...body.agent !== void 0 ? {
739
1050
  agentName: body.agent
740
1051
  } : {},
741
- ...body.attachments !== void 0 ? {
742
- attachments: body.attachments
1052
+ ...attachments.length > 0 ? {
1053
+ attachments
743
1054
  } : {},
744
1055
  ...body.pageContext !== void 0 ? {
745
1056
  pageContext: body.pageContext
@@ -751,10 +1062,11 @@ var ChatController = class {
751
1062
  transient: true
752
1063
  } : {}
753
1064
  });
754
- await this.pipe(res, runId, threadId);
1065
+ await this.pipe(res, runId, this.agent.subscribe(runId), threadId);
755
1066
  }
756
- async stream(runId, res) {
757
- await this.pipe(res, runId);
1067
+ async stream(req, runId, res) {
1068
+ const actor = await this.actorResolver.resolve(req);
1069
+ await this.pipe(res, runId, await this.agent.subscribeAs(actor, runId));
758
1070
  }
759
1071
  async cancel(req, runId) {
760
1072
  const actor = await this.actorResolver.resolve(req);
@@ -763,7 +1075,7 @@ var ChatController = class {
763
1075
  aborted: true
764
1076
  };
765
1077
  }
766
- async pipe(res, runId, threadId) {
1078
+ async pipe(res, runId, events, threadId) {
767
1079
  res.setHeader("Content-Type", "text/event-stream");
768
1080
  res.setHeader("Cache-Control", "no-cache");
769
1081
  res.setHeader("Connection", "keep-alive");
@@ -781,7 +1093,7 @@ data: ${JSON.stringify({
781
1093
  const decoder = new TextDecoder();
782
1094
  let buffer = "";
783
1095
  try {
784
- for await (const chunk of this.agent.subscribe(runId)) {
1096
+ for await (const chunk of events) {
785
1097
  buffer += decoder.decode(chunk, {
786
1098
  stream: true
787
1099
  });
@@ -833,11 +1145,13 @@ _ts_decorate6([
833
1145
  _ts_metadata6("design:returntype", Promise)
834
1146
  ], ChatController.prototype, "chat", null);
835
1147
  _ts_decorate6([
836
- Get2("chat/:runId/stream"),
837
- _ts_param5(0, Param("runId")),
838
- _ts_param5(1, Res()),
1148
+ Get3("chat/:runId/stream"),
1149
+ _ts_param5(0, Req2()),
1150
+ _ts_param5(1, Param("runId")),
1151
+ _ts_param5(2, Res()),
839
1152
  _ts_metadata6("design:type", Function),
840
1153
  _ts_metadata6("design:paramtypes", [
1154
+ typeof Request === "undefined" ? Object : Request,
841
1155
  String,
842
1156
  typeof Response === "undefined" ? Object : Response
843
1157
  ]),
@@ -864,9 +1178,9 @@ ChatController = _ts_decorate6([
864
1178
  ])
865
1179
  ], ChatController);
866
1180
 
867
- // src/controller/quota.controller.ts
868
- import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER3 } from "@dudousxd/nestjs-agent-core";
869
- import { Controller as Controller4, Get as Get3, Inject as Inject6, Req as Req3 } from "@nestjs/common";
1181
+ // src/controller/memories.controller.ts
1182
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER3, AGENT_MEMORY as AGENT_MEMORY2, memoryForgetVerdict, offerMemories } from "@dudousxd/nestjs-agent-core";
1183
+ import { Controller as Controller4, Delete, ForbiddenException as ForbiddenException2, Get as Get4, Inject as Inject6, NotFoundException as NotFoundException2, Optional as Optional4, Param as Param2, Query, Req as Req3 } from "@nestjs/common";
870
1184
  function _ts_decorate7(decorators, target, key, desc) {
871
1185
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
872
1186
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -884,43 +1198,115 @@ function _ts_param6(paramIndex, decorator) {
884
1198
  };
885
1199
  }
886
1200
  __name(_ts_param6, "_ts_param");
887
- var QuotaController = class {
1201
+ var MemoriesController = class {
888
1202
  static {
889
- __name(this, "QuotaController");
1203
+ __name(this, "MemoriesController");
890
1204
  }
891
- agent;
892
1205
  actorResolver;
893
- constructor(agent, actorResolver) {
894
- this.agent = agent;
1206
+ memory;
1207
+ constructor(actorResolver, memory) {
895
1208
  this.actorResolver = actorResolver;
1209
+ this.memory = memory;
896
1210
  }
897
- async today(req) {
1211
+ /**
1212
+ * Every memory this actor can reach, most specific first, each carrying its origin and whatever
1213
+ * wider value it outranks.
1214
+ *
1215
+ * DELIBERATELY IGNORES `maxMemories`. That ceiling is a budget on what one TURN carries; applying
1216
+ * it here would mean a person could not see — and so could not delete — a belief the assistant is
1217
+ * one write away from acting on again. Showing someone more than the model sees is harmless;
1218
+ * showing them less is the failure this endpoint exists to prevent.
1219
+ */
1220
+ async list(req, threadId) {
1221
+ if (this.memory === void 0) {
1222
+ return [];
1223
+ }
898
1224
  const actor = await this.actorResolver.resolve(req);
899
- return this.agent.quotaToday(actor.id);
1225
+ return (await this.unbounded(threadId ?? "", actor)).entries;
1226
+ }
1227
+ /**
1228
+ * Forget one. Authorized against the actor's OWN resolved list, the same way a `skill` load is
1229
+ * authorized against the turn's catalog: an id this actor cannot see is answered as missing rather
1230
+ * than refused, so the endpoint cannot be used to find out which memories exist about other people.
1231
+ */
1232
+ async forget(req, id) {
1233
+ if (this.memory === void 0) {
1234
+ throw new NotFoundException2("No memory is configured in this deployment.");
1235
+ }
1236
+ const actor = await this.actorResolver.resolve(req);
1237
+ const digest = await this.unbounded("", actor);
1238
+ const entry = digest.entries.find((candidate) => candidate.id === id);
1239
+ if (entry === void 0) {
1240
+ throw new NotFoundException2(`No memory with id "${id}".`);
1241
+ }
1242
+ const verdict = memoryForgetVerdict({
1243
+ record: entry,
1244
+ actor
1245
+ });
1246
+ if (!verdict.allowed) {
1247
+ throw new ForbiddenException2(verdict.reason);
1248
+ }
1249
+ return {
1250
+ forgotten: await this.memory.provider.forget({
1251
+ id: entry.id,
1252
+ ctx: {
1253
+ actor,
1254
+ threadId: ""
1255
+ }
1256
+ })
1257
+ };
1258
+ }
1259
+ async unbounded(threadId, actor) {
1260
+ const config = this.memory;
1261
+ return await offerMemories({
1262
+ config: {
1263
+ ...config,
1264
+ maxMemories: Number.POSITIVE_INFINITY
1265
+ },
1266
+ ctx: {
1267
+ actor,
1268
+ threadId
1269
+ }
1270
+ });
900
1271
  }
901
1272
  };
902
1273
  _ts_decorate7([
903
- Get3("today"),
1274
+ Get4(),
1275
+ _ts_param6(0, Req3()),
1276
+ _ts_param6(1, Query("threadId")),
1277
+ _ts_metadata7("design:type", Function),
1278
+ _ts_metadata7("design:paramtypes", [
1279
+ typeof Request === "undefined" ? Object : Request,
1280
+ String
1281
+ ]),
1282
+ _ts_metadata7("design:returntype", Promise)
1283
+ ], MemoriesController.prototype, "list", null);
1284
+ _ts_decorate7([
1285
+ Delete(":id"),
904
1286
  _ts_param6(0, Req3()),
1287
+ _ts_param6(1, Param2("id")),
905
1288
  _ts_metadata7("design:type", Function),
906
1289
  _ts_metadata7("design:paramtypes", [
907
- typeof Request === "undefined" ? Object : Request
1290
+ typeof Request === "undefined" ? Object : Request,
1291
+ String
908
1292
  ]),
909
1293
  _ts_metadata7("design:returntype", Promise)
910
- ], QuotaController.prototype, "today", null);
911
- QuotaController = _ts_decorate7([
912
- Controller4("quota"),
913
- _ts_param6(1, Inject6(AGENT_ACTOR_RESOLVER3)),
1294
+ ], MemoriesController.prototype, "forget", null);
1295
+ MemoriesController = _ts_decorate7([
1296
+ Controller4("memories"),
1297
+ _ts_param6(0, Inject6(AGENT_ACTOR_RESOLVER3)),
1298
+ _ts_param6(1, Optional4()),
1299
+ _ts_param6(1, Inject6(AGENT_MEMORY2)),
914
1300
  _ts_metadata7("design:type", Function),
915
1301
  _ts_metadata7("design:paramtypes", [
916
- typeof AgentService === "undefined" ? Object : AgentService,
917
- typeof ActorResolver === "undefined" ? Object : ActorResolver
1302
+ typeof ActorResolver === "undefined" ? Object : ActorResolver,
1303
+ Object
918
1304
  ])
919
- ], QuotaController);
1305
+ ], MemoriesController);
920
1306
 
921
- // src/controller/threads.controller.ts
1307
+ // src/controller/quota.controller.ts
922
1308
  import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER4 } from "@dudousxd/nestjs-agent-core";
923
- import { Body as Body2, Controller as Controller5, Delete, Get as Get4, Inject as Inject7, Param as Param2, Patch, Post as Post3, Req as Req4 } from "@nestjs/common";
1309
+ import { Controller as Controller5, Get as Get5, Inject as Inject7, Req as Req4 } from "@nestjs/common";
924
1310
  function _ts_decorate8(decorators, target, key, desc) {
925
1311
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
926
1312
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -938,6 +1324,125 @@ function _ts_param7(paramIndex, decorator) {
938
1324
  };
939
1325
  }
940
1326
  __name(_ts_param7, "_ts_param");
1327
+ var QuotaController = class {
1328
+ static {
1329
+ __name(this, "QuotaController");
1330
+ }
1331
+ agent;
1332
+ actorResolver;
1333
+ constructor(agent, actorResolver) {
1334
+ this.agent = agent;
1335
+ this.actorResolver = actorResolver;
1336
+ }
1337
+ async today(req) {
1338
+ const actor = await this.actorResolver.resolve(req);
1339
+ return this.agent.quotaToday(actor.id);
1340
+ }
1341
+ };
1342
+ _ts_decorate8([
1343
+ Get5("today"),
1344
+ _ts_param7(0, Req4()),
1345
+ _ts_metadata8("design:type", Function),
1346
+ _ts_metadata8("design:paramtypes", [
1347
+ typeof Request === "undefined" ? Object : Request
1348
+ ]),
1349
+ _ts_metadata8("design:returntype", Promise)
1350
+ ], QuotaController.prototype, "today", null);
1351
+ QuotaController = _ts_decorate8([
1352
+ Controller5("quota"),
1353
+ _ts_param7(1, Inject7(AGENT_ACTOR_RESOLVER4)),
1354
+ _ts_metadata8("design:type", Function),
1355
+ _ts_metadata8("design:paramtypes", [
1356
+ typeof AgentService === "undefined" ? Object : AgentService,
1357
+ typeof ActorResolver === "undefined" ? Object : ActorResolver
1358
+ ])
1359
+ ], QuotaController);
1360
+
1361
+ // src/controller/skills.controller.ts
1362
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER5, AGENT_SKILLS as AGENT_SKILLS2, offerSkills } from "@dudousxd/nestjs-agent-core";
1363
+ import { Controller as Controller6, Get as Get6, Inject as Inject8, Optional as Optional5, Query as Query2, Req as Req5 } from "@nestjs/common";
1364
+ function _ts_decorate9(decorators, target, key, desc) {
1365
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1366
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1367
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1368
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1369
+ }
1370
+ __name(_ts_decorate9, "_ts_decorate");
1371
+ function _ts_metadata9(k, v) {
1372
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1373
+ }
1374
+ __name(_ts_metadata9, "_ts_metadata");
1375
+ function _ts_param8(paramIndex, decorator) {
1376
+ return function(target, key) {
1377
+ decorator(target, key, paramIndex);
1378
+ };
1379
+ }
1380
+ __name(_ts_param8, "_ts_param");
1381
+ var SkillsController = class {
1382
+ static {
1383
+ __name(this, "SkillsController");
1384
+ }
1385
+ actorResolver;
1386
+ skills;
1387
+ constructor(actorResolver, skills) {
1388
+ this.actorResolver = actorResolver;
1389
+ this.skills = skills;
1390
+ }
1391
+ async list(req, threadId) {
1392
+ if (this.skills === void 0) {
1393
+ return [];
1394
+ }
1395
+ const actor = await this.actorResolver.resolve(req);
1396
+ const offer = await offerSkills(this.skills, {
1397
+ actor,
1398
+ threadId: threadId ?? ""
1399
+ });
1400
+ return offer.entries;
1401
+ }
1402
+ };
1403
+ _ts_decorate9([
1404
+ Get6(),
1405
+ _ts_param8(0, Req5()),
1406
+ _ts_param8(1, Query2("threadId")),
1407
+ _ts_metadata9("design:type", Function),
1408
+ _ts_metadata9("design:paramtypes", [
1409
+ typeof Request === "undefined" ? Object : Request,
1410
+ String
1411
+ ]),
1412
+ _ts_metadata9("design:returntype", Promise)
1413
+ ], SkillsController.prototype, "list", null);
1414
+ SkillsController = _ts_decorate9([
1415
+ Controller6("skills"),
1416
+ _ts_param8(0, Inject8(AGENT_ACTOR_RESOLVER5)),
1417
+ _ts_param8(1, Optional5()),
1418
+ _ts_param8(1, Inject8(AGENT_SKILLS2)),
1419
+ _ts_metadata9("design:type", Function),
1420
+ _ts_metadata9("design:paramtypes", [
1421
+ typeof ActorResolver === "undefined" ? Object : ActorResolver,
1422
+ Object
1423
+ ])
1424
+ ], SkillsController);
1425
+
1426
+ // src/controller/threads.controller.ts
1427
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER6 } from "@dudousxd/nestjs-agent-core";
1428
+ import { Body as Body2, Controller as Controller7, Delete as Delete2, Get as Get7, Inject as Inject9, Param as Param3, Patch, Post as Post3, Req as Req6 } from "@nestjs/common";
1429
+ function _ts_decorate10(decorators, target, key, desc) {
1430
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1431
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1432
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1433
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1434
+ }
1435
+ __name(_ts_decorate10, "_ts_decorate");
1436
+ function _ts_metadata10(k, v) {
1437
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1438
+ }
1439
+ __name(_ts_metadata10, "_ts_metadata");
1440
+ function _ts_param9(paramIndex, decorator) {
1441
+ return function(target, key) {
1442
+ decorator(target, key, paramIndex);
1443
+ };
1444
+ }
1445
+ __name(_ts_param9, "_ts_param");
941
1446
  var ThreadsController = class {
942
1447
  static {
943
1448
  __name(this, "ThreadsController");
@@ -1001,117 +1506,173 @@ var ThreadsController = class {
1001
1506
  };
1002
1507
  }
1003
1508
  };
1004
- _ts_decorate8([
1005
- Get4(),
1006
- _ts_param7(0, Req4()),
1007
- _ts_metadata8("design:type", Function),
1008
- _ts_metadata8("design:paramtypes", [
1509
+ _ts_decorate10([
1510
+ Get7(),
1511
+ _ts_param9(0, Req6()),
1512
+ _ts_metadata10("design:type", Function),
1513
+ _ts_metadata10("design:paramtypes", [
1009
1514
  typeof Request === "undefined" ? Object : Request
1010
1515
  ]),
1011
- _ts_metadata8("design:returntype", Promise)
1516
+ _ts_metadata10("design:returntype", Promise)
1012
1517
  ], ThreadsController.prototype, "list", null);
1013
- _ts_decorate8([
1014
- Get4(":id"),
1015
- _ts_param7(0, Req4()),
1016
- _ts_param7(1, Param2("id")),
1017
- _ts_metadata8("design:type", Function),
1018
- _ts_metadata8("design:paramtypes", [
1518
+ _ts_decorate10([
1519
+ Get7(":id"),
1520
+ _ts_param9(0, Req6()),
1521
+ _ts_param9(1, Param3("id")),
1522
+ _ts_metadata10("design:type", Function),
1523
+ _ts_metadata10("design:paramtypes", [
1019
1524
  typeof Request === "undefined" ? Object : Request,
1020
1525
  String
1021
1526
  ]),
1022
- _ts_metadata8("design:returntype", Promise)
1527
+ _ts_metadata10("design:returntype", Promise)
1023
1528
  ], ThreadsController.prototype, "detail", null);
1024
- _ts_decorate8([
1025
- Delete(":id"),
1026
- _ts_param7(0, Req4()),
1027
- _ts_param7(1, Param2("id")),
1028
- _ts_metadata8("design:type", Function),
1029
- _ts_metadata8("design:paramtypes", [
1529
+ _ts_decorate10([
1530
+ Delete2(":id"),
1531
+ _ts_param9(0, Req6()),
1532
+ _ts_param9(1, Param3("id")),
1533
+ _ts_metadata10("design:type", Function),
1534
+ _ts_metadata10("design:paramtypes", [
1030
1535
  typeof Request === "undefined" ? Object : Request,
1031
1536
  String
1032
1537
  ]),
1033
- _ts_metadata8("design:returntype", Promise)
1538
+ _ts_metadata10("design:returntype", Promise)
1034
1539
  ], ThreadsController.prototype, "remove", null);
1035
- _ts_decorate8([
1540
+ _ts_decorate10([
1036
1541
  Post3(":id/fork-from/:messageId"),
1037
- _ts_param7(0, Req4()),
1038
- _ts_param7(1, Param2("id")),
1039
- _ts_param7(2, Param2("messageId")),
1040
- _ts_metadata8("design:type", Function),
1041
- _ts_metadata8("design:paramtypes", [
1542
+ _ts_param9(0, Req6()),
1543
+ _ts_param9(1, Param3("id")),
1544
+ _ts_param9(2, Param3("messageId")),
1545
+ _ts_metadata10("design:type", Function),
1546
+ _ts_metadata10("design:paramtypes", [
1042
1547
  typeof Request === "undefined" ? Object : Request,
1043
1548
  String,
1044
1549
  String
1045
1550
  ]),
1046
- _ts_metadata8("design:returntype", Promise)
1551
+ _ts_metadata10("design:returntype", Promise)
1047
1552
  ], ThreadsController.prototype, "fork", null);
1048
- _ts_decorate8([
1553
+ _ts_decorate10([
1049
1554
  Patch(":id"),
1050
- _ts_param7(0, Req4()),
1051
- _ts_param7(1, Param2("id")),
1052
- _ts_param7(2, Body2()),
1053
- _ts_metadata8("design:type", Function),
1054
- _ts_metadata8("design:paramtypes", [
1555
+ _ts_param9(0, Req6()),
1556
+ _ts_param9(1, Param3("id")),
1557
+ _ts_param9(2, Body2()),
1558
+ _ts_metadata10("design:type", Function),
1559
+ _ts_metadata10("design:paramtypes", [
1055
1560
  typeof Request === "undefined" ? Object : Request,
1056
1561
  String,
1057
1562
  typeof UpdateThreadBody === "undefined" ? Object : UpdateThreadBody
1058
1563
  ]),
1059
- _ts_metadata8("design:returntype", Promise)
1564
+ _ts_metadata10("design:returntype", Promise)
1060
1565
  ], ThreadsController.prototype, "update", null);
1061
- _ts_decorate8([
1566
+ _ts_decorate10([
1062
1567
  Post3(":id/promote"),
1063
- _ts_param7(0, Req4()),
1064
- _ts_param7(1, Param2("id")),
1065
- _ts_metadata8("design:type", Function),
1066
- _ts_metadata8("design:paramtypes", [
1568
+ _ts_param9(0, Req6()),
1569
+ _ts_param9(1, Param3("id")),
1570
+ _ts_metadata10("design:type", Function),
1571
+ _ts_metadata10("design:paramtypes", [
1067
1572
  typeof Request === "undefined" ? Object : Request,
1068
1573
  String
1069
1574
  ]),
1070
- _ts_metadata8("design:returntype", Promise)
1575
+ _ts_metadata10("design:returntype", Promise)
1071
1576
  ], ThreadsController.prototype, "promote", null);
1072
- _ts_decorate8([
1073
- Delete(":id/from/:messageId"),
1074
- _ts_param7(0, Req4()),
1075
- _ts_param7(1, Param2("id")),
1076
- _ts_param7(2, Param2("messageId")),
1077
- _ts_metadata8("design:type", Function),
1078
- _ts_metadata8("design:paramtypes", [
1577
+ _ts_decorate10([
1578
+ Delete2(":id/from/:messageId"),
1579
+ _ts_param9(0, Req6()),
1580
+ _ts_param9(1, Param3("id")),
1581
+ _ts_param9(2, Param3("messageId")),
1582
+ _ts_metadata10("design:type", Function),
1583
+ _ts_metadata10("design:paramtypes", [
1079
1584
  typeof Request === "undefined" ? Object : Request,
1080
1585
  String,
1081
1586
  String
1082
1587
  ]),
1083
- _ts_metadata8("design:returntype", Promise)
1588
+ _ts_metadata10("design:returntype", Promise)
1084
1589
  ], ThreadsController.prototype, "truncateFrom", null);
1085
- ThreadsController = _ts_decorate8([
1086
- Controller5("threads"),
1087
- _ts_param7(1, Inject7(AGENT_ACTOR_RESOLVER4)),
1088
- _ts_metadata8("design:type", Function),
1089
- _ts_metadata8("design:paramtypes", [
1590
+ ThreadsController = _ts_decorate10([
1591
+ Controller7("threads"),
1592
+ _ts_param9(1, Inject9(AGENT_ACTOR_RESOLVER6)),
1593
+ _ts_metadata10("design:type", Function),
1594
+ _ts_metadata10("design:paramtypes", [
1090
1595
  typeof AgentService === "undefined" ? Object : AgentService,
1091
1596
  typeof ActorResolver === "undefined" ? Object : ActorResolver
1092
1597
  ])
1093
1598
  ], ThreadsController);
1094
1599
 
1095
1600
  // src/controller/tool-call.controller.ts
1096
- import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER5 } from "@dudousxd/nestjs-agent-core";
1097
- import { Body as Body3, Controller as Controller6, Inject as Inject8, Post as Post4, Req as Req5 } from "@nestjs/common";
1098
- function _ts_decorate9(decorators, target, key, desc) {
1601
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER7 } from "@dudousxd/nestjs-agent-core";
1602
+ import { BadRequestException as BadRequestException4, Body as Body3, Controller as Controller8, Inject as Inject10, Post as Post4, Req as Req7 } from "@nestjs/common";
1603
+ function _ts_decorate11(decorators, target, key, desc) {
1099
1604
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1100
1605
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1101
1606
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1102
1607
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1103
1608
  }
1104
- __name(_ts_decorate9, "_ts_decorate");
1105
- function _ts_metadata9(k, v) {
1609
+ __name(_ts_decorate11, "_ts_decorate");
1610
+ function _ts_metadata11(k, v) {
1106
1611
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1107
1612
  }
1108
- __name(_ts_metadata9, "_ts_metadata");
1109
- function _ts_param8(paramIndex, decorator) {
1613
+ __name(_ts_metadata11, "_ts_metadata");
1614
+ function _ts_param10(paramIndex, decorator) {
1110
1615
  return function(target, key) {
1111
1616
  decorator(target, key, paramIndex);
1112
1617
  };
1113
1618
  }
1114
- __name(_ts_param8, "_ts_param");
1619
+ __name(_ts_param10, "_ts_param");
1620
+ var MAX_ANSWERED_QUESTIONS = 100;
1621
+ var MAX_VALUES_PER_QUESTION = 100;
1622
+ var MAX_ANSWER_VALUE_LENGTH = 4096;
1623
+ var MAX_REJECT_REASON_LENGTH = 4096;
1624
+ function toolCallId(claimed) {
1625
+ if (typeof claimed !== "string" || claimed.length === 0) {
1626
+ throw new BadRequestException4("toolCallId must be a non-empty string");
1627
+ }
1628
+ return claimed;
1629
+ }
1630
+ __name(toolCallId, "toolCallId");
1631
+ function rejectReason(claimed) {
1632
+ if (claimed === void 0) {
1633
+ return void 0;
1634
+ }
1635
+ if (typeof claimed !== "string") {
1636
+ throw new BadRequestException4("reason must be a string");
1637
+ }
1638
+ if (claimed.length > MAX_REJECT_REASON_LENGTH) {
1639
+ throw new BadRequestException4(`reason must be at most ${MAX_REJECT_REASON_LENGTH} characters`);
1640
+ }
1641
+ return claimed;
1642
+ }
1643
+ __name(rejectReason, "rejectReason");
1644
+ function answers(claimed) {
1645
+ if (claimed === void 0) {
1646
+ return void 0;
1647
+ }
1648
+ if (typeof claimed !== "object" || claimed === null || Array.isArray(claimed)) {
1649
+ throw new BadRequestException4("answers must be an object of questionId \u2192 chosen values");
1650
+ }
1651
+ const entries = Object.entries(claimed);
1652
+ if (entries.length > MAX_ANSWERED_QUESTIONS) {
1653
+ throw new BadRequestException4(`answers may cover at most ${MAX_ANSWERED_QUESTIONS} questions`);
1654
+ }
1655
+ const validated = /* @__PURE__ */ Object.create(null);
1656
+ for (const [questionId, values] of entries) {
1657
+ if (!Array.isArray(values)) {
1658
+ throw new BadRequestException4(`answers["${questionId}"] must be an array of chosen values`);
1659
+ }
1660
+ if (values.length > MAX_VALUES_PER_QUESTION) {
1661
+ throw new BadRequestException4(`answers["${questionId}"] may carry at most ${MAX_VALUES_PER_QUESTION} values`);
1662
+ }
1663
+ for (const value of values) {
1664
+ if (typeof value !== "string") {
1665
+ throw new BadRequestException4(`answers["${questionId}"] must contain only strings`);
1666
+ }
1667
+ if (value.length > MAX_ANSWER_VALUE_LENGTH) {
1668
+ throw new BadRequestException4(`answers["${questionId}"] values must be at most ${MAX_ANSWER_VALUE_LENGTH} characters`);
1669
+ }
1670
+ }
1671
+ validated[questionId] = values;
1672
+ }
1673
+ return validated;
1674
+ }
1675
+ __name(answers, "answers");
1115
1676
  var ToolCallController = class {
1116
1677
  static {
1117
1678
  __name(this, "ToolCallController");
@@ -1124,46 +1685,88 @@ var ToolCallController = class {
1124
1685
  }
1125
1686
  async approve(req, body) {
1126
1687
  const actor = await this.actorResolver.resolve(req);
1127
- await this.agent.approve(actor, body.toolCallId);
1688
+ await this.agent.approve(actor, toolCallId(body.toolCallId));
1128
1689
  return {
1129
1690
  ok: true
1130
1691
  };
1131
1692
  }
1132
1693
  async reject(req, body) {
1133
1694
  const actor = await this.actorResolver.resolve(req);
1134
- await this.agent.reject(actor, body.toolCallId, body.reason);
1695
+ await this.agent.reject(actor, toolCallId(body.toolCallId), rejectReason(body.reason));
1696
+ return {
1697
+ ok: true
1698
+ };
1699
+ }
1700
+ /**
1701
+ * Settle a parked question set. Alongside `approve`/`reject` rather than under a surface of its
1702
+ * own: an elicitation IS a parked tool call, gated by the same ownership check and delivered
1703
+ * through the same signal.
1704
+ */
1705
+ async answer(req, body) {
1706
+ const actor = await this.actorResolver.resolve(req);
1707
+ await this.agent.answer(actor, toolCallId(body.toolCallId), answers(body.answers));
1708
+ return {
1709
+ ok: true
1710
+ };
1711
+ }
1712
+ /** Decline to answer and let the agent proceed on the answers it pre-picked. */
1713
+ async skip(req, body) {
1714
+ const actor = await this.actorResolver.resolve(req);
1715
+ await this.agent.skip(actor, toolCallId(body.toolCallId));
1135
1716
  return {
1136
1717
  ok: true
1137
1718
  };
1138
1719
  }
1139
1720
  };
1140
- _ts_decorate9([
1721
+ _ts_decorate11([
1141
1722
  Post4("approve"),
1142
- _ts_param8(0, Req5()),
1143
- _ts_param8(1, Body3()),
1144
- _ts_metadata9("design:type", Function),
1145
- _ts_metadata9("design:paramtypes", [
1723
+ _ts_param10(0, Req7()),
1724
+ _ts_param10(1, Body3()),
1725
+ _ts_metadata11("design:type", Function),
1726
+ _ts_metadata11("design:paramtypes", [
1146
1727
  typeof Request === "undefined" ? Object : Request,
1147
1728
  typeof ApproveBody === "undefined" ? Object : ApproveBody
1148
1729
  ]),
1149
- _ts_metadata9("design:returntype", Promise)
1730
+ _ts_metadata11("design:returntype", Promise)
1150
1731
  ], ToolCallController.prototype, "approve", null);
1151
- _ts_decorate9([
1732
+ _ts_decorate11([
1152
1733
  Post4("reject"),
1153
- _ts_param8(0, Req5()),
1154
- _ts_param8(1, Body3()),
1155
- _ts_metadata9("design:type", Function),
1156
- _ts_metadata9("design:paramtypes", [
1734
+ _ts_param10(0, Req7()),
1735
+ _ts_param10(1, Body3()),
1736
+ _ts_metadata11("design:type", Function),
1737
+ _ts_metadata11("design:paramtypes", [
1157
1738
  typeof Request === "undefined" ? Object : Request,
1158
1739
  typeof RejectBody === "undefined" ? Object : RejectBody
1159
1740
  ]),
1160
- _ts_metadata9("design:returntype", Promise)
1741
+ _ts_metadata11("design:returntype", Promise)
1161
1742
  ], ToolCallController.prototype, "reject", null);
1162
- ToolCallController = _ts_decorate9([
1163
- Controller6("tool-call"),
1164
- _ts_param8(1, Inject8(AGENT_ACTOR_RESOLVER5)),
1165
- _ts_metadata9("design:type", Function),
1166
- _ts_metadata9("design:paramtypes", [
1743
+ _ts_decorate11([
1744
+ Post4("answer"),
1745
+ _ts_param10(0, Req7()),
1746
+ _ts_param10(1, Body3()),
1747
+ _ts_metadata11("design:type", Function),
1748
+ _ts_metadata11("design:paramtypes", [
1749
+ typeof Request === "undefined" ? Object : Request,
1750
+ typeof AnswerBody === "undefined" ? Object : AnswerBody
1751
+ ]),
1752
+ _ts_metadata11("design:returntype", Promise)
1753
+ ], ToolCallController.prototype, "answer", null);
1754
+ _ts_decorate11([
1755
+ Post4("skip"),
1756
+ _ts_param10(0, Req7()),
1757
+ _ts_param10(1, Body3()),
1758
+ _ts_metadata11("design:type", Function),
1759
+ _ts_metadata11("design:paramtypes", [
1760
+ typeof Request === "undefined" ? Object : Request,
1761
+ typeof ApproveBody === "undefined" ? Object : ApproveBody
1762
+ ]),
1763
+ _ts_metadata11("design:returntype", Promise)
1764
+ ], ToolCallController.prototype, "skip", null);
1765
+ ToolCallController = _ts_decorate11([
1766
+ Controller8("tool-call"),
1767
+ _ts_param10(1, Inject10(AGENT_ACTOR_RESOLVER7)),
1768
+ _ts_metadata11("design:type", Function),
1769
+ _ts_metadata11("design:paramtypes", [
1167
1770
  typeof AgentService === "undefined" ? Object : AgentService,
1168
1771
  typeof ActorResolver === "undefined" ? Object : ActorResolver
1169
1772
  ])
@@ -1171,7 +1774,7 @@ ToolCallController = _ts_decorate9([
1171
1774
 
1172
1775
  // src/discovery/agent-discovery.service.ts
1173
1776
  import { AGENT_PROMPT_CONTRIBUTORS as AGENT_PROMPT_CONTRIBUTORS2, AGENT_REGISTRY as AGENT_REGISTRY3, AgentRegistry as AgentRegistry4 } from "@dudousxd/nestjs-agent-core";
1174
- import { Inject as Inject9, Injectable as Injectable4, Logger } from "@nestjs/common";
1777
+ import { Inject as Inject11, Injectable as Injectable4, Logger } from "@nestjs/common";
1175
1778
  import { DiscoveryService, MetadataScanner } from "@nestjs/core";
1176
1779
 
1177
1780
  // src/decorator/agent.decorator.ts
@@ -1206,23 +1809,23 @@ function SystemPromptContributor() {
1206
1809
  __name(SystemPromptContributor, "SystemPromptContributor");
1207
1810
 
1208
1811
  // src/discovery/agent-discovery.service.ts
1209
- function _ts_decorate10(decorators, target, key, desc) {
1812
+ function _ts_decorate12(decorators, target, key, desc) {
1210
1813
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1211
1814
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1212
1815
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1213
1816
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1214
1817
  }
1215
- __name(_ts_decorate10, "_ts_decorate");
1216
- function _ts_metadata10(k, v) {
1818
+ __name(_ts_decorate12, "_ts_decorate");
1819
+ function _ts_metadata12(k, v) {
1217
1820
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1218
1821
  }
1219
- __name(_ts_metadata10, "_ts_metadata");
1220
- function _ts_param9(paramIndex, decorator) {
1822
+ __name(_ts_metadata12, "_ts_metadata");
1823
+ function _ts_param11(paramIndex, decorator) {
1221
1824
  return function(target, key) {
1222
1825
  decorator(target, key, paramIndex);
1223
1826
  };
1224
1827
  }
1225
- __name(_ts_param9, "_ts_param");
1828
+ __name(_ts_param11, "_ts_param");
1226
1829
  var AgentDiscoveryService = class _AgentDiscoveryService {
1227
1830
  static {
1228
1831
  __name(this, "AgentDiscoveryService");
@@ -1270,23 +1873,53 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
1270
1873
  ...meta.maxSteps !== void 0 ? {
1271
1874
  maxSteps: meta.maxSteps
1272
1875
  } : {},
1876
+ ...meta.maxDelegationDepth !== void 0 ? {
1877
+ maxDelegationDepth: meta.maxDelegationDepth
1878
+ } : {},
1879
+ ...meta.maxAgentAppearances !== void 0 ? {
1880
+ maxAgentAppearances: meta.maxAgentAppearances
1881
+ } : {},
1882
+ ...meta.history !== void 0 ? {
1883
+ history: meta.history
1884
+ } : {},
1885
+ ...meta.outputSchema !== void 0 ? {
1886
+ outputSchema: meta.outputSchema
1887
+ } : {},
1888
+ ...meta.intake !== void 0 ? {
1889
+ intake: meta.intake
1890
+ } : {},
1891
+ ...meta.ask !== void 0 ? {
1892
+ ask: meta.ask
1893
+ } : {},
1894
+ ...meta.outputRepairAttempts !== void 0 ? {
1895
+ outputRepairAttempts: meta.outputRepairAttempts
1896
+ } : {},
1273
1897
  ...meta.handoff !== void 0 ? {
1274
- delegatesTo: this.handoffNames(meta)
1898
+ delegatesTo: this.handoffEdges(meta)
1275
1899
  } : {}
1276
1900
  };
1277
1901
  }
1278
- /** Resolve each handoff `@Agent` class to its registered name (skipping non-agent classes). */
1279
- handoffNames(meta) {
1280
- const names = [];
1281
- for (const target of meta.handoff ?? []) {
1902
+ /**
1903
+ * Resolve each handoff entry to an {@link AgentDelegation} — its target's registered name, plus
1904
+ * whether that edge detaches (skipping non-agent classes). A bare name is kept for a plain edge
1905
+ * rather than an always-object form, so a definition an older host built by hand still reads.
1906
+ */
1907
+ handoffEdges(meta) {
1908
+ const edges = [];
1909
+ for (const entry of meta.handoff ?? []) {
1910
+ const target = typeof entry === "function" ? entry : entry.agent;
1911
+ const detached = typeof entry === "function" ? false : entry.detached === true;
1282
1912
  const targetMeta = readAgentMetadata(target);
1283
1913
  if (targetMeta === void 0) {
1284
1914
  this.logger.warn(`Agent "${meta.name}" hands off to ${target.name}, which is not an @Agent \u2014 skipped.`);
1285
1915
  continue;
1286
1916
  }
1287
- names.push(targetMeta.name);
1917
+ edges.push(detached ? {
1918
+ agent: targetMeta.name,
1919
+ detached: true
1920
+ } : targetMeta.name);
1288
1921
  }
1289
- return names;
1922
+ return edges;
1290
1923
  }
1291
1924
  /** Push every `@SystemPromptContributor()` method on `instance` (bound) into the contributor list. */
1292
1925
  collectContributors(instance) {
@@ -1319,12 +1952,12 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
1319
1952
  return (ctx) => candidate.call(instance, ctx);
1320
1953
  }
1321
1954
  };
1322
- AgentDiscoveryService = _ts_decorate10([
1955
+ AgentDiscoveryService = _ts_decorate12([
1323
1956
  Injectable4(),
1324
- _ts_param9(2, Inject9(AGENT_REGISTRY3)),
1325
- _ts_param9(3, Inject9(AGENT_PROMPT_CONTRIBUTORS2)),
1326
- _ts_metadata10("design:type", Function),
1327
- _ts_metadata10("design:paramtypes", [
1957
+ _ts_param11(2, Inject11(AGENT_REGISTRY3)),
1958
+ _ts_param11(3, Inject11(AGENT_PROMPT_CONTRIBUTORS2)),
1959
+ _ts_metadata12("design:type", Function),
1960
+ _ts_metadata12("design:paramtypes", [
1328
1961
  typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
1329
1962
  typeof MetadataScanner === "undefined" ? Object : MetadataScanner,
1330
1963
  typeof AgentRegistry4 === "undefined" ? Object : AgentRegistry4,
@@ -1333,8 +1966,8 @@ AgentDiscoveryService = _ts_decorate10([
1333
1966
  ], AgentDiscoveryService);
1334
1967
 
1335
1968
  // src/discovery/ai-tool-discovery.service.ts
1336
- import { AGENT_OPTIONS as AGENT_OPTIONS3, AGENT_REGISTRY as AGENT_REGISTRY4, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY2 } from "@dudousxd/nestjs-agent-core";
1337
- import { Inject as Inject10, Injectable as Injectable5, Logger as Logger2 } from "@nestjs/common";
1969
+ import { AGENT_OPTIONS as AGENT_OPTIONS3, AGENT_REGISTRY as AGENT_REGISTRY4, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY2, normalizeDelegation as normalizeDelegation2 } from "@dudousxd/nestjs-agent-core";
1970
+ import { Inject as Inject12, Injectable as Injectable5, Logger as Logger2 } from "@nestjs/common";
1338
1971
  import { DiscoveryService as DiscoveryService2 } from "@nestjs/core";
1339
1972
  import { z } from "zod";
1340
1973
 
@@ -1383,23 +2016,23 @@ function provideAgentTool(factoryOrTool, inject) {
1383
2016
  __name(provideAgentTool, "provideAgentTool");
1384
2017
 
1385
2018
  // src/discovery/ai-tool-discovery.service.ts
1386
- function _ts_decorate11(decorators, target, key, desc) {
2019
+ function _ts_decorate13(decorators, target, key, desc) {
1387
2020
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1388
2021
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1389
2022
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1390
2023
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1391
2024
  }
1392
- __name(_ts_decorate11, "_ts_decorate");
1393
- function _ts_metadata11(k, v) {
2025
+ __name(_ts_decorate13, "_ts_decorate");
2026
+ function _ts_metadata13(k, v) {
1394
2027
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1395
2028
  }
1396
- __name(_ts_metadata11, "_ts_metadata");
1397
- function _ts_param10(paramIndex, decorator) {
2029
+ __name(_ts_metadata13, "_ts_metadata");
2030
+ function _ts_param12(paramIndex, decorator) {
1398
2031
  return function(target, key) {
1399
2032
  decorator(target, key, paramIndex);
1400
2033
  };
1401
2034
  }
1402
- __name(_ts_param10, "_ts_param");
2035
+ __name(_ts_param12, "_ts_param");
1403
2036
  var AiToolDiscoveryService = class _AiToolDiscoveryService {
1404
2037
  static {
1405
2038
  __name(this, "AiToolDiscoveryService");
@@ -1475,22 +2108,30 @@ var AiToolDiscoveryService = class _AiToolDiscoveryService {
1475
2108
  }
1476
2109
  let delegates = 0;
1477
2110
  for (const definition of this.agents.list()) {
1478
- for (const target of definition.delegatesTo ?? []) {
2111
+ for (const edge of definition.delegatesTo ?? []) {
2112
+ const { agent: target, detached } = normalizeDelegation2(edge);
1479
2113
  const targetDefinition = this.agents.get(target);
1480
2114
  if (targetDefinition === void 0) {
1481
2115
  throw new Error(`Agent "${definition.name}" hands off to "${target}", which is not a registered @Agent. Declare it as an @Agent provider or fix the reference.`);
1482
2116
  }
1483
- const name = delegateToolName(target);
2117
+ const name = delegateToolName({
2118
+ target,
2119
+ detached
2120
+ });
1484
2121
  if (this.registry.has(name)) {
1485
2122
  continue;
1486
2123
  }
1487
2124
  const targetBlurb = typeof targetDefinition.systemPrompt === "string" ? ` It is: ${targetDefinition.systemPrompt}` : "";
2125
+ const description = detached ? `Start the "${target}" agent working on a task in the BACKGROUND. Returns immediately with a receipt, NOT an answer \u2014 the answer is delivered to this conversation as a separate message later. Use it for work the user does not need to sit and wait for.${targetBlurb}` : `Delegate a task to the "${target}" agent and get its answer.${targetBlurb}`;
1488
2126
  this.registry.register(
1489
2127
  {
1490
2128
  name,
1491
2129
  kind: "agent",
1492
2130
  targetAgent: target,
1493
- description: `Delegate a task to the "${target}" agent and get its answer.${targetBlurb}`,
2131
+ ...detached ? {
2132
+ detached: true
2133
+ } : {},
2134
+ description,
1494
2135
  inputSchema: z.object({
1495
2136
  task: z.string()
1496
2137
  })
@@ -1518,13 +2159,13 @@ var AiToolDiscoveryService = class _AiToolDiscoveryService {
1518
2159
  return true;
1519
2160
  }
1520
2161
  };
1521
- AiToolDiscoveryService = _ts_decorate11([
2162
+ AiToolDiscoveryService = _ts_decorate13([
1522
2163
  Injectable5(),
1523
- _ts_param10(1, Inject10(AGENT_TOOL_REGISTRY2)),
1524
- _ts_param10(2, Inject10(AGENT_REGISTRY4)),
1525
- _ts_param10(3, Inject10(AGENT_OPTIONS3)),
1526
- _ts_metadata11("design:type", Function),
1527
- _ts_metadata11("design:paramtypes", [
2164
+ _ts_param12(1, Inject12(AGENT_TOOL_REGISTRY2)),
2165
+ _ts_param12(2, Inject12(AGENT_REGISTRY4)),
2166
+ _ts_param12(3, Inject12(AGENT_OPTIONS3)),
2167
+ _ts_metadata13("design:type", Function),
2168
+ _ts_metadata13("design:paramtypes", [
1528
2169
  typeof DiscoveryService2 === "undefined" ? Object : DiscoveryService2,
1529
2170
  typeof ToolRegistry === "undefined" ? Object : ToolRegistry,
1530
2171
  typeof AgentRegistry === "undefined" ? Object : AgentRegistry,
@@ -1532,6 +2173,101 @@ AiToolDiscoveryService = _ts_decorate11([
1532
2173
  ])
1533
2174
  ], AiToolDiscoveryService);
1534
2175
 
2176
+ // src/discovery/skill-discovery.service.ts
2177
+ import { AGENT_SKILL_SOURCES } from "@dudousxd/nestjs-agent-core";
2178
+ import { Inject as Inject13, Injectable as Injectable6, Logger as Logger3 } from "@nestjs/common";
2179
+ import { DiscoveryService as DiscoveryService3 } from "@nestjs/core";
2180
+
2181
+ // src/decorator/skill.decorator.ts
2182
+ import "reflect-metadata";
2183
+ import { GLOBAL_SCOPE } from "@dudousxd/nestjs-agent-core";
2184
+ var SKILL_METADATA = Symbol.for("@dudousxd/nestjs-agent:skill-metadata");
2185
+ function Skill(options) {
2186
+ return (target) => {
2187
+ Reflect.defineMetadata(SKILL_METADATA, options, target);
2188
+ };
2189
+ }
2190
+ __name(Skill, "Skill");
2191
+ function readSkillMetadata(target) {
2192
+ return Reflect.getMetadata(SKILL_METADATA, target);
2193
+ }
2194
+ __name(readSkillMetadata, "readSkillMetadata");
2195
+ function skillScope(options) {
2196
+ return options.scope ?? GLOBAL_SCOPE;
2197
+ }
2198
+ __name(skillScope, "skillScope");
2199
+
2200
+ // src/discovery/skill-discovery.service.ts
2201
+ function _ts_decorate14(decorators, target, key, desc) {
2202
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2203
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2204
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2205
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2206
+ }
2207
+ __name(_ts_decorate14, "_ts_decorate");
2208
+ function _ts_metadata14(k, v) {
2209
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2210
+ }
2211
+ __name(_ts_metadata14, "_ts_metadata");
2212
+ function _ts_param13(paramIndex, decorator) {
2213
+ return function(target, key) {
2214
+ decorator(target, key, paramIndex);
2215
+ };
2216
+ }
2217
+ __name(_ts_param13, "_ts_param");
2218
+ var SkillDiscoveryService = class _SkillDiscoveryService {
2219
+ static {
2220
+ __name(this, "SkillDiscoveryService");
2221
+ }
2222
+ discovery;
2223
+ declared;
2224
+ logger = new Logger3(_SkillDiscoveryService.name);
2225
+ constructor(discovery, declared) {
2226
+ this.discovery = discovery;
2227
+ this.declared = declared;
2228
+ }
2229
+ onModuleInit() {
2230
+ for (const wrapper of this.discovery.getProviders()) {
2231
+ const instance = wrapper.instance;
2232
+ if (instance === null || typeof instance !== "object") {
2233
+ continue;
2234
+ }
2235
+ const meta = readSkillMetadata(instance.constructor);
2236
+ if (meta === void 0) {
2237
+ continue;
2238
+ }
2239
+ const body = this.bodyOf(instance, meta.body);
2240
+ if (body === void 0) {
2241
+ this.logger.warn(`Skill "${meta.name}" declares no body \u2014 add a \`body()\` method or \`@Skill({ body })\`. Skipped.`);
2242
+ continue;
2243
+ }
2244
+ this.declared.push({
2245
+ name: meta.name,
2246
+ description: meta.description,
2247
+ scope: skillScope(meta),
2248
+ body
2249
+ });
2250
+ }
2251
+ }
2252
+ /** The instance's `body()` method (bound), else the flat string, else nothing to offer. */
2253
+ bodyOf(instance, flat) {
2254
+ const candidate = instance.body;
2255
+ if (typeof candidate === "function") {
2256
+ return (ctx) => candidate.call(instance, ctx);
2257
+ }
2258
+ return flat === void 0 ? void 0 : () => flat;
2259
+ }
2260
+ };
2261
+ SkillDiscoveryService = _ts_decorate14([
2262
+ Injectable6(),
2263
+ _ts_param13(1, Inject13(AGENT_SKILL_SOURCES)),
2264
+ _ts_metadata14("design:type", Function),
2265
+ _ts_metadata14("design:paramtypes", [
2266
+ typeof DiscoveryService3 === "undefined" ? Object : DiscoveryService3,
2267
+ Array
2268
+ ])
2269
+ ], SkillDiscoveryService);
2270
+
1535
2271
  // src/in-process-sink.ts
1536
2272
  import { AgentStreamError as AgentStreamError2 } from "@dudousxd/nestjs-agent-core";
1537
2273
  var InProcessTokenStreamSink = class {
@@ -1619,38 +2355,44 @@ var LedgerQuotaStore = class {
1619
2355
  withinLimit: usedTokens < this.limitTokens
1620
2356
  };
1621
2357
  }
1622
- async bump() {
2358
+ async bump(_actorRef, _day, _tokens) {
1623
2359
  }
1624
2360
  };
1625
2361
 
1626
2362
  // src/runner/inline-agent-runner.ts
1627
- import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY3, AGENT_STORE as AGENT_STORE3, QuotaExceededError, publishAgentRunFailed, runAgentLoop } from "@dudousxd/nestjs-agent-core";
1628
- import { Inject as Inject11, Injectable as Injectable6, Logger as Logger3 } from "@nestjs/common";
1629
- function _ts_decorate12(decorators, target, key, desc) {
2363
+ import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY3, AGENT_STORE as AGENT_STORE3, RunCancelledError, agentFailureCode, encodeStreamEvent, publishAgentRunFailed, runAgentLoop, settleAll, settleUnsettledDelegation } from "@dudousxd/nestjs-agent-core";
2364
+ import { Inject as Inject14, Injectable as Injectable7, Logger as Logger4 } from "@nestjs/common";
2365
+ function _ts_decorate15(decorators, target, key, desc) {
1630
2366
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1631
2367
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1632
2368
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1633
2369
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1634
2370
  }
1635
- __name(_ts_decorate12, "_ts_decorate");
1636
- function _ts_metadata12(k, v) {
2371
+ __name(_ts_decorate15, "_ts_decorate");
2372
+ function _ts_metadata15(k, v) {
1637
2373
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1638
2374
  }
1639
- __name(_ts_metadata12, "_ts_metadata");
1640
- function _ts_param11(paramIndex, decorator) {
2375
+ __name(_ts_metadata15, "_ts_metadata");
2376
+ function _ts_param14(paramIndex, decorator) {
1641
2377
  return function(target, key) {
1642
2378
  decorator(target, key, paramIndex);
1643
2379
  };
1644
2380
  }
1645
- __name(_ts_param11, "_ts_param");
2381
+ __name(_ts_param14, "_ts_param");
1646
2382
  var InlineAgentRunner = class _InlineAgentRunner {
1647
2383
  static {
1648
2384
  __name(this, "InlineAgentRunner");
1649
2385
  }
1650
2386
  factory;
1651
2387
  store;
1652
- logger = new Logger3(_InlineAgentRunner.name);
2388
+ logger = new Logger4(_InlineAgentRunner.name);
1653
2389
  pending = /* @__PURE__ */ new Map();
2390
+ /**
2391
+ * Runs someone has asked to stop. In-process, like `pending`, and for the same reason: this runner
2392
+ * is single-replica by construction, so the request is readable by the only loop that could be
2393
+ * running it. A cancelled id is dropped once the run settles, so the set tracks live runs only.
2394
+ */
2395
+ cancelled = /* @__PURE__ */ new Set();
1654
2396
  constructor(factory, store) {
1655
2397
  this.factory = factory;
1656
2398
  this.store = store;
@@ -1659,13 +2401,29 @@ var InlineAgentRunner = class _InlineAgentRunner {
1659
2401
  const runId = crypto.randomUUID();
1660
2402
  const day = input.day ?? utcDay();
1661
2403
  const deps = this.factory.forAgent(input.agentName);
1662
- const hooks = this.topLevelHooks(runId, deps, input.actor, day);
2404
+ const hooks = this.topLevelHooks({
2405
+ runId,
2406
+ deps,
2407
+ actor: input.actor,
2408
+ day,
2409
+ threadId: input.threadId,
2410
+ chainBelow: [
2411
+ ...input.delegationPath ?? [],
2412
+ ...input.agentName !== void 0 ? [
2413
+ input.agentName
2414
+ ] : []
2415
+ ]
2416
+ });
1663
2417
  void runAgentLoop({
1664
2418
  ...deps,
1665
2419
  day
1666
2420
  }, input, hooks).then(() => this.store.setActiveStream(input.threadId, null)).catch(async (error) => {
2421
+ if (error instanceof RunCancelledError) {
2422
+ await this.settleCancelled(runId, input.threadId, deps);
2423
+ return;
2424
+ }
1667
2425
  const message = error instanceof Error ? error.message : String(error);
1668
- const code = error instanceof QuotaExceededError ? "quota_exceeded" : "run_failed";
2426
+ const code = agentFailureCode(error);
1669
2427
  this.logger.error(`agent run ${runId} failed (${code}): ${message}`);
1670
2428
  publishAgentRunFailed({
1671
2429
  runId,
@@ -1684,33 +2442,216 @@ var InlineAgentRunner = class _InlineAgentRunner {
1684
2442
  code,
1685
2443
  message
1686
2444
  });
1687
- });
2445
+ }).finally(() => this.cancelled.delete(runId));
1688
2446
  return {
1689
2447
  runId
1690
2448
  };
1691
2449
  }
1692
- async signal(runId, toolCallId, decision) {
1693
- const key = `${runId}:${toolCallId}`;
1694
- const resolve = this.pending.get(key);
1695
- if (resolve !== void 0) {
2450
+ async signal(runId, toolCallId2, reply) {
2451
+ const key = `${runId}:${toolCallId2}`;
2452
+ const waiter = this.pending.get(key);
2453
+ if (waiter !== void 0) {
1696
2454
  this.pending.delete(key);
1697
- resolve(decision);
2455
+ waiter.resolve(reply);
1698
2456
  }
1699
2457
  }
2458
+ /**
2459
+ * Ask a run to stop, and settle whatever it was parked on.
2460
+ *
2461
+ * The loop observes the request at its next safe point (between steps, before the turn's tools) and
2462
+ * unwinds from there — so a tool already executing is left to finish, and its result is recorded
2463
+ * exactly as it would have been. What this cannot wait for is a turn parked on a human: that wait
2464
+ * is a promise nobody is going to resolve, so it is REJECTED here with the same
2465
+ * {@link RunCancelledError} the loop would have thrown, and the run unwinds through the identical
2466
+ * path.
2467
+ *
2468
+ * Settlement — the run row, the thread's active stream, the stream's terminal — belongs to the
2469
+ * `RunCancelledError` catch in `start`, so a cancel and an ordinary failure settle in one place
2470
+ * each and can never both write.
2471
+ */
1700
2472
  async cancel(runId) {
1701
- const deps = this.factory.forAgent();
2473
+ this.cancelled.add(runId);
2474
+ for (const [key, waiter] of this.pending) {
2475
+ if (key.startsWith(`${runId}:`)) {
2476
+ this.pending.delete(key);
2477
+ waiter.reject(new RunCancelledError());
2478
+ }
2479
+ }
2480
+ await Promise.resolve();
2481
+ }
2482
+ /**
2483
+ * The single place a cancelled run's state is settled: recorded as its own terminal (never
2484
+ * `failed` — a user pressing Stop must not land in someone's error rate), the thread released, and
2485
+ * the stream ENDED after a `cancelled` frame rather than failed, so a client that retries failed
2486
+ * streams does not retry this one.
2487
+ */
2488
+ async settleCancelled(runId, threadId, deps) {
2489
+ this.logger.log(`agent run ${runId} cancelled`);
2490
+ await this.store.setActiveStream(threadId, null);
1702
2491
  const writer = await deps.sink.open(runId);
2492
+ await writer.write(encodeStreamEvent({
2493
+ kind: "cancelled"
2494
+ }));
1703
2495
  await writer.end();
2496
+ await this.store.recordRunEnd?.({
2497
+ runId,
2498
+ status: "cancelled"
2499
+ });
2500
+ }
2501
+ /**
2502
+ * Park a run on a human, keyed by run + tool call. One map for approvals and for question-set
2503
+ * answers alike: both wait on the same key, and `signal` cannot tell (or need to tell) which
2504
+ * shape it is delivering — the loop asked for one and only ever gets what the caller sent. A
2505
+ * cancel settles the wait too, by rejecting it.
2506
+ */
2507
+ park(runId, toolCallId2) {
2508
+ return new Promise((resolve, reject) => {
2509
+ this.pending.set(`${runId}:${toolCallId2}`, {
2510
+ resolve,
2511
+ reject
2512
+ });
2513
+ });
1704
2514
  }
1705
- topLevelHooks(runId, deps, actor, day) {
2515
+ topLevelHooks(args) {
2516
+ const { runId, deps, actor, day, threadId, chainBelow } = args;
1706
2517
  return {
1707
2518
  runId,
1708
2519
  openSink: /* @__PURE__ */ __name(() => deps.sink.open(runId), "openSink"),
1709
- awaitApproval: /* @__PURE__ */ __name((call) => new Promise((resolve) => {
1710
- this.pending.set(`${runId}:${call.id}`, resolve);
1711
- }), "awaitApproval"),
2520
+ awaitApproval: /* @__PURE__ */ __name((call) => this.park(runId, call.id), "awaitApproval"),
2521
+ awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
1712
2522
  step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
1713
- runAgent: /* @__PURE__ */ __name((agentName, task) => this.runNested(agentName, task, actor, day, 1, runId), "runAgent")
2523
+ parallel: settleAll,
2524
+ cancelled: /* @__PURE__ */ __name(async () => this.cancelled.has(runId), "cancelled"),
2525
+ runAgent: /* @__PURE__ */ __name((agentName, task) => this.runNested({
2526
+ agentName,
2527
+ task,
2528
+ actor,
2529
+ day,
2530
+ depth: 1,
2531
+ path: chainBelow,
2532
+ sinkRunId: runId,
2533
+ parentRunId: runId
2534
+ }), "runAgent"),
2535
+ startAgent: /* @__PURE__ */ __name(({ agentName, task, toolCallId: toolCallId2 }) => this.startDetached({
2536
+ agentName,
2537
+ task,
2538
+ actor,
2539
+ day,
2540
+ depth: 1,
2541
+ path: chainBelow,
2542
+ parentRunId: runId,
2543
+ deliverTo: {
2544
+ threadId,
2545
+ toolCallId: toolCallId2
2546
+ }
2547
+ }), "startAgent")
2548
+ };
2549
+ }
2550
+ /**
2551
+ * Delegate WITHOUT waiting: a nested loop nobody awaits, on its own thread and its own sink, which
2552
+ * posts its answer back into the delegating thread when it lands (`deliverTo`, settled by the
2553
+ * loop). The calling turn gets the run id straight back and finishes.
2554
+ *
2555
+ * No `sinkRunId`, unlike {@link runNested}: forwarding into the stream the human is watching would
2556
+ * write into a turn that has already ended. Its own approvals resolve through the shared pending
2557
+ * map keyed by its own runId — which is what the pending-approvals surface routes to.
2558
+ */
2559
+ async startDetached(args) {
2560
+ const { agentName, task, actor, day, depth, path, parentRunId, deliverTo } = args;
2561
+ const subThread = await this.store.createThread({
2562
+ actor,
2563
+ transient: true
2564
+ });
2565
+ const runId = crypto.randomUUID();
2566
+ await this.store.setActiveStream(subThread.id, runId);
2567
+ const deps = this.factory.forAgent(agentName);
2568
+ const hooks = {
2569
+ runId,
2570
+ openSink: /* @__PURE__ */ __name(() => deps.sink.open(runId), "openSink"),
2571
+ awaitApproval: /* @__PURE__ */ __name((call) => this.park(runId, call.id), "awaitApproval"),
2572
+ awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
2573
+ step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
2574
+ parallel: settleAll,
2575
+ cancelled: /* @__PURE__ */ __name(async () => this.cancelled.has(runId), "cancelled"),
2576
+ runAgent: /* @__PURE__ */ __name((childName, childTask) => this.runNested({
2577
+ agentName: childName,
2578
+ task: childTask,
2579
+ actor,
2580
+ day,
2581
+ depth: depth + 1,
2582
+ path: [
2583
+ ...path,
2584
+ agentName
2585
+ ],
2586
+ // A detached run owns a stream of its own, so ITS sub-agents forward into that one.
2587
+ sinkRunId: runId,
2588
+ parentRunId: runId
2589
+ }), "runAgent"),
2590
+ startAgent: /* @__PURE__ */ __name(({ agentName: childName, task: childTask, toolCallId: toolCallId2 }) => this.startDetached({
2591
+ agentName: childName,
2592
+ task: childTask,
2593
+ actor,
2594
+ day,
2595
+ depth: depth + 1,
2596
+ path: [
2597
+ ...path,
2598
+ agentName
2599
+ ],
2600
+ parentRunId: runId,
2601
+ deliverTo: {
2602
+ threadId: subThread.id,
2603
+ toolCallId: toolCallId2
2604
+ }
2605
+ }), "startAgent")
2606
+ };
2607
+ void runAgentLoop({
2608
+ ...deps,
2609
+ day
2610
+ }, {
2611
+ threadId: subThread.id,
2612
+ actor,
2613
+ userText: task,
2614
+ agentName,
2615
+ day,
2616
+ delegationDepth: depth,
2617
+ delegationPath: path,
2618
+ parentRunId,
2619
+ deliverTo
2620
+ }, hooks).catch(async (error) => {
2621
+ const cancelled = error instanceof RunCancelledError;
2622
+ const message = error instanceof Error ? error.message : String(error);
2623
+ if (!cancelled) {
2624
+ this.logger.error(`detached agent run ${runId} failed: ${message}`);
2625
+ publishAgentRunFailed({
2626
+ runId,
2627
+ code: agentFailureCode(error),
2628
+ message
2629
+ });
2630
+ }
2631
+ await this.store.recordRunEnd?.({
2632
+ runId,
2633
+ status: cancelled ? "cancelled" : "failed",
2634
+ ...cancelled ? {} : {
2635
+ errorCode: agentFailureCode(error),
2636
+ errorMessage: message
2637
+ }
2638
+ });
2639
+ await settleUnsettledDelegation({
2640
+ store: this.store,
2641
+ delivery: deliverTo,
2642
+ agent: agentName,
2643
+ runId,
2644
+ status: cancelled ? "cancelled" : "failed",
2645
+ ...cancelled ? {} : {
2646
+ error: message
2647
+ }
2648
+ });
2649
+ }).finally(async () => {
2650
+ this.cancelled.delete(runId);
2651
+ await this.store.setActiveStream(subThread.id, null);
2652
+ });
2653
+ return {
2654
+ runId
1714
2655
  };
1715
2656
  }
1716
2657
  /**
@@ -1718,7 +2659,8 @@ var InlineAgentRunner = class _InlineAgentRunner {
1718
2659
  * (the top-level run the human is watching) so its output and any pending action tool are visible,
1719
2660
  * and its own approvals resolve through the shared pending map keyed by its own runId.
1720
2661
  */
1721
- async runNested(agentName, task, actor, day, depth, sinkRunId) {
2662
+ async runNested(args) {
2663
+ const { agentName, task, actor, day, depth, path, sinkRunId, parentRunId } = args;
1722
2664
  const subThread = await this.store.createThread({
1723
2665
  actor,
1724
2666
  transient: true
@@ -1730,11 +2672,42 @@ var InlineAgentRunner = class _InlineAgentRunner {
1730
2672
  runId,
1731
2673
  // Forward into the top-level stream; the top-level run owns end/fail on that shared sink.
1732
2674
  openSink: /* @__PURE__ */ __name(async () => childSinkWriter(await deps.sink.open(sinkRunId)), "openSink"),
1733
- awaitApproval: /* @__PURE__ */ __name((call) => new Promise((resolve) => {
1734
- this.pending.set(`${runId}:${call.id}`, resolve);
1735
- }), "awaitApproval"),
2675
+ awaitApproval: /* @__PURE__ */ __name((call) => this.park(runId, call.id), "awaitApproval"),
2676
+ awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
1736
2677
  step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
1737
- runAgent: /* @__PURE__ */ __name((childName, childTask) => this.runNested(childName, childTask, actor, day, depth + 1, sinkRunId), "runAgent")
2678
+ parallel: settleAll,
2679
+ // A child stops when it is cancelled OR when the run a human is actually watching is: the
2680
+ // canceller names the top-level run, which is the only id that ever left the server.
2681
+ cancelled: /* @__PURE__ */ __name(async () => this.cancelled.has(runId) || this.cancelled.has(sinkRunId), "cancelled"),
2682
+ runAgent: /* @__PURE__ */ __name((childName, childTask) => this.runNested({
2683
+ agentName: childName,
2684
+ task: childTask,
2685
+ actor,
2686
+ day,
2687
+ depth: depth + 1,
2688
+ path: [
2689
+ ...path,
2690
+ agentName
2691
+ ],
2692
+ sinkRunId,
2693
+ parentRunId: runId
2694
+ }), "runAgent"),
2695
+ startAgent: /* @__PURE__ */ __name(({ agentName: childName, task: childTask, toolCallId: toolCallId2 }) => this.startDetached({
2696
+ agentName: childName,
2697
+ task: childTask,
2698
+ actor,
2699
+ day,
2700
+ depth: depth + 1,
2701
+ path: [
2702
+ ...path,
2703
+ agentName
2704
+ ],
2705
+ parentRunId: runId,
2706
+ deliverTo: {
2707
+ threadId: subThread.id,
2708
+ toolCallId: toolCallId2
2709
+ }
2710
+ }), "startAgent")
1738
2711
  };
1739
2712
  try {
1740
2713
  return await runAgentLoop({
@@ -1747,6 +2720,8 @@ var InlineAgentRunner = class _InlineAgentRunner {
1747
2720
  agentName,
1748
2721
  day,
1749
2722
  delegationDepth: depth,
2723
+ delegationPath: path,
2724
+ parentRunId,
1750
2725
  sinkRunId
1751
2726
  }, hooks);
1752
2727
  } finally {
@@ -1754,25 +2729,63 @@ var InlineAgentRunner = class _InlineAgentRunner {
1754
2729
  }
1755
2730
  }
1756
2731
  };
1757
- InlineAgentRunner = _ts_decorate12([
1758
- Injectable6(),
1759
- _ts_param11(0, Inject11(AGENT_DEPS_FACTORY3)),
1760
- _ts_param11(1, Inject11(AGENT_STORE3)),
1761
- _ts_metadata12("design:type", Function),
1762
- _ts_metadata12("design:paramtypes", [
2732
+ InlineAgentRunner = _ts_decorate15([
2733
+ Injectable7(),
2734
+ _ts_param14(0, Inject14(AGENT_DEPS_FACTORY3)),
2735
+ _ts_param14(1, Inject14(AGENT_STORE3)),
2736
+ _ts_metadata15("design:type", Function),
2737
+ _ts_metadata15("design:paramtypes", [
1763
2738
  typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory,
1764
2739
  typeof AgentStore === "undefined" ? Object : AgentStore
1765
2740
  ])
1766
2741
  ], InlineAgentRunner);
1767
2742
 
2743
+ // src/skills-config.ts
2744
+ import { compositeSkillProvider } from "@dudousxd/nestjs-agent-core";
2745
+ function declaredSkillProvider(declared) {
2746
+ return {
2747
+ list: /* @__PURE__ */ __name(({ scopes }) => declared.filter((skill) => scopes.includes(skill.scope)).map(({ name, description, scope }) => ({
2748
+ name,
2749
+ description,
2750
+ scope
2751
+ })), "list"),
2752
+ load: /* @__PURE__ */ __name(async ({ name, scope, ctx }) => {
2753
+ const match = declared.find((skill) => skill.name === name && skill.scope === scope);
2754
+ return match === void 0 ? null : await match.body(ctx);
2755
+ }, "load")
2756
+ };
2757
+ }
2758
+ __name(declaredSkillProvider, "declaredSkillProvider");
2759
+ function resolveSkillsConfig(options, declared) {
2760
+ if (options === void 0) {
2761
+ return void 0;
2762
+ }
2763
+ const providers = options.provider === void 0 ? [
2764
+ declaredSkillProvider(declared)
2765
+ ] : [
2766
+ options.provider,
2767
+ declaredSkillProvider(declared)
2768
+ ];
2769
+ return {
2770
+ provider: providers.length === 1 ? providers[0] : compositeSkillProvider(providers),
2771
+ ...options.scopes !== void 0 ? {
2772
+ scopes: options.scopes
2773
+ } : {},
2774
+ ...options.maxSkills !== void 0 ? {
2775
+ maxSkills: options.maxSkills
2776
+ } : {}
2777
+ };
2778
+ }
2779
+ __name(resolveSkillsConfig, "resolveSkillsConfig");
2780
+
1768
2781
  // src/agent.module.ts
1769
- function _ts_decorate13(decorators, target, key, desc) {
2782
+ function _ts_decorate16(decorators, target, key, desc) {
1770
2783
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1771
2784
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1772
2785
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1773
2786
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1774
2787
  }
1775
- __name(_ts_decorate13, "_ts_decorate");
2788
+ __name(_ts_decorate16, "_ts_decorate");
1776
2789
  var DEFAULT_PATH = "agent";
1777
2790
  function sharedProviders(durable, includeStore) {
1778
2791
  const providers = [
@@ -1790,6 +2803,32 @@ function sharedProviders(durable, includeStore) {
1790
2803
  provide: AGENT_PROMPT_CONTRIBUTORS3,
1791
2804
  useFactory: /* @__PURE__ */ __name(() => [], "useFactory")
1792
2805
  },
2806
+ // Likewise for `@Skill`-decorated providers — filled by SkillDiscoveryService at onModuleInit,
2807
+ // read lazily by the AGENT_SKILLS provider below (which DI builds before discovery has run).
2808
+ {
2809
+ provide: AGENT_SKILL_SOURCES2,
2810
+ useFactory: /* @__PURE__ */ __name(() => [], "useFactory")
2811
+ },
2812
+ {
2813
+ // One resolution, shared by the loop's deps and the listing endpoint: a second one would
2814
+ // drift, and a user would be offered a skill the agent cannot reach. `undefined` when the
2815
+ // host configured none — which is what keeps the turn's checkpoint sequence unchanged.
2816
+ provide: AGENT_SKILLS3,
2817
+ useFactory: /* @__PURE__ */ __name((options, declared) => resolveSkillsConfig(options.skills, declared), "useFactory"),
2818
+ inject: [
2819
+ AGENT_OPTIONS4,
2820
+ AGENT_SKILL_SOURCES2
2821
+ ]
2822
+ },
2823
+ {
2824
+ // One resolution, shared by the loop's deps and the read-back endpoint: a person has to be
2825
+ // shown what the model was shown, and two resolutions would eventually disagree about that.
2826
+ provide: AGENT_MEMORY3,
2827
+ useFactory: /* @__PURE__ */ __name((options) => options.memory, "useFactory"),
2828
+ inject: [
2829
+ AGENT_OPTIONS4
2830
+ ]
2831
+ },
1793
2832
  {
1794
2833
  provide: AGENT_SINK2,
1795
2834
  useFactory: /* @__PURE__ */ __name((options) => options.sink ?? new InProcessTokenStreamSink(), "useFactory"),
@@ -1805,7 +2844,7 @@ function sharedProviders(durable, includeStore) {
1805
2844
  ]
1806
2845
  },
1807
2846
  {
1808
- provide: AGENT_ACTOR_RESOLVER6,
2847
+ provide: AGENT_ACTOR_RESOLVER8,
1809
2848
  useFactory: /* @__PURE__ */ __name((options) => options.actorResolver, "useFactory"),
1810
2849
  inject: [
1811
2850
  AGENT_OPTIONS4
@@ -1838,6 +2877,7 @@ function sharedProviders(durable, includeStore) {
1838
2877
  // handoff tools (onApplicationBootstrap) reads the registry.
1839
2878
  AgentDiscoveryService,
1840
2879
  AiToolDiscoveryService,
2880
+ SkillDiscoveryService,
1841
2881
  InlineAgentRunner,
1842
2882
  AgentService,
1843
2883
  // Bound ALWAYS (durable or inline) — the console's cross-thread approvals inbox routes decisions
@@ -1889,13 +2929,15 @@ function exportsFor(includeStore) {
1889
2929
  AGENT_REGISTRY5,
1890
2930
  AGENT_SINK2,
1891
2931
  AGENT_ROLES_POLICY2,
1892
- AGENT_ACTOR_RESOLVER6,
2932
+ AGENT_ACTOR_RESOLVER8,
1893
2933
  AGENT_MODEL2,
1894
2934
  ...includeStore ? [
1895
2935
  AGENT_STORE4
1896
2936
  ] : [],
1897
2937
  AGENT_QUOTA_STORE3,
1898
2938
  AGENT_PROMPT_CONTRIBUTORS3,
2939
+ AGENT_SKILLS3,
2940
+ AGENT_MEMORY3,
1899
2941
  AGENT_DEPS_FACTORY4,
1900
2942
  AgentDepsFactory2,
1901
2943
  AgentService,
@@ -1909,7 +2951,9 @@ var BASE_CONTROLLERS = [
1909
2951
  ThreadsController,
1910
2952
  ToolCallController,
1911
2953
  QuotaController,
1912
- AgentsController
2954
+ AgentsController,
2955
+ SkillsController,
2956
+ MemoriesController
1913
2957
  ];
1914
2958
  var GUARDABLE_CONTROLLERS = [
1915
2959
  ...BASE_CONTROLLERS,
@@ -2024,12 +3068,13 @@ var AgentModule = class _AgentModule {
2024
3068
  };
2025
3069
  }
2026
3070
  };
2027
- AgentModule = _ts_decorate13([
3071
+ AgentModule = _ts_decorate16([
2028
3072
  Global(),
2029
3073
  Module({})
2030
3074
  ], AgentModule);
2031
3075
 
2032
3076
  // src/resolver/header-actor-resolver.ts
3077
+ import { UnauthorizedException } from "@nestjs/common";
2033
3078
  function readHeader(req, name) {
2034
3079
  if (typeof req !== "object" || req === null || !("headers" in req)) return void 0;
2035
3080
  const { headers } = req;
@@ -2045,7 +3090,7 @@ var HeaderActorResolver = class {
2045
3090
  resolve(req) {
2046
3091
  const id = readHeader(req, "x-actor-id");
2047
3092
  if (id === void 0 || id.length === 0) {
2048
- throw new Error("HeaderActorResolver: missing x-actor-id header. No default actor is fabricated. Send x-actor-id from a trusted gateway, or configure AgentModule.forRoot({ actorResolver }) with a resolver that reads your authenticated principal.");
3093
+ throw new UnauthorizedException("HeaderActorResolver: missing x-actor-id header. No default actor is fabricated. Send x-actor-id from a trusted gateway, or configure AgentModule.forRoot({ actorResolver }) with a resolver that reads your authenticated principal.");
2049
3094
  }
2050
3095
  const roles = (readHeader(req, "x-actor-role") ?? "").split(",").map((role) => role.trim()).filter((role) => role.length > 0);
2051
3096
  const tenantRef = readHeader(req, "x-tenant-ref");
@@ -2065,6 +3110,7 @@ export {
2065
3110
  AGENT_METADATA,
2066
3111
  AGENT_TOOL_BRAND,
2067
3112
  AI_TOOL_METADATA,
3113
+ ATTACHMENT_PAGE_SIZE,
2068
3114
  Agent,
2069
3115
  AgentApprovalPortAdapter,
2070
3116
  AgentDepsFactory2 as AgentDepsFactory,
@@ -2080,14 +3126,23 @@ export {
2080
3126
  InProcessTokenStreamSink,
2081
3127
  InlineAgentRunner,
2082
3128
  LedgerQuotaStore,
3129
+ MemoriesController,
3130
+ SKILL_METADATA,
2083
3131
  SYSTEM_PROMPT_CONTRIBUTOR_METADATA,
2084
3132
  SYSTEM_PROMPT_METADATA,
3133
+ Skill,
3134
+ SkillDiscoveryService,
3135
+ SkillsController,
2085
3136
  SystemPrompt,
2086
3137
  SystemPromptContributor,
3138
+ declaredSkillProvider,
2087
3139
  delegateToolName,
2088
3140
  provideAgentTool,
2089
3141
  readAgentMetadata,
2090
3142
  readAiToolMetadata,
3143
+ readSkillMetadata,
3144
+ resolveSkillsConfig,
3145
+ skillScope,
2091
3146
  utcDay
2092
3147
  };
2093
3148
  //# sourceMappingURL=index.js.map