@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.
@@ -2,10 +2,10 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // src/durable/agent-run.workflow.ts
5
- import { AGENT_DEPS_FACTORY, AGENT_STORE, QuotaExceededError, publishAgentRunFailed, runAgentLoop } from "@dudousxd/nestjs-agent-core";
6
- import { Workflow } from "@dudousxd/nestjs-durable";
5
+ import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY2, AGENT_STORE, RunCancelledError, agentFailureCode, isReplayIntegrityError, publishAgentRunFailed, runAgentLoop, settleAll, settleUnsettledDelegation } from "@dudousxd/nestjs-agent-core";
6
+ import { RUN_GATEWAY, Workflow } from "@dudousxd/nestjs-durable";
7
7
  import { isWorkflowControlFlowSignal } from "@dudousxd/nestjs-durable-core";
8
- import { Inject, Injectable, Optional } from "@nestjs/common";
8
+ import { Inject as Inject2, Injectable as Injectable2, Optional } from "@nestjs/common";
9
9
 
10
10
  // src/agent-deps.ts
11
11
  function utcDay(date = /* @__PURE__ */ new Date()) {
@@ -23,10 +23,10 @@ function childSinkWriter(writer) {
23
23
  }
24
24
  __name(childSinkWriter, "childSinkWriter");
25
25
 
26
- // src/durable/dispatched-steps.token.ts
27
- var AGENT_DISPATCHED_STEPS = Symbol.for("@dudousxd/nestjs-agent:dispatched-steps");
28
-
29
- // src/durable/agent-run.workflow.ts
26
+ // src/durable/agent-run.steps.ts
27
+ import { AGENT_DEPS_FACTORY, createFrameBuffer, invokeWithTransientRetry, publishAgentToolRetry, traceLlmTurn, traceToolExecution, withAskTool, withMemoryTool, withSkillTool, withToolTimeout } from "@dudousxd/nestjs-agent-core";
28
+ import { Step } from "@dudousxd/nestjs-durable";
29
+ import { Inject, Injectable } from "@nestjs/common";
30
30
  function _ts_decorate(decorators, target, key, desc) {
31
31
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
32
32
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -44,34 +44,236 @@ function _ts_param(paramIndex, decorator) {
44
44
  };
45
45
  }
46
46
  __name(_ts_param, "_ts_param");
47
+ var AgentRunSteps = class {
48
+ static {
49
+ __name(this, "AgentRunSteps");
50
+ }
51
+ factory;
52
+ constructor(factory) {
53
+ this.factory = factory;
54
+ }
55
+ /**
56
+ * Retryable: every side effect (persist/quota/stream markers) happens loop-side under
57
+ * `hooks.step`, so a durable retry only re-runs the model call and re-streams its tokens — never
58
+ * double-writes. NEVER call `writer.end()`/`fail()` here: the workflow owns the stream's lifecycle,
59
+ * across however many turns/retries it takes.
60
+ */
61
+ async llm(input) {
62
+ const deps = this.factory.forAgent(input.agentName);
63
+ const tools = withMemoryTool({
64
+ tools: withSkillTool({
65
+ tools: withAskTool({
66
+ tools: await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, deps.toolAllowList),
67
+ ask: deps.ask
68
+ }),
69
+ enabled: deps.skills !== void 0
70
+ }),
71
+ enabled: deps.memory?.provider.write !== void 0
72
+ });
73
+ const buffer = input.bufferOutput === true ? createFrameBuffer() : void 0;
74
+ let writer;
75
+ if (buffer !== void 0) {
76
+ writer = buffer.writer;
77
+ } else {
78
+ const opened = await deps.sink.open(input.sinkRunId);
79
+ writer = input.childSink ? childSinkWriter(opened) : opened;
80
+ }
81
+ const turn = await traceLlmTurn(input.runId, input.step, () => deps.model.runTurn({
82
+ system: input.system,
83
+ messages: input.messages,
84
+ tools,
85
+ sink: writer
86
+ }));
87
+ return buffer === void 0 ? turn : {
88
+ ...turn,
89
+ bufferedFrames: buffer.frames()
90
+ };
91
+ }
92
+ /**
93
+ * NO DURABLE retries — a tool may not be idempotent (a bare `@Step()` carries none, unlike `llm`
94
+ * above). What it does retry, in place, is a CLASSIFIED-TRANSIENT error from the tool's own
95
+ * invocation (a DB deadlock, a lock-wait timeout — see `isTransientToolError`): the server rolled
96
+ * that work back, so re-invoking it is safe, and doing it here (inside this one step, never a new
97
+ * checkpoint) is exactly how the non-dispatched loop path handles it too. The timeout is applied
98
+ * HERE, handler-side, via `withToolTimeout` — never as a durable dispatch `timeoutMs`, which
99
+ * bounds engine liveness (retryable) rather than the tool's own business outcome — and per
100
+ * ATTEMPT, so a retried invocation gets its own fresh timeout window.
101
+ */
102
+ async tool(input) {
103
+ const deps = this.factory.forAgent(input.ctx.agentName);
104
+ if (deps.registry.spec(input.toolName)?.kind === "action" && input.toolType !== "action") {
105
+ throw new Error(`tool "${input.toolName}" is an action tool but was dispatched without approval: the dispatching process resolved its kind against a registry that does not have it`);
106
+ }
107
+ const ctx = {
108
+ ...input.ctx
109
+ };
110
+ const localClassify = deps.toolTransientRetry !== false ? deps.toolTransientRetry?.classify : void 0;
111
+ const transientRetry = input.transientRetry === false ? false : {
112
+ attempts: input.transientRetry.attempts,
113
+ backoffMs: input.transientRetry.backoffMs,
114
+ ...localClassify !== void 0 ? {
115
+ classify: localClassify
116
+ } : {}
117
+ };
118
+ return traceToolExecution(input.ctx.runId, {
119
+ toolCallId: input.toolCallId,
120
+ toolName: input.toolName,
121
+ toolType: input.toolType
122
+ }, () => invokeWithTransientRetry(() => {
123
+ const invocation = deps.registry.invoke(input.toolName, input.input, ctx, deps.rolesPolicy);
124
+ return input.timeoutMs !== void 0 ? withToolTimeout(invocation, input.timeoutMs, input.toolName) : invocation;
125
+ }, transientRetry, {
126
+ onRetry: /* @__PURE__ */ __name((attempt, error) => {
127
+ publishAgentToolRetry({
128
+ toolName: input.toolName,
129
+ toolCallId: input.toolCallId,
130
+ attempt,
131
+ message: error instanceof Error ? error.message : String(error)
132
+ });
133
+ }, "onRetry")
134
+ }));
135
+ }
136
+ };
137
+ _ts_decorate([
138
+ Step({
139
+ retries: 3
140
+ }),
141
+ _ts_metadata("design:type", Function),
142
+ _ts_metadata("design:paramtypes", [
143
+ typeof DispatchedLlmInput === "undefined" ? Object : DispatchedLlmInput
144
+ ]),
145
+ _ts_metadata("design:returntype", Promise)
146
+ ], AgentRunSteps.prototype, "llm", null);
147
+ _ts_decorate([
148
+ Step(),
149
+ _ts_metadata("design:type", Function),
150
+ _ts_metadata("design:paramtypes", [
151
+ typeof DispatchedToolInput === "undefined" ? Object : DispatchedToolInput
152
+ ]),
153
+ _ts_metadata("design:returntype", Promise)
154
+ ], AgentRunSteps.prototype, "tool", null);
155
+ AgentRunSteps = _ts_decorate([
156
+ Injectable(),
157
+ _ts_param(0, Inject(AGENT_DEPS_FACTORY)),
158
+ _ts_metadata("design:type", Function),
159
+ _ts_metadata("design:paramtypes", [
160
+ typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory
161
+ ])
162
+ ], AgentRunSteps);
163
+
164
+ // src/durable/dispatched-steps.token.ts
165
+ var AGENT_DISPATCHED_STEPS = Symbol.for("@dudousxd/nestjs-agent:dispatched-steps");
166
+
167
+ // src/durable/agent-run.workflow.ts
168
+ function _ts_decorate2(decorators, target, key, desc) {
169
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
170
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
171
+ 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;
172
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
173
+ }
174
+ __name(_ts_decorate2, "_ts_decorate");
175
+ function _ts_metadata2(k, v) {
176
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
177
+ }
178
+ __name(_ts_metadata2, "_ts_metadata");
179
+ function _ts_param2(paramIndex, decorator) {
180
+ return function(target, key) {
181
+ decorator(target, key, paramIndex);
182
+ };
183
+ }
184
+ __name(_ts_param2, "_ts_param");
47
185
  var AgentRunWorkflow = class _AgentRunWorkflow {
48
186
  static {
49
187
  __name(this, "AgentRunWorkflow");
50
188
  }
51
189
  factory;
52
190
  store;
53
- steps;
54
191
  dispatchedSteps;
55
- constructor(factory, store, steps, dispatchedSteps) {
192
+ runs;
193
+ constructor(factory, store, dispatchedSteps, runs) {
56
194
  this.factory = factory;
57
195
  this.store = store;
58
- this.steps = steps;
59
196
  this.dispatchedSteps = dispatchedSteps;
197
+ this.runs = runs;
198
+ }
199
+ /**
200
+ * Has someone asked this run to stop? Read from the runtime's own run status, which is where
201
+ * `DurableAgentRunner.cancel` puts it — `cancelling` while a compensating cancel is in flight,
202
+ * `cancelled` once it has settled.
203
+ *
204
+ * Costs a run read at each of the loop's observation points (two per step). That is a row the
205
+ * control plane already has, against a turn that is otherwise making model calls, and it buys the
206
+ * property that matters: the answer comes from a place BOTH the canceller and every pod that might
207
+ * replay this run can see.
208
+ *
209
+ * Failing to ask is not a cancel. A tenant with no gateway wired, or an operator briefly
210
+ * unreachable, answers "no" and the run carries on — the alternative is failing healthy runs
211
+ * because a status read timed out. The loop journals that answer like any other, so two processes
212
+ * can never disagree about what was seen here.
213
+ */
214
+ async isCancelled(runId) {
215
+ try {
216
+ const status = (await this.runs?.getRunDetail(runId))?.run.status;
217
+ return status === "cancelled" || status === "cancelling";
218
+ } catch {
219
+ return false;
220
+ }
221
+ }
222
+ /**
223
+ * Settle the delegation a DETACHED run was started for, when that run ends without an answer.
224
+ *
225
+ * The loop delivers its own success (`deliver:detached`) but cannot catch its own crash, and the
226
+ * runtime settles the run row without knowing anything delegated it. Left alone, the card in the
227
+ * calling conversation says "started" for ever — the one state a reader can neither wait on nor
228
+ * act on. A no-op for every other run: only a detached one carries a delivery address.
229
+ */
230
+ async settleDetachedParent(ctx, input, outcome) {
231
+ const delivery = input.deliverTo;
232
+ if (delivery === void 0) {
233
+ return;
234
+ }
235
+ await ctx.localStep("deliver:detached:unsettled", () => settleUnsettledDelegation({
236
+ store: this.store,
237
+ delivery,
238
+ agent: input.agentName ?? "default",
239
+ runId: ctx.runId,
240
+ status: outcome.status,
241
+ ...outcome.error !== void 0 ? {
242
+ error: outcome.error
243
+ } : {}
244
+ }));
60
245
  }
61
246
  async run(ctx, input) {
62
247
  const day = input.day ?? utcDay();
63
248
  const deps = this.factory.forAgent(input.agentName);
64
- const dispatchSteps = this.dispatchedSteps ? this.steps : void 0;
65
- if (input.sinkRunId !== void 0) {
249
+ const dispatchSteps = this.dispatchedSteps && await ctx.patched("agent:dispatched-steps");
250
+ if (input.sinkRunId !== void 0 || input.deliverTo !== void 0) {
66
251
  await ctx.localStep("activate", () => this.store.setActiveStream(input.threadId, ctx.runId));
67
252
  }
68
253
  const sinkRunId = input.sinkRunId ?? ctx.runId;
254
+ const chainBelow = [
255
+ ...input.delegationPath ?? [],
256
+ ...input.agentName !== void 0 ? [
257
+ input.agentName
258
+ ] : []
259
+ ];
69
260
  const hooks = {
70
261
  runId: ctx.runId,
71
262
  // A child forwards into the top-level sink but must not end/fail it (the top-level run owns it).
72
263
  openSink: /* @__PURE__ */ __name(async () => input.sinkRunId !== void 0 ? childSinkWriter(await deps.sink.open(sinkRunId)) : deps.sink.open(ctx.runId), "openSink"),
73
264
  awaitApproval: /* @__PURE__ */ __name((call) => ctx.waitForSignal(`tool:${ctx.runId}:${call.id}`), "awaitApproval"),
265
+ // The same wait, on the same signal key, carrying a typed answer instead of a yes/no — so a
266
+ // question set and an approval reach a parked run through one path, and the HTTP surface that
267
+ // settles either one is the same `POST /agent/tool-call/*` family.
268
+ awaitAnswers: /* @__PURE__ */ __name((request) => ctx.waitForSignal(`tool:${ctx.runId}:${request.id}`), "awaitAnswers"),
74
269
  step: /* @__PURE__ */ __name((name, fn) => ctx.localStep(name, fn), "step"),
270
+ // Both durable step primitives take their checkpoint position on the CALL, before their first
271
+ // await, so launching a batch in one tick — what `settleAll` does — fixes the block of
272
+ // positions in call order however the tools then settle. `ctx.patched` keeps a run that
273
+ // recorded the one-call-at-a-time shape replaying against that shape.
274
+ parallel: settleAll,
275
+ cancelled: /* @__PURE__ */ __name(() => this.isCancelled(ctx.runId), "cancelled"),
276
+ patched: /* @__PURE__ */ __name((id) => ctx.patched(id), "patched"),
75
277
  // Dispatched-step suspends must escape the loop's tool catch — control flow, not a tool
76
278
  // failure. Marker-based predicate, NOT instanceof: the signal's CLASS differs by runtime
77
279
  // (engine in-process throws durable-core's WorkflowSuspended/ContinueAsNew; the BullMQ thin
@@ -92,15 +294,54 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
92
294
  userText: task,
93
295
  day,
94
296
  delegationDepth: (input.delegationDepth ?? 0) + 1,
297
+ delegationPath: chainBelow,
298
+ parentRunId: ctx.runId,
95
299
  sinkRunId
96
300
  });
97
301
  }, "runAgent"),
302
+ // The same delegation, not awaited: `ctx.startChild` records a `spawn:<childRunId>` at this
303
+ // position and returns, so THIS turn ends while the child is still working. Two things the
304
+ // awaited form does are deliberately left off:
305
+ // - no `sinkRunId`. A detached run owns its own stream. Forwarding it into the turn that
306
+ // started it would write tokens, and a pending action-tool frame, into a stream whose
307
+ // reader has already seen `done` — and would put the child's approval card in whatever
308
+ // turn happened to be open. Its approval goes to the pending-approvals surface instead,
309
+ // which it reaches for free: the call is persisted `pending_approval` against the child's
310
+ // OWN runId, and that is what `runForToolCall` answers with.
311
+ // - `deliverTo` instead. The parent's tool result is a receipt, so the answer needs an
312
+ // address of its own, and by the time it exists nobody is holding one.
313
+ startAgent: /* @__PURE__ */ __name(async ({ agentName, task, toolCallId: toolCallId2 }) => {
314
+ const subThreadId = await ctx.localStep(`subthread:${agentName}`, async () => {
315
+ const thread = await this.store.createThread({
316
+ actor: input.actor,
317
+ transient: true
318
+ });
319
+ return thread.id;
320
+ });
321
+ const childRunId = await ctx.startChild(_AgentRunWorkflow, {
322
+ agentName,
323
+ threadId: subThreadId,
324
+ actor: input.actor,
325
+ userText: task,
326
+ day,
327
+ delegationDepth: (input.delegationDepth ?? 0) + 1,
328
+ delegationPath: chainBelow,
329
+ parentRunId: ctx.runId,
330
+ deliverTo: {
331
+ threadId: input.threadId,
332
+ toolCallId: toolCallId2
333
+ }
334
+ });
335
+ return {
336
+ runId: childRunId
337
+ };
338
+ }, "startAgent"),
98
339
  // Routes the two long steps through AgentRunSteps as engine-dispatched `ctx.step`s instead of
99
340
  // `ctx.localStep`s, so a turn isn't pinned to this workflow worker for the model call or a tool
100
341
  // execution. `sinkRunId`/`childSink` are sink routing this workflow already resolved above —
101
342
  // core's dispatchLlm signature stays sink-topology-agnostic, so we add them here, not in core.
102
- ...dispatchSteps !== void 0 ? {
103
- dispatchLlm: /* @__PURE__ */ __name((index, envelope) => ctx.step(dispatchSteps.llm, {
343
+ ...dispatchSteps ? {
344
+ dispatchLlm: /* @__PURE__ */ __name((index, envelope) => ctx.step(AgentRunSteps.prototype.llm, {
104
345
  ...envelope,
105
346
  runId: ctx.runId,
106
347
  step: index,
@@ -112,7 +353,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
112
353
  // ('agent'-kind delegations branch to runAgent earlier in the loop), so anything that
113
354
  // isn't 'action' is defensively 'read' — the same posture core takes for an
114
355
  // unresolvable `kind`.
115
- dispatchTool: /* @__PURE__ */ __name((call, envelope) => ctx.step(dispatchSteps.tool, {
356
+ dispatchTool: /* @__PURE__ */ __name((call, envelope) => ctx.step(AgentRunSteps.prototype.tool, {
116
357
  ...envelope,
117
358
  toolCallId: call.id,
118
359
  toolType: call.kind === "action" ? "action" : "read"
@@ -130,8 +371,27 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
130
371
  if (isWorkflowControlFlowSignal(error)) {
131
372
  throw error;
132
373
  }
374
+ if (error instanceof RunCancelledError) {
375
+ await ctx.localStep("deactivate", () => this.store.setActiveStream(input.threadId, null));
376
+ await this.settleDetachedParent(ctx, input, {
377
+ status: "cancelled"
378
+ });
379
+ throw error;
380
+ }
381
+ if (isReplayIntegrityError(error)) {
382
+ const failure = {
383
+ code: "run_failed",
384
+ message: error.message
385
+ };
386
+ publishAgentRunFailed({
387
+ runId: ctx.runId,
388
+ ...failure
389
+ });
390
+ await (await hooks.openSink()).fail(failure);
391
+ throw error;
392
+ }
133
393
  const message = error instanceof Error ? error.message : String(error);
134
- const code = error instanceof QuotaExceededError ? "quota_exceeded" : "run_failed";
394
+ const code = agentFailureCode(error);
135
395
  publishAgentRunFailed({
136
396
  runId: ctx.runId,
137
397
  code,
@@ -144,6 +404,10 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
144
404
  errorMessage: message
145
405
  }) ?? Promise.resolve());
146
406
  await ctx.localStep("deactivate", () => this.store.setActiveStream(input.threadId, null));
407
+ await this.settleDetachedParent(ctx, input, {
408
+ status: "failed",
409
+ error: message
410
+ });
147
411
  const writer = await hooks.openSink();
148
412
  await writer.fail({
149
413
  code,
@@ -153,146 +417,32 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
153
417
  }
154
418
  }
155
419
  };
156
- AgentRunWorkflow = _ts_decorate([
157
- Injectable(),
420
+ AgentRunWorkflow = _ts_decorate2([
421
+ Injectable2(),
158
422
  Workflow({
159
423
  name: "agent.run",
160
424
  version: "1"
161
425
  }),
162
- _ts_param(0, Inject(AGENT_DEPS_FACTORY)),
163
- _ts_param(1, Inject(AGENT_STORE)),
164
- _ts_param(2, Optional()),
165
- _ts_param(3, Inject(AGENT_DISPATCHED_STEPS)),
166
- _ts_metadata("design:type", Function),
167
- _ts_metadata("design:paramtypes", [
168
- typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory,
169
- typeof AgentStore === "undefined" ? Object : AgentStore,
170
- Object,
171
- Boolean
172
- ])
173
- ], AgentRunWorkflow);
174
-
175
- // src/durable/agent-run.steps.ts
176
- import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY2, invokeWithTransientRetry, publishAgentToolRetry, traceLlmTurn, traceToolExecution, withToolTimeout } from "@dudousxd/nestjs-agent-core";
177
- import { Step } from "@dudousxd/nestjs-durable";
178
- import { Inject as Inject2, Injectable as Injectable2 } from "@nestjs/common";
179
- function _ts_decorate2(decorators, target, key, desc) {
180
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
181
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
182
- 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;
183
- return c > 3 && r && Object.defineProperty(target, key, r), r;
184
- }
185
- __name(_ts_decorate2, "_ts_decorate");
186
- function _ts_metadata2(k, v) {
187
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
188
- }
189
- __name(_ts_metadata2, "_ts_metadata");
190
- function _ts_param2(paramIndex, decorator) {
191
- return function(target, key) {
192
- decorator(target, key, paramIndex);
193
- };
194
- }
195
- __name(_ts_param2, "_ts_param");
196
- var AgentRunSteps = class {
197
- static {
198
- __name(this, "AgentRunSteps");
199
- }
200
- factory;
201
- constructor(factory) {
202
- this.factory = factory;
203
- }
204
- /**
205
- * Retryable: every side effect (persist/quota/stream markers) happens loop-side under
206
- * `hooks.step`, so a durable retry only re-runs the model call and re-streams its tokens — never
207
- * double-writes. NEVER call `writer.end()`/`fail()` here: the workflow owns the stream's lifecycle,
208
- * across however many turns/retries it takes.
209
- */
210
- async llm(input) {
211
- const deps = this.factory.forAgent(input.agentName);
212
- const tools = await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, deps.toolAllowList);
213
- const opened = await deps.sink.open(input.sinkRunId);
214
- const writer = input.childSink ? childSinkWriter(opened) : opened;
215
- return traceLlmTurn(input.runId, input.step, () => deps.model.runTurn({
216
- system: input.system,
217
- messages: input.messages,
218
- tools,
219
- sink: writer
220
- }));
221
- }
222
- /**
223
- * NO DURABLE retries — a tool may not be idempotent (a bare `@Step()` carries none, unlike `llm`
224
- * above). What it does retry, in place, is a CLASSIFIED-TRANSIENT error from the tool's own
225
- * invocation (a DB deadlock, a lock-wait timeout — see `isTransientToolError`): the server rolled
226
- * that work back, so re-invoking it is safe, and doing it here (inside this one step, never a new
227
- * checkpoint) is exactly how the non-dispatched loop path handles it too. The timeout is applied
228
- * HERE, handler-side, via `withToolTimeout` — never as a durable dispatch `timeoutMs`, which
229
- * bounds engine liveness (retryable) rather than the tool's own business outcome — and per
230
- * ATTEMPT, so a retried invocation gets its own fresh timeout window.
231
- */
232
- async tool(input) {
233
- const deps = this.factory.forAgent(input.ctx.agentName);
234
- const ctx = {
235
- ...input.ctx
236
- };
237
- const localClassify = deps.toolTransientRetry !== false ? deps.toolTransientRetry?.classify : void 0;
238
- const transientRetry = input.transientRetry === false ? false : {
239
- attempts: input.transientRetry.attempts,
240
- backoffMs: input.transientRetry.backoffMs,
241
- ...localClassify !== void 0 ? {
242
- classify: localClassify
243
- } : {}
244
- };
245
- return traceToolExecution(input.ctx.runId, {
246
- toolCallId: input.toolCallId,
247
- toolName: input.toolName,
248
- toolType: input.toolType
249
- }, () => invokeWithTransientRetry(() => {
250
- const invocation = deps.registry.invoke(input.toolName, input.input, ctx, deps.rolesPolicy);
251
- return input.timeoutMs !== void 0 ? withToolTimeout(invocation, input.timeoutMs, input.toolName) : invocation;
252
- }, transientRetry, {
253
- onRetry: /* @__PURE__ */ __name((attempt, error) => {
254
- publishAgentToolRetry({
255
- toolName: input.toolName,
256
- toolCallId: input.toolCallId,
257
- attempt,
258
- message: error instanceof Error ? error.message : String(error)
259
- });
260
- }, "onRetry")
261
- }));
262
- }
263
- };
264
- _ts_decorate2([
265
- Step({
266
- retries: 3
267
- }),
268
- _ts_metadata2("design:type", Function),
269
- _ts_metadata2("design:paramtypes", [
270
- typeof DispatchedLlmInput === "undefined" ? Object : DispatchedLlmInput
271
- ]),
272
- _ts_metadata2("design:returntype", Promise)
273
- ], AgentRunSteps.prototype, "llm", null);
274
- _ts_decorate2([
275
- Step(),
276
- _ts_metadata2("design:type", Function),
277
- _ts_metadata2("design:paramtypes", [
278
- typeof DispatchedToolInput === "undefined" ? Object : DispatchedToolInput
279
- ]),
280
- _ts_metadata2("design:returntype", Promise)
281
- ], AgentRunSteps.prototype, "tool", null);
282
- AgentRunSteps = _ts_decorate2([
283
- Injectable2(),
284
426
  _ts_param2(0, Inject2(AGENT_DEPS_FACTORY2)),
427
+ _ts_param2(1, Inject2(AGENT_STORE)),
428
+ _ts_param2(2, Inject2(AGENT_DISPATCHED_STEPS)),
429
+ _ts_param2(3, Optional()),
430
+ _ts_param2(3, Inject2(RUN_GATEWAY)),
285
431
  _ts_metadata2("design:type", Function),
286
432
  _ts_metadata2("design:paramtypes", [
287
- typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory
433
+ typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory,
434
+ typeof AgentStore === "undefined" ? Object : AgentStore,
435
+ Boolean,
436
+ typeof RunGateway === "undefined" ? Object : RunGateway
288
437
  ])
289
- ], AgentRunSteps);
438
+ ], AgentRunWorkflow);
290
439
 
291
440
  // src/durable/durable-agent-runner.ts
292
441
  import { randomUUID } from "node:crypto";
293
- import { WorkflowService } from "@dudousxd/nestjs-durable";
442
+ import { AGENT_SINK, AGENT_STORE as AGENT_STORE2, encodeStreamEvent } from "@dudousxd/nestjs-agent-core";
443
+ import { RUN_GATEWAY as RUN_GATEWAY2, WorkflowService } from "@dudousxd/nestjs-durable";
294
444
  import { isWorkflowControlFlowSignal as isWorkflowControlFlowSignal2 } from "@dudousxd/nestjs-durable-core";
295
- import { Injectable as Injectable3 } from "@nestjs/common";
445
+ import { Inject as Inject3, Injectable as Injectable3, Logger } from "@nestjs/common";
296
446
  function _ts_decorate3(decorators, target, key, desc) {
297
447
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
298
448
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -304,13 +454,26 @@ function _ts_metadata3(k, v) {
304
454
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
305
455
  }
306
456
  __name(_ts_metadata3, "_ts_metadata");
307
- var DurableAgentRunner = class {
457
+ function _ts_param3(paramIndex, decorator) {
458
+ return function(target, key) {
459
+ decorator(target, key, paramIndex);
460
+ };
461
+ }
462
+ __name(_ts_param3, "_ts_param");
463
+ var DurableAgentRunner = class _DurableAgentRunner {
308
464
  static {
309
465
  __name(this, "DurableAgentRunner");
310
466
  }
311
467
  workflows;
312
- constructor(workflows) {
468
+ runs;
469
+ store;
470
+ sink;
471
+ logger = new Logger(_DurableAgentRunner.name);
472
+ constructor(workflows, runs, store, sink) {
313
473
  this.workflows = workflows;
474
+ this.runs = runs;
475
+ this.store = store;
476
+ this.sink = sink;
314
477
  }
315
478
  async start(input) {
316
479
  const stamped = {
@@ -329,24 +492,59 @@ var DurableAgentRunner = class {
329
492
  runId
330
493
  };
331
494
  }
332
- async signal(runId, toolCallId, decision) {
333
- await this.workflows.signal(`tool:${runId}:${toolCallId}`, decision);
495
+ async signal(runId, toolCallId2, reply) {
496
+ await this.workflows.signal(`tool:${runId}:${toolCallId2}`, reply);
334
497
  }
335
- async cancel(_runId) {
336
- await Promise.resolve();
498
+ /**
499
+ * Stop a run, and settle what the durable runtime knows nothing about.
500
+ *
501
+ * `compensate: true` is the form that gives the BODY a chance: it moves the run to `cancelling`
502
+ * (which is what the workflow's own cancel observation reads) and then re-drives it, so a turn
503
+ * running in-process unwinds at its next safe point instead of finishing. It also guarantees the
504
+ * outcome for the turn that CANNOT observe anything — one parked on `waitForSignal`, suspended
505
+ * inside a position its journal already holds — because that re-drive re-suspends and the runtime
506
+ * settles the run `cancelled` regardless. The cascade to child runs comes with it.
507
+ *
508
+ * Neither of those settles the agent's own state, so this does: the run row gets its own terminal,
509
+ * and the subscriber gets a `cancelled` frame followed by a normal end. Both are keyed by runId
510
+ * alone, which is why they belong here rather than in the body — the body is the only place that
511
+ * knows which THREAD was streaming, so releasing that stays there (see `AgentRunWorkflow`).
512
+ *
513
+ * A tool already executing is not interrupted, here or anywhere: see `haltIfCancelled` in core.
514
+ */
515
+ async cancel(runId) {
516
+ this.logger.log(`cancelling agent run ${runId}`);
517
+ await this.runs.cancel(runId, {
518
+ compensate: true
519
+ });
520
+ const writer = await this.sink.open(runId);
521
+ await writer.write(encodeStreamEvent({
522
+ kind: "cancelled"
523
+ }));
524
+ await writer.end();
525
+ await this.store.recordRunEnd?.({
526
+ runId,
527
+ status: "cancelled"
528
+ });
337
529
  }
338
530
  };
339
531
  DurableAgentRunner = _ts_decorate3([
340
532
  Injectable3(),
533
+ _ts_param3(1, Inject3(RUN_GATEWAY2)),
534
+ _ts_param3(2, Inject3(AGENT_STORE2)),
535
+ _ts_param3(3, Inject3(AGENT_SINK)),
341
536
  _ts_metadata3("design:type", Function),
342
537
  _ts_metadata3("design:paramtypes", [
343
- typeof WorkflowService === "undefined" ? Object : WorkflowService
538
+ typeof WorkflowService === "undefined" ? Object : WorkflowService,
539
+ typeof RunGateway === "undefined" ? Object : RunGateway,
540
+ typeof AgentStore === "undefined" ? Object : AgentStore,
541
+ typeof TokenStreamSink === "undefined" ? Object : TokenStreamSink
344
542
  ])
345
543
  ], DurableAgentRunner);
346
544
 
347
545
  // src/durable/agent-durable.module.ts
348
- import { AGENT_DURABLE_RUNNER, AGENT_OPTIONS, AGENT_SINK } from "@dudousxd/nestjs-agent-core";
349
- import { Global, Logger, Module } from "@nestjs/common";
546
+ import { AGENT_DURABLE_RUNNER, AGENT_OPTIONS, AGENT_SINK as AGENT_SINK2 } from "@dudousxd/nestjs-agent-core";
547
+ import { Global, Logger as Logger2, Module } from "@nestjs/common";
350
548
 
351
549
  // src/in-process-sink.ts
352
550
  import { AgentStreamError } from "@dudousxd/nestjs-agent-core";
@@ -424,7 +622,7 @@ function _ts_decorate4(decorators, target, key, desc) {
424
622
  return c > 3 && r && Object.defineProperty(target, key, r), r;
425
623
  }
426
624
  __name(_ts_decorate4, "_ts_decorate");
427
- var logger = new Logger("AgentDurableModule");
625
+ var logger = new Logger2("AgentDurableModule");
428
626
  function runnerProviders() {
429
627
  return [
430
628
  DurableAgentRunner,
@@ -443,7 +641,7 @@ function runnerProviders() {
443
641
  }, "useFactory"),
444
642
  inject: [
445
643
  AGENT_OPTIONS,
446
- AGENT_SINK
644
+ AGENT_SINK2
447
645
  ]
448
646
  }
449
647
  ];
@@ -511,14 +709,14 @@ AgentDurableModule = _ts_decorate4([
511
709
  ], AgentDurableModule);
512
710
 
513
711
  // src/agent.module.ts
514
- import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER6, AGENT_APPROVAL_PORT, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY6, AGENT_DURABLE_RUNNER as AGENT_DURABLE_RUNNER2, AGENT_MODEL as AGENT_MODEL2, AGENT_OPTIONS as AGENT_OPTIONS5, 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_SINK3, AGENT_STORE as AGENT_STORE5, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY3, AgentRegistry as AgentRegistry5, DefaultRolesPolicy, ToolRegistry as ToolRegistry3 } from "@dudousxd/nestjs-agent-core";
712
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER8, AGENT_APPROVAL_PORT, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY6, AGENT_DURABLE_RUNNER as AGENT_DURABLE_RUNNER2, AGENT_MEMORY as AGENT_MEMORY3, AGENT_MODEL as AGENT_MODEL2, AGENT_OPTIONS as AGENT_OPTIONS5, 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_SINK4, AGENT_SKILLS as AGENT_SKILLS3, AGENT_SKILL_SOURCES as AGENT_SKILL_SOURCES2, AGENT_STORE as AGENT_STORE6, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY3, AgentRegistry as AgentRegistry5, DefaultRolesPolicy, ToolRegistry as ToolRegistry3 } from "@dudousxd/nestjs-agent-core";
515
713
  import { Global as Global2, Module as Module2 } from "@nestjs/common";
516
714
  import { DiscoveryModule, RouterModule } from "@nestjs/core";
517
715
 
518
716
  // src/agent-deps.factory.ts
519
- import { AGENT_MODEL, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_QUOTA_STORE, AGENT_REGISTRY, AGENT_ROLES_POLICY, AGENT_SINK as AGENT_SINK2, AGENT_STORE as AGENT_STORE2, AGENT_TOOL_REGISTRY, AgentRegistry as AgentRegistry2, ToolRegistry as ToolRegistry2 } from "@dudousxd/nestjs-agent-core";
717
+ import { AGENT_MEMORY, AGENT_MODEL, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_QUOTA_STORE, AGENT_REGISTRY, AGENT_ROLES_POLICY, AGENT_SINK as AGENT_SINK3, AGENT_SKILLS, AGENT_STORE as AGENT_STORE3, AGENT_TOOL_REGISTRY, AgentRegistry as AgentRegistry2, ToolRegistry as ToolRegistry2, normalizeDelegation, summarizeWithModel, windowHistory } from "@dudousxd/nestjs-agent-core";
520
718
  import { AGENT_OPTIONS as AGENT_OPTIONS2 } from "@dudousxd/nestjs-agent-core";
521
- import { Inject as Inject3, Injectable as Injectable4, Optional as Optional2 } from "@nestjs/common";
719
+ import { Inject as Inject4, Injectable as Injectable4, Optional as Optional2 } from "@nestjs/common";
522
720
  function _ts_decorate5(decorators, target, key, desc) {
523
721
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
524
722
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -530,14 +728,15 @@ function _ts_metadata4(k, v) {
530
728
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
531
729
  }
532
730
  __name(_ts_metadata4, "_ts_metadata");
533
- function _ts_param3(paramIndex, decorator) {
731
+ function _ts_param4(paramIndex, decorator) {
534
732
  return function(target, key) {
535
733
  decorator(target, key, paramIndex);
536
734
  };
537
735
  }
538
- __name(_ts_param3, "_ts_param");
539
- function delegateToolName(target) {
540
- return `ask_${target.replace(/[^a-zA-Z0-9]+/g, "_")}`;
736
+ __name(_ts_param4, "_ts_param");
737
+ function delegateToolName(args) {
738
+ const slug = args.target.replace(/[^a-zA-Z0-9]+/g, "_");
739
+ return args.detached === true ? `start_${slug}` : `ask_${slug}`;
541
740
  }
542
741
  __name(delegateToolName, "delegateToolName");
543
742
  var AgentDepsFactory2 = class {
@@ -554,7 +753,9 @@ var AgentDepsFactory2 = class {
554
753
  promptContributors;
555
754
  quota;
556
755
  pricingStore;
557
- constructor(options, model, store, sink, rolesPolicy, registry, agents, promptContributors, quota, pricingStore) {
756
+ skills;
757
+ memory;
758
+ constructor(options, model, store, sink, rolesPolicy, registry, agents, promptContributors, quota, pricingStore, skills, memory) {
558
759
  this.options = options;
559
760
  this.model = model;
560
761
  this.store = store;
@@ -565,6 +766,16 @@ var AgentDepsFactory2 = class {
565
766
  this.promptContributors = promptContributors;
566
767
  this.quota = quota;
567
768
  this.pricingStore = pricingStore;
769
+ this.skills = skills;
770
+ this.memory = memory;
771
+ }
772
+ /** The turn's memory seam, shared with the read-back endpoint. Undefined → memory is not configured. */
773
+ memoryConfig() {
774
+ return this.memory;
775
+ }
776
+ /** The turn's skills seam, shared with the listing endpoint. Undefined → skills are not configured. */
777
+ skillsConfig() {
778
+ return this.skills;
568
779
  }
569
780
  /**
570
781
  * The agent a turn uses when the caller names none: the explicit `defaultAgent` option, else the
@@ -581,7 +792,13 @@ var AgentDepsFactory2 = class {
581
792
  if (definition === void 0) {
582
793
  return void 0;
583
794
  }
584
- const delegated = (definition.delegatesTo ?? []).map(delegateToolName);
795
+ const delegated = (definition.delegatesTo ?? []).map((edge) => {
796
+ const { agent, detached } = normalizeDelegation(edge);
797
+ return delegateToolName({
798
+ target: agent,
799
+ detached
800
+ });
801
+ });
585
802
  if (definition.tools === void 0 && delegated.length === 0) {
586
803
  return void 0;
587
804
  }
@@ -596,6 +813,8 @@ var AgentDepsFactory2 = class {
596
813
  const toolAllowList = this.effectiveTools(definition);
597
814
  const followUpsCount = this.followUpsCount();
598
815
  const retrieval = this.options.retrieval;
816
+ const historyPolicy = this.historyPolicy(definition);
817
+ const ask = definition?.ask ?? this.options.ask;
599
818
  return {
600
819
  model: this.model,
601
820
  store: this.store,
@@ -605,6 +824,12 @@ var AgentDepsFactory2 = class {
605
824
  promptContributors: this.promptContributors,
606
825
  systemPrompt: definition?.systemPrompt ?? "You are a helpful assistant.",
607
826
  maxSteps: definition?.maxSteps ?? 8,
827
+ ...definition?.maxDelegationDepth !== void 0 ? {
828
+ maxDelegationDepth: definition.maxDelegationDepth
829
+ } : {},
830
+ ...definition?.maxAgentAppearances !== void 0 ? {
831
+ maxAgentAppearances: definition.maxAgentAppearances
832
+ } : {},
608
833
  ...definition?.modelId !== void 0 ? {
609
834
  modelId: definition.modelId
610
835
  } : {},
@@ -614,6 +839,37 @@ var AgentDepsFactory2 = class {
614
839
  ...this.pricingStore !== void 0 ? {
615
840
  pricingStore: this.pricingStore
616
841
  } : {},
842
+ ...historyPolicy !== void 0 ? {
843
+ historyPolicy
844
+ } : {},
845
+ inputProcessors: this.options.inputProcessors ?? [],
846
+ outputProcessors: this.options.outputProcessors ?? [],
847
+ ...definition?.outputSchema !== void 0 ? {
848
+ outputSchema: definition.outputSchema
849
+ } : {},
850
+ ...definition?.outputRepairAttempts !== void 0 ? {
851
+ outputRepairAttempts: definition.outputRepairAttempts
852
+ } : {},
853
+ ...definition?.intake !== void 0 ? {
854
+ intake: definition.intake
855
+ } : {},
856
+ // Most specific wins, exactly as `historyPolicy` resolves: the agent's own `@Agent({ ask })`,
857
+ // else the module-wide flag. Both are module config, so every process of a deployment agrees.
858
+ ...ask === true ? {
859
+ ask: true
860
+ } : {},
861
+ // Module-wide, never per-agent: a skill is a procedure, not a persona, and an agent that could
862
+ // opt out of one would make the catalog depend on which agent a turn happened to select — a
863
+ // per-turn fact the worker re-deriving the tool list cannot see.
864
+ ...this.skills !== void 0 ? {
865
+ skills: this.skills
866
+ } : {},
867
+ // Module-wide, never per-agent, for the same reason skills are: which memories a turn carries
868
+ // cannot depend on which persona answered it, or the worker re-deriving a dispatched turn's
869
+ // tool list would disagree with the loop about whether `remember` was offered.
870
+ ...this.memory !== void 0 ? {
871
+ memory: this.memory
872
+ } : {},
617
873
  ...toolAllowList !== void 0 ? {
618
874
  toolAllowList
619
875
  } : {},
@@ -634,6 +890,36 @@ var AgentDepsFactory2 = class {
634
890
  } : {}
635
891
  };
636
892
  }
893
+ /**
894
+ * The ceiling on how much of a thread this agent's turns carry, most specific wins: the agent's
895
+ * own `@Agent({ history })`, else a module-wide custom `historyPolicy`, else the module-wide
896
+ * `history` window. Undefined → unbounded, the behaviour of a module that configures none.
897
+ */
898
+ historyPolicy(definition) {
899
+ if (definition?.history !== void 0) {
900
+ return this.window(definition.history);
901
+ }
902
+ if (this.options.historyPolicy !== void 0) {
903
+ return this.options.historyPolicy;
904
+ }
905
+ return this.options.history === void 0 ? void 0 : this.window(this.options.history);
906
+ }
907
+ /** Build the built-in window policy from the declarative `{ maxMessages, maxTokens, summarize }`. */
908
+ window(config) {
909
+ return windowHistory({
910
+ ...config.maxMessages !== void 0 ? {
911
+ maxMessages: config.maxMessages
912
+ } : {},
913
+ ...config.maxTokens !== void 0 ? {
914
+ maxTokens: config.maxTokens
915
+ } : {},
916
+ // The shared module model, not a per-agent one: an agent's `modelId` is an accounting label,
917
+ // and only one provider is ever bound.
918
+ ...config.summarize === true ? {
919
+ summarize: summarizeWithModel(this.model)
920
+ } : {}
921
+ });
922
+ }
637
923
  /** Normalize the `followUps` option (`true` → 3, `{ count }` → count) to a number, or undefined. */
638
924
  followUpsCount() {
639
925
  const followUps = this.options.followUps;
@@ -648,17 +934,21 @@ var AgentDepsFactory2 = class {
648
934
  };
649
935
  AgentDepsFactory2 = _ts_decorate5([
650
936
  Injectable4(),
651
- _ts_param3(0, Inject3(AGENT_OPTIONS2)),
652
- _ts_param3(1, Inject3(AGENT_MODEL)),
653
- _ts_param3(2, Inject3(AGENT_STORE2)),
654
- _ts_param3(3, Inject3(AGENT_SINK2)),
655
- _ts_param3(4, Inject3(AGENT_ROLES_POLICY)),
656
- _ts_param3(5, Inject3(AGENT_TOOL_REGISTRY)),
657
- _ts_param3(6, Inject3(AGENT_REGISTRY)),
658
- _ts_param3(7, Inject3(AGENT_PROMPT_CONTRIBUTORS)),
659
- _ts_param3(8, Inject3(AGENT_QUOTA_STORE)),
660
- _ts_param3(9, Optional2()),
661
- _ts_param3(9, Inject3(AGENT_PRICING_STORE)),
937
+ _ts_param4(0, Inject4(AGENT_OPTIONS2)),
938
+ _ts_param4(1, Inject4(AGENT_MODEL)),
939
+ _ts_param4(2, Inject4(AGENT_STORE3)),
940
+ _ts_param4(3, Inject4(AGENT_SINK3)),
941
+ _ts_param4(4, Inject4(AGENT_ROLES_POLICY)),
942
+ _ts_param4(5, Inject4(AGENT_TOOL_REGISTRY)),
943
+ _ts_param4(6, Inject4(AGENT_REGISTRY)),
944
+ _ts_param4(7, Inject4(AGENT_PROMPT_CONTRIBUTORS)),
945
+ _ts_param4(8, Inject4(AGENT_QUOTA_STORE)),
946
+ _ts_param4(9, Optional2()),
947
+ _ts_param4(9, Inject4(AGENT_PRICING_STORE)),
948
+ _ts_param4(10, Optional2()),
949
+ _ts_param4(10, Inject4(AGENT_SKILLS)),
950
+ _ts_param4(11, Optional2()),
951
+ _ts_param4(11, Inject4(AGENT_MEMORY)),
662
952
  _ts_metadata4("design:type", Function),
663
953
  _ts_metadata4("design:paramtypes", [
664
954
  typeof AgentModuleOptions === "undefined" ? Object : AgentModuleOptions,
@@ -670,13 +960,15 @@ AgentDepsFactory2 = _ts_decorate5([
670
960
  typeof AgentRegistry2 === "undefined" ? Object : AgentRegistry2,
671
961
  Array,
672
962
  Object,
963
+ Object,
964
+ Object,
673
965
  Object
674
966
  ])
675
967
  ], AgentDepsFactory2);
676
968
 
677
969
  // src/agent.service.ts
678
- import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY3, AGENT_QUOTA_STORE as AGENT_QUOTA_STORE2, AGENT_RUNNER, AGENT_STORE as AGENT_STORE3 } from "@dudousxd/nestjs-agent-core";
679
- import { BadRequestException, ForbiddenException, Inject as Inject4, Injectable as Injectable5, NotFoundException, NotImplementedException } from "@nestjs/common";
970
+ import { AGENT_ATTACHMENT_STAGING, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY3, AGENT_QUOTA_STORE as AGENT_QUOTA_STORE2, AGENT_RUNNER, AGENT_STORE as AGENT_STORE4 } from "@dudousxd/nestjs-agent-core";
971
+ import { BadRequestException, ForbiddenException, Inject as Inject5, Injectable as Injectable5, NotFoundException, NotImplementedException, Optional as Optional3 } from "@nestjs/common";
680
972
  function _ts_decorate6(decorators, target, key, desc) {
681
973
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
682
974
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -688,12 +980,12 @@ function _ts_metadata5(k, v) {
688
980
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
689
981
  }
690
982
  __name(_ts_metadata5, "_ts_metadata");
691
- function _ts_param4(paramIndex, decorator) {
983
+ function _ts_param5(paramIndex, decorator) {
692
984
  return function(target, key) {
693
985
  decorator(target, key, paramIndex);
694
986
  };
695
987
  }
696
- __name(_ts_param4, "_ts_param");
988
+ __name(_ts_param5, "_ts_param");
697
989
  var AgentService = class {
698
990
  static {
699
991
  __name(this, "AgentService");
@@ -702,14 +994,17 @@ var AgentService = class {
702
994
  store;
703
995
  deps;
704
996
  quota;
705
- constructor(runner, store, deps, quota) {
997
+ staging;
998
+ constructor(runner, store, deps, quota, staging) {
706
999
  this.runner = runner;
707
1000
  this.store = store;
708
1001
  this.deps = deps;
709
1002
  this.quota = quota;
1003
+ this.staging = staging;
710
1004
  }
711
1005
  async chat(params) {
712
1006
  const agentName = await this.resolveAgentName(params.agentName, params.threadId);
1007
+ const attachments = await this.resolveAttachments(params.actor, params.attachments ?? []);
713
1008
  let threadId = params.threadId;
714
1009
  if (threadId === void 0) {
715
1010
  if (params.regenerate === true) {
@@ -734,8 +1029,8 @@ var AgentService = class {
734
1029
  ...params.regenerate === true ? {
735
1030
  regenerate: true
736
1031
  } : {},
737
- ...params.attachments !== void 0 ? {
738
- attachments: params.attachments
1032
+ ...attachments.length > 0 ? {
1033
+ attachments
739
1034
  } : {},
740
1035
  ...params.pageContext !== void 0 ? {
741
1036
  pageContext: params.pageContext
@@ -748,18 +1043,66 @@ var AgentService = class {
748
1043
  threadId
749
1044
  };
750
1045
  }
1046
+ /**
1047
+ * Rebuild this turn's attachments from the staging store, keyed by the ids the caller claimed.
1048
+ * The model provider fetches {@link MessageAttachment.url} verbatim, so that url is only ever
1049
+ * allowed to come from the host's own store — a caller that could name it could point the server
1050
+ * at anything it can reach (cloud metadata, internal services) and read the response back out of
1051
+ * the model's answer.
1052
+ *
1053
+ * No store bound means no way to tell an id the actor owns from one they don't, so a turn simply
1054
+ * cannot carry attachments: 501 rather than trusting the request.
1055
+ */
1056
+ async resolveAttachments(actor, refs) {
1057
+ if (refs.length === 0) {
1058
+ return [];
1059
+ }
1060
+ const staging = this.staging;
1061
+ if (staging === void 0 || typeof staging.resolve !== "function") {
1062
+ throw new NotImplementedException("Message attachments require an AGENT_ATTACHMENT_STAGING provider implementing resolve(); attachment urls are never taken from the request.");
1063
+ }
1064
+ const resolved = [];
1065
+ for (const ref of refs) {
1066
+ const attachment = await staging.resolve({
1067
+ mediaId: ref.mediaId,
1068
+ actor
1069
+ });
1070
+ if (attachment === null) {
1071
+ throw new ForbiddenException(`attachment ${ref.mediaId} is not available to this actor`);
1072
+ }
1073
+ resolved.push(attachment);
1074
+ }
1075
+ return resolved;
1076
+ }
1077
+ /**
1078
+ * The run's live token stream, WITHOUT an ownership check — for in-process callers that are
1079
+ * already authorized (the MCP bridge, a host's own job code), the same split as
1080
+ * {@link signalToolCall} vs {@link approve}. Anything reachable from a request must go through
1081
+ * {@link subscribeAs} instead: a runId is the only thing standing between a caller and another
1082
+ * actor's answer, tool arguments and tool results.
1083
+ */
751
1084
  subscribe(runId) {
752
1085
  return this.deps.forAgent().sink.subscribe(runId);
753
1086
  }
754
- async approve(actor, toolCallId) {
755
- await this.assertOwnsToolCall(actor, toolCallId);
756
- return this.signalToolCall(toolCallId, {
1087
+ /**
1088
+ * {@link subscribe}, gated the way {@link cancel} is: only the actor whose thread is currently
1089
+ * streaming this run may read it. A run that has already finished is reported missing rather than
1090
+ * replayed — the runner clears the thread's active stream as the run ends, and that field is what
1091
+ * ownership is derived from.
1092
+ */
1093
+ async subscribeAs(actor, runId) {
1094
+ await this.assertOwnsActiveStream(actor, runId);
1095
+ return this.subscribe(runId);
1096
+ }
1097
+ async approve(actor, toolCallId2) {
1098
+ await this.assertOwnsToolCall(actor, toolCallId2);
1099
+ return this.signalToolCall(toolCallId2, {
757
1100
  approved: true
758
1101
  });
759
1102
  }
760
- async reject(actor, toolCallId, reason) {
761
- await this.assertOwnsToolCall(actor, toolCallId);
762
- return this.signalToolCall(toolCallId, {
1103
+ async reject(actor, toolCallId2, reason) {
1104
+ await this.assertOwnsToolCall(actor, toolCallId2);
1105
+ return this.signalToolCall(toolCallId2, {
763
1106
  approved: false,
764
1107
  ...reason !== void 0 ? {
765
1108
  reason
@@ -767,6 +1110,34 @@ var AgentService = class {
767
1110
  });
768
1111
  }
769
1112
  /**
1113
+ * Answer a parked question set. An omitted question takes the request's own pre-picked default —
1114
+ * resolved by the LOOP against the request it already holds, so "the user just submitted" and
1115
+ * "the user picked exactly the defaults" persist as the same answers, and no client has to
1116
+ * re-send what it was shown.
1117
+ */
1118
+ async answer(actor, toolCallId2, answers2 = {}) {
1119
+ await this.assertOwnsToolCall(actor, toolCallId2);
1120
+ const reply = {
1121
+ answers: answers2,
1122
+ answeredByRef: actor.id
1123
+ };
1124
+ return this.signalToolCall(toolCallId2, reply);
1125
+ }
1126
+ /**
1127
+ * Decline to answer and let the agent proceed on its own assumptions. NOT the same as confirming
1128
+ * them: the run records this as a rejection, so a reader auditing what the agent was told can
1129
+ * tell a choice the user made from one they refused to make.
1130
+ */
1131
+ async skip(actor, toolCallId2) {
1132
+ await this.assertOwnsToolCall(actor, toolCallId2);
1133
+ const reply = {
1134
+ answers: {},
1135
+ skipped: true,
1136
+ answeredByRef: actor.id
1137
+ };
1138
+ return this.signalToolCall(toolCallId2, reply);
1139
+ }
1140
+ /**
770
1141
  * The decision core behind approve/reject, WITHOUT an ownership check: resolves the run
771
1142
  * currently awaiting `toolCallId` and signals it. `approve`/`reject` above call this after their
772
1143
  * own `assertOwnsToolCall` gate; the console `AgentApprovalPort` adapter (bound by `AgentModule`)
@@ -774,9 +1145,9 @@ var AgentService = class {
774
1145
  * re-deriving thread ownership here would reject a legitimate operator decision (an operator is
775
1146
  * not the thread's own actor).
776
1147
  */
777
- async signalToolCall(toolCallId, decision) {
778
- const runId = await this.resolveRunForToolCall(toolCallId);
779
- return this.runner.signal(runId, toolCallId, decision);
1148
+ async signalToolCall(toolCallId2, reply) {
1149
+ const runId = await this.resolveRunForToolCall(toolCallId2);
1150
+ return this.runner.signal(runId, toolCallId2, reply);
780
1151
  }
781
1152
  async cancel(actor, runId) {
782
1153
  await this.assertOwnsActiveStream(actor, runId);
@@ -843,20 +1214,33 @@ var AgentService = class {
843
1214
  /**
844
1215
  * Agent-selection precedence for a `chat()` call with no explicit `agentName`: the thread's own
845
1216
  * `defaultAgent` (if the thread exists and one is set) wins over the module's configured default.
846
- * Skips the thread lookup entirely when the caller already named an agent or there's no thread yet.
1217
+ * Skips the store entirely when the caller already named an agent or there's no thread yet.
1218
+ *
1219
+ * What this needs is one nullable scalar, so it asks for one. `getThread` returns the entire
1220
+ * transcript — every message, every persisted tool output — and this discards all of it; on a
1221
+ * long thread with large tool results that is hundreds of kilobytes read per turn, outside the
1222
+ * workflow, for a string. A store predating {@link ThreadDefaultAgentReader} still answers, via
1223
+ * the full read.
847
1224
  */
848
1225
  async resolveAgentName(agentName, threadId) {
849
1226
  if (agentName !== void 0) {
850
1227
  return agentName;
851
1228
  }
852
1229
  if (threadId !== void 0) {
853
- const thread = await this.store.getThread(threadId);
854
- if (thread?.defaultAgent !== void 0 && thread.defaultAgent !== null) {
855
- return thread.defaultAgent;
1230
+ const threadDefault = await this.threadDefaultAgent(threadId);
1231
+ if (threadDefault !== null) {
1232
+ return threadDefault;
856
1233
  }
857
1234
  }
858
1235
  return this.deps.defaultAgentName();
859
1236
  }
1237
+ async threadDefaultAgent(threadId) {
1238
+ const projecting = this.store;
1239
+ if (typeof projecting.defaultAgentForThread === "function") {
1240
+ return projecting.defaultAgentForThread(threadId);
1241
+ }
1242
+ return (await this.store.getThread(threadId))?.defaultAgent ?? null;
1243
+ }
860
1244
  async toSummaryView(thread) {
861
1245
  return {
862
1246
  ...thread,
@@ -888,6 +1272,67 @@ var AgentService = class {
888
1272
  return this.store.truncateFrom(threadId, messageId);
889
1273
  }
890
1274
  /**
1275
+ * Every file this actor has staged, newest first — across threads, including uploads that were
1276
+ * never sent, which nothing else on this surface can see.
1277
+ *
1278
+ * Metadata only. A url is minted per turn by `resolve` so it can be short-lived, and handing one
1279
+ * back per entry would undo that for the price of rendering a list.
1280
+ */
1281
+ async listAttachments(actor, options = {}) {
1282
+ const list = this.attachmentInventory();
1283
+ return list({
1284
+ actor,
1285
+ ...options.limit !== void 0 ? {
1286
+ limit: options.limit
1287
+ } : {}
1288
+ });
1289
+ }
1290
+ /**
1291
+ * This actor's staged media that no live message references and that is old enough not to be an
1292
+ * upload in flight — the candidate set for a sweep. Returns them; never deletes them. The bytes
1293
+ * are the host's, and so is the decision.
1294
+ *
1295
+ * `olderThan` is required and has no default. How long a composer may sit open with a file
1296
+ * attached is the host's knowledge, and a library-chosen grace period would eventually delete a
1297
+ * file someone was about to send. The staging store is asked to apply it, and the result is
1298
+ * filtered again here: a store that ignores the hint would otherwise turn this into exactly that
1299
+ * bug, silently.
1300
+ *
1301
+ * Both halves must be answerable or this refuses. An unanswerable reference query means "cannot
1302
+ * tell", and treating it as "nothing is referenced" would hand back every attachment the actor
1303
+ * ever sent, marked safe to delete.
1304
+ */
1305
+ async collectableAttachments(actor, options) {
1306
+ const list = this.attachmentInventory();
1307
+ const referencedMediaIds = this.store.referencedMediaIds?.bind(this.store);
1308
+ if (referencedMediaIds === void 0) {
1309
+ 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.");
1310
+ }
1311
+ const stagedBefore = options.olderThan.toISOString();
1312
+ const inventory = await list({
1313
+ actor,
1314
+ stagedBefore,
1315
+ ...options.limit !== void 0 ? {
1316
+ limit: options.limit
1317
+ } : {}
1318
+ });
1319
+ const aged = inventory.filter((entry) => entry.createdAt < stagedBefore);
1320
+ const referenced = new Set(await referencedMediaIds(actor.id, aged.map((entry) => entry.mediaId)));
1321
+ return aged.filter((entry) => !referenced.has(entry.mediaId));
1322
+ }
1323
+ /**
1324
+ * The bound staging store's `list`, or a refusal. Absence is never answered with an empty list:
1325
+ * "this host keeps no inventory" and "this actor has no files" are opposite facts that would
1326
+ * otherwise render identically — as a clean file list, and as a sweep that collects nothing.
1327
+ */
1328
+ attachmentInventory() {
1329
+ const list = this.staging?.list?.bind(this.staging);
1330
+ if (list === void 0) {
1331
+ 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.");
1332
+ }
1333
+ return list;
1334
+ }
1335
+ /**
891
1336
  * The day's usage for the badge: tokens + summed USD cost from the store, and the configured
892
1337
  * limit from the quota store (null → unlimited). `withinLimit` comes from the quota store so it
893
1338
  * can never drift from what enforcement uses.
@@ -929,24 +1374,26 @@ var AgentService = class {
929
1374
  * out from run resolution (see {@link resolveRunForToolCall}) so the console `AgentApprovalPort`
930
1375
  * adapter can resolve+signal a decision WITHOUT this check — it is authorized upstream instead.
931
1376
  */
932
- async assertOwnsToolCall(actor, toolCallId) {
933
- const owner = await this.store.ownerOfToolCall(toolCallId);
1377
+ async assertOwnsToolCall(actor, toolCallId2) {
1378
+ const owner = await this.store.ownerOfToolCall(toolCallId2);
934
1379
  if (owner === null) {
935
- throw new NotFoundException(`tool call ${toolCallId} not found`);
1380
+ throw new NotFoundException(`tool call ${toolCallId2} not found`);
936
1381
  }
937
1382
  if (owner !== actor.id) {
938
1383
  throw new ForbiddenException("tool call belongs to another actor");
939
1384
  }
940
1385
  }
941
1386
  /**
942
- * Resolve the run currently awaiting `toolCallId` (its thread's active stream). That run is the
943
- * sub-agent's own child run when the pending call belongs to a delegated agent — the client never
944
- * knows or supplies a runId; it is derived here from the tool call alone.
1387
+ * Resolve the run awaiting `toolCallId`. Derived server-side from the tool call alone — the client
1388
+ * never knows or supplies a runId, and that run is the sub-agent's own child run when the pending
1389
+ * call belongs to a delegated agent. The store answers from the CALL'S OWN `runId` where it has
1390
+ * one; see `AgentStore.runForToolCall` for why that matters once a thread can hold more than one
1391
+ * live run.
945
1392
  */
946
- async resolveRunForToolCall(toolCallId) {
947
- const runId = await this.store.runForToolCall(toolCallId);
1393
+ async resolveRunForToolCall(toolCallId2) {
1394
+ const runId = await this.store.runForToolCall(toolCallId2);
948
1395
  if (runId === null) {
949
- throw new NotFoundException(`tool call ${toolCallId} has no active run to signal`);
1396
+ throw new NotFoundException(`tool call ${toolCallId2} has no active run to signal`);
950
1397
  }
951
1398
  return runId;
952
1399
  }
@@ -963,16 +1410,19 @@ var AgentService = class {
963
1410
  };
964
1411
  AgentService = _ts_decorate6([
965
1412
  Injectable5(),
966
- _ts_param4(0, Inject4(AGENT_RUNNER)),
967
- _ts_param4(1, Inject4(AGENT_STORE3)),
968
- _ts_param4(2, Inject4(AGENT_DEPS_FACTORY3)),
969
- _ts_param4(3, Inject4(AGENT_QUOTA_STORE2)),
1413
+ _ts_param5(0, Inject5(AGENT_RUNNER)),
1414
+ _ts_param5(1, Inject5(AGENT_STORE4)),
1415
+ _ts_param5(2, Inject5(AGENT_DEPS_FACTORY3)),
1416
+ _ts_param5(3, Inject5(AGENT_QUOTA_STORE2)),
1417
+ _ts_param5(4, Optional3()),
1418
+ _ts_param5(4, Inject5(AGENT_ATTACHMENT_STAGING)),
970
1419
  _ts_metadata5("design:type", Function),
971
1420
  _ts_metadata5("design:paramtypes", [
972
1421
  typeof AgentRunner === "undefined" ? Object : AgentRunner,
973
1422
  typeof AgentStore === "undefined" ? Object : AgentStore,
974
1423
  typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory,
975
- Object
1424
+ Object,
1425
+ typeof AttachmentStagingStore === "undefined" ? Object : AttachmentStagingStore
976
1426
  ])
977
1427
  ], AgentService);
978
1428
 
@@ -997,16 +1447,16 @@ var AgentApprovalPortAdapter = class {
997
1447
  constructor(agent) {
998
1448
  this.agent = agent;
999
1449
  }
1000
- async approve(toolCallId, opts) {
1001
- await this.agent.signalToolCall(toolCallId, {
1450
+ async approve(toolCallId2, opts) {
1451
+ await this.agent.signalToolCall(toolCallId2, {
1002
1452
  approved: true,
1003
1453
  ...opts?.executedByRef !== void 0 ? {
1004
1454
  executedByRef: opts.executedByRef
1005
1455
  } : {}
1006
1456
  });
1007
1457
  }
1008
- async reject(toolCallId, opts) {
1009
- await this.agent.signalToolCall(toolCallId, {
1458
+ async reject(toolCallId2, opts) {
1459
+ await this.agent.signalToolCall(toolCallId2, {
1010
1460
  approved: false,
1011
1461
  ...opts?.executedByRef !== void 0 ? {
1012
1462
  executedByRef: opts.executedByRef
@@ -1027,7 +1477,7 @@ AgentApprovalPortAdapter = _ts_decorate7([
1027
1477
 
1028
1478
  // src/controller/agents.controller.ts
1029
1479
  import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY4, AGENT_REGISTRY as AGENT_REGISTRY2, AgentRegistry as AgentRegistry3 } from "@dudousxd/nestjs-agent-core";
1030
- import { Controller, Get, Inject as Inject5 } from "@nestjs/common";
1480
+ import { Controller, Get, Inject as Inject6 } from "@nestjs/common";
1031
1481
  function _ts_decorate8(decorators, target, key, desc) {
1032
1482
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1033
1483
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1039,12 +1489,12 @@ function _ts_metadata7(k, v) {
1039
1489
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1040
1490
  }
1041
1491
  __name(_ts_metadata7, "_ts_metadata");
1042
- function _ts_param5(paramIndex, decorator) {
1492
+ function _ts_param6(paramIndex, decorator) {
1043
1493
  return function(target, key) {
1044
1494
  decorator(target, key, paramIndex);
1045
1495
  };
1046
1496
  }
1047
- __name(_ts_param5, "_ts_param");
1497
+ __name(_ts_param6, "_ts_param");
1048
1498
  var AgentsController = class {
1049
1499
  static {
1050
1500
  __name(this, "AgentsController");
@@ -1074,8 +1524,8 @@ _ts_decorate8([
1074
1524
  ], AgentsController.prototype, "list", null);
1075
1525
  AgentsController = _ts_decorate8([
1076
1526
  Controller("agents"),
1077
- _ts_param5(0, Inject5(AGENT_REGISTRY2)),
1078
- _ts_param5(1, Inject5(AGENT_DEPS_FACTORY4)),
1527
+ _ts_param6(0, Inject6(AGENT_REGISTRY2)),
1528
+ _ts_param6(1, Inject6(AGENT_DEPS_FACTORY4)),
1079
1529
  _ts_metadata7("design:type", Function),
1080
1530
  _ts_metadata7("design:paramtypes", [
1081
1531
  typeof AgentRegistry3 === "undefined" ? Object : AgentRegistry3,
@@ -1084,8 +1534,8 @@ AgentsController = _ts_decorate8([
1084
1534
  ], AgentsController);
1085
1535
 
1086
1536
  // src/controller/attachments.controller.ts
1087
- import { AGENT_ACTOR_RESOLVER, AGENT_ATTACHMENT_STAGING, AGENT_OPTIONS as AGENT_OPTIONS3 } from "@dudousxd/nestjs-agent-core";
1088
- import { BadRequestException as BadRequestException2, Controller as Controller2, Inject as Inject6, NotImplementedException as NotImplementedException2, Optional as Optional3, PayloadTooLargeException, Post, Req, UnsupportedMediaTypeException, UploadedFile, UseInterceptors } from "@nestjs/common";
1537
+ import { AGENT_ACTOR_RESOLVER, AGENT_ATTACHMENT_STAGING as AGENT_ATTACHMENT_STAGING2, AGENT_OPTIONS as AGENT_OPTIONS3 } from "@dudousxd/nestjs-agent-core";
1538
+ import { BadRequestException as BadRequestException2, Controller as Controller2, Get as Get2, Inject as Inject7, NotImplementedException as NotImplementedException2, Optional as Optional4, PayloadTooLargeException, Post, Req, UnsupportedMediaTypeException, UploadedFile, UseInterceptors } from "@nestjs/common";
1089
1539
  import { FileInterceptor } from "@nestjs/platform-express";
1090
1540
  function _ts_decorate9(decorators, target, key, desc) {
1091
1541
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -1098,13 +1548,15 @@ function _ts_metadata8(k, v) {
1098
1548
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1099
1549
  }
1100
1550
  __name(_ts_metadata8, "_ts_metadata");
1101
- function _ts_param6(paramIndex, decorator) {
1551
+ function _ts_param7(paramIndex, decorator) {
1102
1552
  return function(target, key) {
1103
1553
  decorator(target, key, paramIndex);
1104
1554
  };
1105
1555
  }
1106
- __name(_ts_param6, "_ts_param");
1556
+ __name(_ts_param7, "_ts_param");
1107
1557
  var DEFAULT_MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
1558
+ var HARD_MAX_ATTACHMENT_BYTES = 32 * 1024 * 1024;
1559
+ var ATTACHMENT_PAGE_SIZE = 50;
1108
1560
  var DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES = [
1109
1561
  "image/png",
1110
1562
  "image/jpeg",
@@ -1120,10 +1572,12 @@ var AttachmentsController = class {
1120
1572
  }
1121
1573
  options;
1122
1574
  actorResolver;
1575
+ agents;
1123
1576
  staging;
1124
- constructor(options, actorResolver, staging) {
1577
+ constructor(options, actorResolver, agents, staging) {
1125
1578
  this.options = options;
1126
1579
  this.actorResolver = actorResolver;
1580
+ this.agents = agents;
1127
1581
  this.staging = staging;
1128
1582
  }
1129
1583
  /**
@@ -1137,6 +1591,21 @@ var AttachmentsController = class {
1137
1591
  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.");
1138
1592
  }
1139
1593
  }
1594
+ /**
1595
+ * The caller's own staged files, newest first — the one question `GET /agent/threads/:id` cannot
1596
+ * answer, since an upload that was never sent belongs to no thread.
1597
+ *
1598
+ * Scoped by the RESOLVED actor and nothing else. There is deliberately no `threadId` filter: a
1599
+ * thread's attachments already ride on its messages in the thread payload, and re-serving them
1600
+ * from here would add a second ownership path to get wrong for information the client already
1601
+ * holds. Metadata only — see {@link StagedAttachment}.
1602
+ */
1603
+ async list(req) {
1604
+ const actor = await this.actorResolver.resolve(req);
1605
+ return this.agents.listAttachments(actor, {
1606
+ limit: ATTACHMENT_PAGE_SIZE
1607
+ });
1608
+ }
1140
1609
  async upload(req, file) {
1141
1610
  if (this.staging === void 0) {
1142
1611
  throw new NotImplementedException2("Attachment upload is not configured on this server.");
@@ -1162,11 +1631,24 @@ var AttachmentsController = class {
1162
1631
  });
1163
1632
  }
1164
1633
  };
1634
+ _ts_decorate9([
1635
+ Get2(),
1636
+ _ts_param7(0, Req()),
1637
+ _ts_metadata8("design:type", Function),
1638
+ _ts_metadata8("design:paramtypes", [
1639
+ typeof Request === "undefined" ? Object : Request
1640
+ ]),
1641
+ _ts_metadata8("design:returntype", Promise)
1642
+ ], AttachmentsController.prototype, "list", null);
1165
1643
  _ts_decorate9([
1166
1644
  Post(),
1167
- UseInterceptors(FileInterceptor("file")),
1168
- _ts_param6(0, Req()),
1169
- _ts_param6(1, UploadedFile()),
1645
+ UseInterceptors(FileInterceptor("file", {
1646
+ limits: {
1647
+ fileSize: HARD_MAX_ATTACHMENT_BYTES
1648
+ }
1649
+ })),
1650
+ _ts_param7(0, Req()),
1651
+ _ts_param7(1, UploadedFile()),
1170
1652
  _ts_metadata8("design:type", Function),
1171
1653
  _ts_metadata8("design:paramtypes", [
1172
1654
  typeof Request === "undefined" ? Object : Request,
@@ -1176,21 +1658,22 @@ _ts_decorate9([
1176
1658
  ], AttachmentsController.prototype, "upload", null);
1177
1659
  AttachmentsController = _ts_decorate9([
1178
1660
  Controller2("attachments"),
1179
- _ts_param6(0, Inject6(AGENT_OPTIONS3)),
1180
- _ts_param6(1, Inject6(AGENT_ACTOR_RESOLVER)),
1181
- _ts_param6(2, Optional3()),
1182
- _ts_param6(2, Inject6(AGENT_ATTACHMENT_STAGING)),
1661
+ _ts_param7(0, Inject7(AGENT_OPTIONS3)),
1662
+ _ts_param7(1, Inject7(AGENT_ACTOR_RESOLVER)),
1663
+ _ts_param7(3, Optional4()),
1664
+ _ts_param7(3, Inject7(AGENT_ATTACHMENT_STAGING2)),
1183
1665
  _ts_metadata8("design:type", Function),
1184
1666
  _ts_metadata8("design:paramtypes", [
1185
1667
  typeof AgentModuleOptions === "undefined" ? Object : AgentModuleOptions,
1186
1668
  typeof ActorResolver === "undefined" ? Object : ActorResolver,
1669
+ typeof AgentService === "undefined" ? Object : AgentService,
1187
1670
  Object
1188
1671
  ])
1189
1672
  ], AttachmentsController);
1190
1673
 
1191
1674
  // src/controller/chat.controller.ts
1192
1675
  import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER2, AgentStreamError as AgentStreamError2 } from "@dudousxd/nestjs-agent-core";
1193
- import { Body, Controller as Controller3, Get as Get2, Inject as Inject7, Param, Post as Post2, Req as Req2, Res } from "@nestjs/common";
1676
+ import { BadRequestException as BadRequestException3, Body, Controller as Controller3, Get as Get3, Inject as Inject8, Param, Post as Post2, Req as Req2, Res } from "@nestjs/common";
1194
1677
  function _ts_decorate10(decorators, target, key, desc) {
1195
1678
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1196
1679
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1202,12 +1685,37 @@ function _ts_metadata9(k, v) {
1202
1685
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1203
1686
  }
1204
1687
  __name(_ts_metadata9, "_ts_metadata");
1205
- function _ts_param7(paramIndex, decorator) {
1688
+ function _ts_param8(paramIndex, decorator) {
1206
1689
  return function(target, key) {
1207
1690
  decorator(target, key, paramIndex);
1208
1691
  };
1209
1692
  }
1210
- __name(_ts_param7, "_ts_param");
1693
+ __name(_ts_param8, "_ts_param");
1694
+ var MAX_ATTACHMENTS_PER_MESSAGE = 10;
1695
+ function attachmentRefs(claimed) {
1696
+ if (claimed === void 0) {
1697
+ return [];
1698
+ }
1699
+ if (!Array.isArray(claimed)) {
1700
+ throw new BadRequestException3("attachments must be an array of { mediaId }");
1701
+ }
1702
+ if (claimed.length > MAX_ATTACHMENTS_PER_MESSAGE) {
1703
+ throw new BadRequestException3(`a message may carry at most ${MAX_ATTACHMENTS_PER_MESSAGE} attachments`);
1704
+ }
1705
+ return claimed.map((entry) => {
1706
+ if (typeof entry !== "object" || entry === null || !("mediaId" in entry)) {
1707
+ throw new BadRequestException3("each attachment must be an object with a mediaId");
1708
+ }
1709
+ const { mediaId } = entry;
1710
+ if (typeof mediaId !== "string" || mediaId.length === 0) {
1711
+ throw new BadRequestException3("each attachment must carry a non-empty string mediaId");
1712
+ }
1713
+ return {
1714
+ mediaId
1715
+ };
1716
+ });
1717
+ }
1718
+ __name(attachmentRefs, "attachmentRefs");
1211
1719
  var ChatController = class {
1212
1720
  static {
1213
1721
  __name(this, "ChatController");
@@ -1220,6 +1728,7 @@ var ChatController = class {
1220
1728
  }
1221
1729
  async chat(req, res, body) {
1222
1730
  const actor = await this.actorResolver.resolve(req);
1731
+ const attachments = attachmentRefs(body.attachments);
1223
1732
  const { runId, threadId } = await this.agent.chat({
1224
1733
  actor,
1225
1734
  message: body.message,
@@ -1229,8 +1738,8 @@ var ChatController = class {
1229
1738
  ...body.agent !== void 0 ? {
1230
1739
  agentName: body.agent
1231
1740
  } : {},
1232
- ...body.attachments !== void 0 ? {
1233
- attachments: body.attachments
1741
+ ...attachments.length > 0 ? {
1742
+ attachments
1234
1743
  } : {},
1235
1744
  ...body.pageContext !== void 0 ? {
1236
1745
  pageContext: body.pageContext
@@ -1242,10 +1751,11 @@ var ChatController = class {
1242
1751
  transient: true
1243
1752
  } : {}
1244
1753
  });
1245
- await this.pipe(res, runId, threadId);
1754
+ await this.pipe(res, runId, this.agent.subscribe(runId), threadId);
1246
1755
  }
1247
- async stream(runId, res) {
1248
- await this.pipe(res, runId);
1756
+ async stream(req, runId, res) {
1757
+ const actor = await this.actorResolver.resolve(req);
1758
+ await this.pipe(res, runId, await this.agent.subscribeAs(actor, runId));
1249
1759
  }
1250
1760
  async cancel(req, runId) {
1251
1761
  const actor = await this.actorResolver.resolve(req);
@@ -1254,7 +1764,7 @@ var ChatController = class {
1254
1764
  aborted: true
1255
1765
  };
1256
1766
  }
1257
- async pipe(res, runId, threadId) {
1767
+ async pipe(res, runId, events, threadId) {
1258
1768
  res.setHeader("Content-Type", "text/event-stream");
1259
1769
  res.setHeader("Cache-Control", "no-cache");
1260
1770
  res.setHeader("Connection", "keep-alive");
@@ -1272,7 +1782,7 @@ data: ${JSON.stringify({
1272
1782
  const decoder = new TextDecoder();
1273
1783
  let buffer = "";
1274
1784
  try {
1275
- for await (const chunk of this.agent.subscribe(runId)) {
1785
+ for await (const chunk of events) {
1276
1786
  buffer += decoder.decode(chunk, {
1277
1787
  stream: true
1278
1788
  });
@@ -1312,9 +1822,9 @@ data: ${JSON.stringify(payload)}
1312
1822
  };
1313
1823
  _ts_decorate10([
1314
1824
  Post2("chat"),
1315
- _ts_param7(0, Req2()),
1316
- _ts_param7(1, Res()),
1317
- _ts_param7(2, Body()),
1825
+ _ts_param8(0, Req2()),
1826
+ _ts_param8(1, Res()),
1827
+ _ts_param8(2, Body()),
1318
1828
  _ts_metadata9("design:type", Function),
1319
1829
  _ts_metadata9("design:paramtypes", [
1320
1830
  typeof Request === "undefined" ? Object : Request,
@@ -1324,11 +1834,13 @@ _ts_decorate10([
1324
1834
  _ts_metadata9("design:returntype", Promise)
1325
1835
  ], ChatController.prototype, "chat", null);
1326
1836
  _ts_decorate10([
1327
- Get2("chat/:runId/stream"),
1328
- _ts_param7(0, Param("runId")),
1329
- _ts_param7(1, Res()),
1837
+ Get3("chat/:runId/stream"),
1838
+ _ts_param8(0, Req2()),
1839
+ _ts_param8(1, Param("runId")),
1840
+ _ts_param8(2, Res()),
1330
1841
  _ts_metadata9("design:type", Function),
1331
1842
  _ts_metadata9("design:paramtypes", [
1843
+ typeof Request === "undefined" ? Object : Request,
1332
1844
  String,
1333
1845
  typeof Response === "undefined" ? Object : Response
1334
1846
  ]),
@@ -1336,8 +1848,8 @@ _ts_decorate10([
1336
1848
  ], ChatController.prototype, "stream", null);
1337
1849
  _ts_decorate10([
1338
1850
  Post2("chat/:runId/cancel"),
1339
- _ts_param7(0, Req2()),
1340
- _ts_param7(1, Param("runId")),
1851
+ _ts_param8(0, Req2()),
1852
+ _ts_param8(1, Param("runId")),
1341
1853
  _ts_metadata9("design:type", Function),
1342
1854
  _ts_metadata9("design:paramtypes", [
1343
1855
  typeof Request === "undefined" ? Object : Request,
@@ -1347,7 +1859,7 @@ _ts_decorate10([
1347
1859
  ], ChatController.prototype, "cancel", null);
1348
1860
  ChatController = _ts_decorate10([
1349
1861
  Controller3(),
1350
- _ts_param7(1, Inject7(AGENT_ACTOR_RESOLVER2)),
1862
+ _ts_param8(1, Inject8(AGENT_ACTOR_RESOLVER2)),
1351
1863
  _ts_metadata9("design:type", Function),
1352
1864
  _ts_metadata9("design:paramtypes", [
1353
1865
  typeof AgentService === "undefined" ? Object : AgentService,
@@ -1355,9 +1867,9 @@ ChatController = _ts_decorate10([
1355
1867
  ])
1356
1868
  ], ChatController);
1357
1869
 
1358
- // src/controller/quota.controller.ts
1359
- import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER3 } from "@dudousxd/nestjs-agent-core";
1360
- import { Controller as Controller4, Get as Get3, Inject as Inject8, Req as Req3 } from "@nestjs/common";
1870
+ // src/controller/memories.controller.ts
1871
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER3, AGENT_MEMORY as AGENT_MEMORY2, memoryForgetVerdict, offerMemories } from "@dudousxd/nestjs-agent-core";
1872
+ import { Controller as Controller4, Delete, ForbiddenException as ForbiddenException2, Get as Get4, Inject as Inject9, NotFoundException as NotFoundException2, Optional as Optional5, Param as Param2, Query, Req as Req3 } from "@nestjs/common";
1361
1873
  function _ts_decorate11(decorators, target, key, desc) {
1362
1874
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1363
1875
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1369,12 +1881,138 @@ function _ts_metadata10(k, v) {
1369
1881
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1370
1882
  }
1371
1883
  __name(_ts_metadata10, "_ts_metadata");
1372
- function _ts_param8(paramIndex, decorator) {
1884
+ function _ts_param9(paramIndex, decorator) {
1373
1885
  return function(target, key) {
1374
1886
  decorator(target, key, paramIndex);
1375
1887
  };
1376
1888
  }
1377
- __name(_ts_param8, "_ts_param");
1889
+ __name(_ts_param9, "_ts_param");
1890
+ var MemoriesController = class {
1891
+ static {
1892
+ __name(this, "MemoriesController");
1893
+ }
1894
+ actorResolver;
1895
+ memory;
1896
+ constructor(actorResolver, memory) {
1897
+ this.actorResolver = actorResolver;
1898
+ this.memory = memory;
1899
+ }
1900
+ /**
1901
+ * Every memory this actor can reach, most specific first, each carrying its origin and whatever
1902
+ * wider value it outranks.
1903
+ *
1904
+ * DELIBERATELY IGNORES `maxMemories`. That ceiling is a budget on what one TURN carries; applying
1905
+ * it here would mean a person could not see — and so could not delete — a belief the assistant is
1906
+ * one write away from acting on again. Showing someone more than the model sees is harmless;
1907
+ * showing them less is the failure this endpoint exists to prevent.
1908
+ */
1909
+ async list(req, threadId) {
1910
+ if (this.memory === void 0) {
1911
+ return [];
1912
+ }
1913
+ const actor = await this.actorResolver.resolve(req);
1914
+ return (await this.unbounded(threadId ?? "", actor)).entries;
1915
+ }
1916
+ /**
1917
+ * Forget one. Authorized against the actor's OWN resolved list, the same way a `skill` load is
1918
+ * authorized against the turn's catalog: an id this actor cannot see is answered as missing rather
1919
+ * than refused, so the endpoint cannot be used to find out which memories exist about other people.
1920
+ */
1921
+ async forget(req, id) {
1922
+ if (this.memory === void 0) {
1923
+ throw new NotFoundException2("No memory is configured in this deployment.");
1924
+ }
1925
+ const actor = await this.actorResolver.resolve(req);
1926
+ const digest = await this.unbounded("", actor);
1927
+ const entry = digest.entries.find((candidate) => candidate.id === id);
1928
+ if (entry === void 0) {
1929
+ throw new NotFoundException2(`No memory with id "${id}".`);
1930
+ }
1931
+ const verdict = memoryForgetVerdict({
1932
+ record: entry,
1933
+ actor
1934
+ });
1935
+ if (!verdict.allowed) {
1936
+ throw new ForbiddenException2(verdict.reason);
1937
+ }
1938
+ return {
1939
+ forgotten: await this.memory.provider.forget({
1940
+ id: entry.id,
1941
+ ctx: {
1942
+ actor,
1943
+ threadId: ""
1944
+ }
1945
+ })
1946
+ };
1947
+ }
1948
+ async unbounded(threadId, actor) {
1949
+ const config = this.memory;
1950
+ return await offerMemories({
1951
+ config: {
1952
+ ...config,
1953
+ maxMemories: Number.POSITIVE_INFINITY
1954
+ },
1955
+ ctx: {
1956
+ actor,
1957
+ threadId
1958
+ }
1959
+ });
1960
+ }
1961
+ };
1962
+ _ts_decorate11([
1963
+ Get4(),
1964
+ _ts_param9(0, Req3()),
1965
+ _ts_param9(1, Query("threadId")),
1966
+ _ts_metadata10("design:type", Function),
1967
+ _ts_metadata10("design:paramtypes", [
1968
+ typeof Request === "undefined" ? Object : Request,
1969
+ String
1970
+ ]),
1971
+ _ts_metadata10("design:returntype", Promise)
1972
+ ], MemoriesController.prototype, "list", null);
1973
+ _ts_decorate11([
1974
+ Delete(":id"),
1975
+ _ts_param9(0, Req3()),
1976
+ _ts_param9(1, Param2("id")),
1977
+ _ts_metadata10("design:type", Function),
1978
+ _ts_metadata10("design:paramtypes", [
1979
+ typeof Request === "undefined" ? Object : Request,
1980
+ String
1981
+ ]),
1982
+ _ts_metadata10("design:returntype", Promise)
1983
+ ], MemoriesController.prototype, "forget", null);
1984
+ MemoriesController = _ts_decorate11([
1985
+ Controller4("memories"),
1986
+ _ts_param9(0, Inject9(AGENT_ACTOR_RESOLVER3)),
1987
+ _ts_param9(1, Optional5()),
1988
+ _ts_param9(1, Inject9(AGENT_MEMORY2)),
1989
+ _ts_metadata10("design:type", Function),
1990
+ _ts_metadata10("design:paramtypes", [
1991
+ typeof ActorResolver === "undefined" ? Object : ActorResolver,
1992
+ Object
1993
+ ])
1994
+ ], MemoriesController);
1995
+
1996
+ // src/controller/quota.controller.ts
1997
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER4 } from "@dudousxd/nestjs-agent-core";
1998
+ import { Controller as Controller5, Get as Get5, Inject as Inject10, Req as Req4 } from "@nestjs/common";
1999
+ function _ts_decorate12(decorators, target, key, desc) {
2000
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2001
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2002
+ 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;
2003
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2004
+ }
2005
+ __name(_ts_decorate12, "_ts_decorate");
2006
+ function _ts_metadata11(k, v) {
2007
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2008
+ }
2009
+ __name(_ts_metadata11, "_ts_metadata");
2010
+ function _ts_param10(paramIndex, decorator) {
2011
+ return function(target, key) {
2012
+ decorator(target, key, paramIndex);
2013
+ };
2014
+ }
2015
+ __name(_ts_param10, "_ts_param");
1378
2016
  var QuotaController = class {
1379
2017
  static {
1380
2018
  __name(this, "QuotaController");
@@ -1390,45 +2028,110 @@ var QuotaController = class {
1390
2028
  return this.agent.quotaToday(actor.id);
1391
2029
  }
1392
2030
  };
1393
- _ts_decorate11([
1394
- Get3("today"),
1395
- _ts_param8(0, Req3()),
1396
- _ts_metadata10("design:type", Function),
1397
- _ts_metadata10("design:paramtypes", [
2031
+ _ts_decorate12([
2032
+ Get5("today"),
2033
+ _ts_param10(0, Req4()),
2034
+ _ts_metadata11("design:type", Function),
2035
+ _ts_metadata11("design:paramtypes", [
1398
2036
  typeof Request === "undefined" ? Object : Request
1399
2037
  ]),
1400
- _ts_metadata10("design:returntype", Promise)
2038
+ _ts_metadata11("design:returntype", Promise)
1401
2039
  ], QuotaController.prototype, "today", null);
1402
- QuotaController = _ts_decorate11([
1403
- Controller4("quota"),
1404
- _ts_param8(1, Inject8(AGENT_ACTOR_RESOLVER3)),
1405
- _ts_metadata10("design:type", Function),
1406
- _ts_metadata10("design:paramtypes", [
2040
+ QuotaController = _ts_decorate12([
2041
+ Controller5("quota"),
2042
+ _ts_param10(1, Inject10(AGENT_ACTOR_RESOLVER4)),
2043
+ _ts_metadata11("design:type", Function),
2044
+ _ts_metadata11("design:paramtypes", [
1407
2045
  typeof AgentService === "undefined" ? Object : AgentService,
1408
2046
  typeof ActorResolver === "undefined" ? Object : ActorResolver
1409
2047
  ])
1410
2048
  ], QuotaController);
1411
2049
 
2050
+ // src/controller/skills.controller.ts
2051
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER5, AGENT_SKILLS as AGENT_SKILLS2, offerSkills } from "@dudousxd/nestjs-agent-core";
2052
+ import { Controller as Controller6, Get as Get6, Inject as Inject11, Optional as Optional6, Query as Query2, Req as Req5 } from "@nestjs/common";
2053
+ function _ts_decorate13(decorators, target, key, desc) {
2054
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2055
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2056
+ 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;
2057
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2058
+ }
2059
+ __name(_ts_decorate13, "_ts_decorate");
2060
+ function _ts_metadata12(k, v) {
2061
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2062
+ }
2063
+ __name(_ts_metadata12, "_ts_metadata");
2064
+ function _ts_param11(paramIndex, decorator) {
2065
+ return function(target, key) {
2066
+ decorator(target, key, paramIndex);
2067
+ };
2068
+ }
2069
+ __name(_ts_param11, "_ts_param");
2070
+ var SkillsController = class {
2071
+ static {
2072
+ __name(this, "SkillsController");
2073
+ }
2074
+ actorResolver;
2075
+ skills;
2076
+ constructor(actorResolver, skills) {
2077
+ this.actorResolver = actorResolver;
2078
+ this.skills = skills;
2079
+ }
2080
+ async list(req, threadId) {
2081
+ if (this.skills === void 0) {
2082
+ return [];
2083
+ }
2084
+ const actor = await this.actorResolver.resolve(req);
2085
+ const offer = await offerSkills(this.skills, {
2086
+ actor,
2087
+ threadId: threadId ?? ""
2088
+ });
2089
+ return offer.entries;
2090
+ }
2091
+ };
2092
+ _ts_decorate13([
2093
+ Get6(),
2094
+ _ts_param11(0, Req5()),
2095
+ _ts_param11(1, Query2("threadId")),
2096
+ _ts_metadata12("design:type", Function),
2097
+ _ts_metadata12("design:paramtypes", [
2098
+ typeof Request === "undefined" ? Object : Request,
2099
+ String
2100
+ ]),
2101
+ _ts_metadata12("design:returntype", Promise)
2102
+ ], SkillsController.prototype, "list", null);
2103
+ SkillsController = _ts_decorate13([
2104
+ Controller6("skills"),
2105
+ _ts_param11(0, Inject11(AGENT_ACTOR_RESOLVER5)),
2106
+ _ts_param11(1, Optional6()),
2107
+ _ts_param11(1, Inject11(AGENT_SKILLS2)),
2108
+ _ts_metadata12("design:type", Function),
2109
+ _ts_metadata12("design:paramtypes", [
2110
+ typeof ActorResolver === "undefined" ? Object : ActorResolver,
2111
+ Object
2112
+ ])
2113
+ ], SkillsController);
2114
+
1412
2115
  // src/controller/threads.controller.ts
1413
- import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER4 } from "@dudousxd/nestjs-agent-core";
1414
- import { Body as Body2, Controller as Controller5, Delete, Get as Get4, Inject as Inject9, Param as Param2, Patch, Post as Post3, Req as Req4 } from "@nestjs/common";
1415
- function _ts_decorate12(decorators, target, key, desc) {
2116
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER6 } from "@dudousxd/nestjs-agent-core";
2117
+ import { Body as Body2, Controller as Controller7, Delete as Delete2, Get as Get7, Inject as Inject12, Param as Param3, Patch, Post as Post3, Req as Req6 } from "@nestjs/common";
2118
+ function _ts_decorate14(decorators, target, key, desc) {
1416
2119
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1417
2120
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1418
2121
  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;
1419
2122
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1420
2123
  }
1421
- __name(_ts_decorate12, "_ts_decorate");
1422
- function _ts_metadata11(k, v) {
2124
+ __name(_ts_decorate14, "_ts_decorate");
2125
+ function _ts_metadata13(k, v) {
1423
2126
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1424
2127
  }
1425
- __name(_ts_metadata11, "_ts_metadata");
1426
- function _ts_param9(paramIndex, decorator) {
2128
+ __name(_ts_metadata13, "_ts_metadata");
2129
+ function _ts_param12(paramIndex, decorator) {
1427
2130
  return function(target, key) {
1428
2131
  decorator(target, key, paramIndex);
1429
2132
  };
1430
2133
  }
1431
- __name(_ts_param9, "_ts_param");
2134
+ __name(_ts_param12, "_ts_param");
1432
2135
  var ThreadsController = class {
1433
2136
  static {
1434
2137
  __name(this, "ThreadsController");
@@ -1492,117 +2195,173 @@ var ThreadsController = class {
1492
2195
  };
1493
2196
  }
1494
2197
  };
1495
- _ts_decorate12([
1496
- Get4(),
1497
- _ts_param9(0, Req4()),
1498
- _ts_metadata11("design:type", Function),
1499
- _ts_metadata11("design:paramtypes", [
2198
+ _ts_decorate14([
2199
+ Get7(),
2200
+ _ts_param12(0, Req6()),
2201
+ _ts_metadata13("design:type", Function),
2202
+ _ts_metadata13("design:paramtypes", [
1500
2203
  typeof Request === "undefined" ? Object : Request
1501
2204
  ]),
1502
- _ts_metadata11("design:returntype", Promise)
2205
+ _ts_metadata13("design:returntype", Promise)
1503
2206
  ], ThreadsController.prototype, "list", null);
1504
- _ts_decorate12([
1505
- Get4(":id"),
1506
- _ts_param9(0, Req4()),
1507
- _ts_param9(1, Param2("id")),
1508
- _ts_metadata11("design:type", Function),
1509
- _ts_metadata11("design:paramtypes", [
2207
+ _ts_decorate14([
2208
+ Get7(":id"),
2209
+ _ts_param12(0, Req6()),
2210
+ _ts_param12(1, Param3("id")),
2211
+ _ts_metadata13("design:type", Function),
2212
+ _ts_metadata13("design:paramtypes", [
1510
2213
  typeof Request === "undefined" ? Object : Request,
1511
2214
  String
1512
2215
  ]),
1513
- _ts_metadata11("design:returntype", Promise)
2216
+ _ts_metadata13("design:returntype", Promise)
1514
2217
  ], ThreadsController.prototype, "detail", null);
1515
- _ts_decorate12([
1516
- Delete(":id"),
1517
- _ts_param9(0, Req4()),
1518
- _ts_param9(1, Param2("id")),
1519
- _ts_metadata11("design:type", Function),
1520
- _ts_metadata11("design:paramtypes", [
2218
+ _ts_decorate14([
2219
+ Delete2(":id"),
2220
+ _ts_param12(0, Req6()),
2221
+ _ts_param12(1, Param3("id")),
2222
+ _ts_metadata13("design:type", Function),
2223
+ _ts_metadata13("design:paramtypes", [
1521
2224
  typeof Request === "undefined" ? Object : Request,
1522
2225
  String
1523
2226
  ]),
1524
- _ts_metadata11("design:returntype", Promise)
2227
+ _ts_metadata13("design:returntype", Promise)
1525
2228
  ], ThreadsController.prototype, "remove", null);
1526
- _ts_decorate12([
2229
+ _ts_decorate14([
1527
2230
  Post3(":id/fork-from/:messageId"),
1528
- _ts_param9(0, Req4()),
1529
- _ts_param9(1, Param2("id")),
1530
- _ts_param9(2, Param2("messageId")),
1531
- _ts_metadata11("design:type", Function),
1532
- _ts_metadata11("design:paramtypes", [
2231
+ _ts_param12(0, Req6()),
2232
+ _ts_param12(1, Param3("id")),
2233
+ _ts_param12(2, Param3("messageId")),
2234
+ _ts_metadata13("design:type", Function),
2235
+ _ts_metadata13("design:paramtypes", [
1533
2236
  typeof Request === "undefined" ? Object : Request,
1534
2237
  String,
1535
2238
  String
1536
2239
  ]),
1537
- _ts_metadata11("design:returntype", Promise)
2240
+ _ts_metadata13("design:returntype", Promise)
1538
2241
  ], ThreadsController.prototype, "fork", null);
1539
- _ts_decorate12([
2242
+ _ts_decorate14([
1540
2243
  Patch(":id"),
1541
- _ts_param9(0, Req4()),
1542
- _ts_param9(1, Param2("id")),
1543
- _ts_param9(2, Body2()),
1544
- _ts_metadata11("design:type", Function),
1545
- _ts_metadata11("design:paramtypes", [
2244
+ _ts_param12(0, Req6()),
2245
+ _ts_param12(1, Param3("id")),
2246
+ _ts_param12(2, Body2()),
2247
+ _ts_metadata13("design:type", Function),
2248
+ _ts_metadata13("design:paramtypes", [
1546
2249
  typeof Request === "undefined" ? Object : Request,
1547
2250
  String,
1548
2251
  typeof UpdateThreadBody === "undefined" ? Object : UpdateThreadBody
1549
2252
  ]),
1550
- _ts_metadata11("design:returntype", Promise)
2253
+ _ts_metadata13("design:returntype", Promise)
1551
2254
  ], ThreadsController.prototype, "update", null);
1552
- _ts_decorate12([
2255
+ _ts_decorate14([
1553
2256
  Post3(":id/promote"),
1554
- _ts_param9(0, Req4()),
1555
- _ts_param9(1, Param2("id")),
1556
- _ts_metadata11("design:type", Function),
1557
- _ts_metadata11("design:paramtypes", [
2257
+ _ts_param12(0, Req6()),
2258
+ _ts_param12(1, Param3("id")),
2259
+ _ts_metadata13("design:type", Function),
2260
+ _ts_metadata13("design:paramtypes", [
1558
2261
  typeof Request === "undefined" ? Object : Request,
1559
2262
  String
1560
2263
  ]),
1561
- _ts_metadata11("design:returntype", Promise)
2264
+ _ts_metadata13("design:returntype", Promise)
1562
2265
  ], ThreadsController.prototype, "promote", null);
1563
- _ts_decorate12([
1564
- Delete(":id/from/:messageId"),
1565
- _ts_param9(0, Req4()),
1566
- _ts_param9(1, Param2("id")),
1567
- _ts_param9(2, Param2("messageId")),
1568
- _ts_metadata11("design:type", Function),
1569
- _ts_metadata11("design:paramtypes", [
2266
+ _ts_decorate14([
2267
+ Delete2(":id/from/:messageId"),
2268
+ _ts_param12(0, Req6()),
2269
+ _ts_param12(1, Param3("id")),
2270
+ _ts_param12(2, Param3("messageId")),
2271
+ _ts_metadata13("design:type", Function),
2272
+ _ts_metadata13("design:paramtypes", [
1570
2273
  typeof Request === "undefined" ? Object : Request,
1571
2274
  String,
1572
2275
  String
1573
2276
  ]),
1574
- _ts_metadata11("design:returntype", Promise)
2277
+ _ts_metadata13("design:returntype", Promise)
1575
2278
  ], ThreadsController.prototype, "truncateFrom", null);
1576
- ThreadsController = _ts_decorate12([
1577
- Controller5("threads"),
1578
- _ts_param9(1, Inject9(AGENT_ACTOR_RESOLVER4)),
1579
- _ts_metadata11("design:type", Function),
1580
- _ts_metadata11("design:paramtypes", [
2279
+ ThreadsController = _ts_decorate14([
2280
+ Controller7("threads"),
2281
+ _ts_param12(1, Inject12(AGENT_ACTOR_RESOLVER6)),
2282
+ _ts_metadata13("design:type", Function),
2283
+ _ts_metadata13("design:paramtypes", [
1581
2284
  typeof AgentService === "undefined" ? Object : AgentService,
1582
2285
  typeof ActorResolver === "undefined" ? Object : ActorResolver
1583
2286
  ])
1584
2287
  ], ThreadsController);
1585
2288
 
1586
2289
  // src/controller/tool-call.controller.ts
1587
- import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER5 } from "@dudousxd/nestjs-agent-core";
1588
- import { Body as Body3, Controller as Controller6, Inject as Inject10, Post as Post4, Req as Req5 } from "@nestjs/common";
1589
- function _ts_decorate13(decorators, target, key, desc) {
2290
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER7 } from "@dudousxd/nestjs-agent-core";
2291
+ import { BadRequestException as BadRequestException4, Body as Body3, Controller as Controller8, Inject as Inject13, Post as Post4, Req as Req7 } from "@nestjs/common";
2292
+ function _ts_decorate15(decorators, target, key, desc) {
1590
2293
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1591
2294
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1592
2295
  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;
1593
2296
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1594
2297
  }
1595
- __name(_ts_decorate13, "_ts_decorate");
1596
- function _ts_metadata12(k, v) {
2298
+ __name(_ts_decorate15, "_ts_decorate");
2299
+ function _ts_metadata14(k, v) {
1597
2300
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1598
2301
  }
1599
- __name(_ts_metadata12, "_ts_metadata");
1600
- function _ts_param10(paramIndex, decorator) {
2302
+ __name(_ts_metadata14, "_ts_metadata");
2303
+ function _ts_param13(paramIndex, decorator) {
1601
2304
  return function(target, key) {
1602
2305
  decorator(target, key, paramIndex);
1603
2306
  };
1604
2307
  }
1605
- __name(_ts_param10, "_ts_param");
2308
+ __name(_ts_param13, "_ts_param");
2309
+ var MAX_ANSWERED_QUESTIONS = 100;
2310
+ var MAX_VALUES_PER_QUESTION = 100;
2311
+ var MAX_ANSWER_VALUE_LENGTH = 4096;
2312
+ var MAX_REJECT_REASON_LENGTH = 4096;
2313
+ function toolCallId(claimed) {
2314
+ if (typeof claimed !== "string" || claimed.length === 0) {
2315
+ throw new BadRequestException4("toolCallId must be a non-empty string");
2316
+ }
2317
+ return claimed;
2318
+ }
2319
+ __name(toolCallId, "toolCallId");
2320
+ function rejectReason(claimed) {
2321
+ if (claimed === void 0) {
2322
+ return void 0;
2323
+ }
2324
+ if (typeof claimed !== "string") {
2325
+ throw new BadRequestException4("reason must be a string");
2326
+ }
2327
+ if (claimed.length > MAX_REJECT_REASON_LENGTH) {
2328
+ throw new BadRequestException4(`reason must be at most ${MAX_REJECT_REASON_LENGTH} characters`);
2329
+ }
2330
+ return claimed;
2331
+ }
2332
+ __name(rejectReason, "rejectReason");
2333
+ function answers(claimed) {
2334
+ if (claimed === void 0) {
2335
+ return void 0;
2336
+ }
2337
+ if (typeof claimed !== "object" || claimed === null || Array.isArray(claimed)) {
2338
+ throw new BadRequestException4("answers must be an object of questionId \u2192 chosen values");
2339
+ }
2340
+ const entries = Object.entries(claimed);
2341
+ if (entries.length > MAX_ANSWERED_QUESTIONS) {
2342
+ throw new BadRequestException4(`answers may cover at most ${MAX_ANSWERED_QUESTIONS} questions`);
2343
+ }
2344
+ const validated = /* @__PURE__ */ Object.create(null);
2345
+ for (const [questionId, values] of entries) {
2346
+ if (!Array.isArray(values)) {
2347
+ throw new BadRequestException4(`answers["${questionId}"] must be an array of chosen values`);
2348
+ }
2349
+ if (values.length > MAX_VALUES_PER_QUESTION) {
2350
+ throw new BadRequestException4(`answers["${questionId}"] may carry at most ${MAX_VALUES_PER_QUESTION} values`);
2351
+ }
2352
+ for (const value of values) {
2353
+ if (typeof value !== "string") {
2354
+ throw new BadRequestException4(`answers["${questionId}"] must contain only strings`);
2355
+ }
2356
+ if (value.length > MAX_ANSWER_VALUE_LENGTH) {
2357
+ throw new BadRequestException4(`answers["${questionId}"] values must be at most ${MAX_ANSWER_VALUE_LENGTH} characters`);
2358
+ }
2359
+ }
2360
+ validated[questionId] = values;
2361
+ }
2362
+ return validated;
2363
+ }
2364
+ __name(answers, "answers");
1606
2365
  var ToolCallController = class {
1607
2366
  static {
1608
2367
  __name(this, "ToolCallController");
@@ -1615,46 +2374,88 @@ var ToolCallController = class {
1615
2374
  }
1616
2375
  async approve(req, body) {
1617
2376
  const actor = await this.actorResolver.resolve(req);
1618
- await this.agent.approve(actor, body.toolCallId);
2377
+ await this.agent.approve(actor, toolCallId(body.toolCallId));
1619
2378
  return {
1620
2379
  ok: true
1621
2380
  };
1622
2381
  }
1623
2382
  async reject(req, body) {
1624
2383
  const actor = await this.actorResolver.resolve(req);
1625
- await this.agent.reject(actor, body.toolCallId, body.reason);
2384
+ await this.agent.reject(actor, toolCallId(body.toolCallId), rejectReason(body.reason));
2385
+ return {
2386
+ ok: true
2387
+ };
2388
+ }
2389
+ /**
2390
+ * Settle a parked question set. Alongside `approve`/`reject` rather than under a surface of its
2391
+ * own: an elicitation IS a parked tool call, gated by the same ownership check and delivered
2392
+ * through the same signal.
2393
+ */
2394
+ async answer(req, body) {
2395
+ const actor = await this.actorResolver.resolve(req);
2396
+ await this.agent.answer(actor, toolCallId(body.toolCallId), answers(body.answers));
2397
+ return {
2398
+ ok: true
2399
+ };
2400
+ }
2401
+ /** Decline to answer and let the agent proceed on the answers it pre-picked. */
2402
+ async skip(req, body) {
2403
+ const actor = await this.actorResolver.resolve(req);
2404
+ await this.agent.skip(actor, toolCallId(body.toolCallId));
1626
2405
  return {
1627
2406
  ok: true
1628
2407
  };
1629
2408
  }
1630
2409
  };
1631
- _ts_decorate13([
2410
+ _ts_decorate15([
1632
2411
  Post4("approve"),
1633
- _ts_param10(0, Req5()),
1634
- _ts_param10(1, Body3()),
1635
- _ts_metadata12("design:type", Function),
1636
- _ts_metadata12("design:paramtypes", [
2412
+ _ts_param13(0, Req7()),
2413
+ _ts_param13(1, Body3()),
2414
+ _ts_metadata14("design:type", Function),
2415
+ _ts_metadata14("design:paramtypes", [
1637
2416
  typeof Request === "undefined" ? Object : Request,
1638
2417
  typeof ApproveBody === "undefined" ? Object : ApproveBody
1639
2418
  ]),
1640
- _ts_metadata12("design:returntype", Promise)
2419
+ _ts_metadata14("design:returntype", Promise)
1641
2420
  ], ToolCallController.prototype, "approve", null);
1642
- _ts_decorate13([
2421
+ _ts_decorate15([
1643
2422
  Post4("reject"),
1644
- _ts_param10(0, Req5()),
1645
- _ts_param10(1, Body3()),
1646
- _ts_metadata12("design:type", Function),
1647
- _ts_metadata12("design:paramtypes", [
2423
+ _ts_param13(0, Req7()),
2424
+ _ts_param13(1, Body3()),
2425
+ _ts_metadata14("design:type", Function),
2426
+ _ts_metadata14("design:paramtypes", [
1648
2427
  typeof Request === "undefined" ? Object : Request,
1649
2428
  typeof RejectBody === "undefined" ? Object : RejectBody
1650
2429
  ]),
1651
- _ts_metadata12("design:returntype", Promise)
2430
+ _ts_metadata14("design:returntype", Promise)
1652
2431
  ], ToolCallController.prototype, "reject", null);
1653
- ToolCallController = _ts_decorate13([
1654
- Controller6("tool-call"),
1655
- _ts_param10(1, Inject10(AGENT_ACTOR_RESOLVER5)),
1656
- _ts_metadata12("design:type", Function),
1657
- _ts_metadata12("design:paramtypes", [
2432
+ _ts_decorate15([
2433
+ Post4("answer"),
2434
+ _ts_param13(0, Req7()),
2435
+ _ts_param13(1, Body3()),
2436
+ _ts_metadata14("design:type", Function),
2437
+ _ts_metadata14("design:paramtypes", [
2438
+ typeof Request === "undefined" ? Object : Request,
2439
+ typeof AnswerBody === "undefined" ? Object : AnswerBody
2440
+ ]),
2441
+ _ts_metadata14("design:returntype", Promise)
2442
+ ], ToolCallController.prototype, "answer", null);
2443
+ _ts_decorate15([
2444
+ Post4("skip"),
2445
+ _ts_param13(0, Req7()),
2446
+ _ts_param13(1, Body3()),
2447
+ _ts_metadata14("design:type", Function),
2448
+ _ts_metadata14("design:paramtypes", [
2449
+ typeof Request === "undefined" ? Object : Request,
2450
+ typeof ApproveBody === "undefined" ? Object : ApproveBody
2451
+ ]),
2452
+ _ts_metadata14("design:returntype", Promise)
2453
+ ], ToolCallController.prototype, "skip", null);
2454
+ ToolCallController = _ts_decorate15([
2455
+ Controller8("tool-call"),
2456
+ _ts_param13(1, Inject13(AGENT_ACTOR_RESOLVER7)),
2457
+ _ts_metadata14("design:type", Function),
2458
+ _ts_metadata14("design:paramtypes", [
1658
2459
  typeof AgentService === "undefined" ? Object : AgentService,
1659
2460
  typeof ActorResolver === "undefined" ? Object : ActorResolver
1660
2461
  ])
@@ -1662,7 +2463,7 @@ ToolCallController = _ts_decorate13([
1662
2463
 
1663
2464
  // src/discovery/agent-discovery.service.ts
1664
2465
  import { AGENT_PROMPT_CONTRIBUTORS as AGENT_PROMPT_CONTRIBUTORS2, AGENT_REGISTRY as AGENT_REGISTRY3, AgentRegistry as AgentRegistry4 } from "@dudousxd/nestjs-agent-core";
1665
- import { Inject as Inject11, Injectable as Injectable7, Logger as Logger2 } from "@nestjs/common";
2466
+ import { Inject as Inject14, Injectable as Injectable7, Logger as Logger3 } from "@nestjs/common";
1666
2467
  import { DiscoveryService, MetadataScanner } from "@nestjs/core";
1667
2468
 
1668
2469
  // src/decorator/agent.decorator.ts
@@ -1679,23 +2480,23 @@ var SYSTEM_PROMPT_METADATA = Symbol.for("@dudousxd/nestjs-agent:system-prompt");
1679
2480
  var SYSTEM_PROMPT_CONTRIBUTOR_METADATA = Symbol.for("@dudousxd/nestjs-agent:system-prompt-contributor");
1680
2481
 
1681
2482
  // src/discovery/agent-discovery.service.ts
1682
- function _ts_decorate14(decorators, target, key, desc) {
2483
+ function _ts_decorate16(decorators, target, key, desc) {
1683
2484
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1684
2485
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1685
2486
  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;
1686
2487
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1687
2488
  }
1688
- __name(_ts_decorate14, "_ts_decorate");
1689
- function _ts_metadata13(k, v) {
2489
+ __name(_ts_decorate16, "_ts_decorate");
2490
+ function _ts_metadata15(k, v) {
1690
2491
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1691
2492
  }
1692
- __name(_ts_metadata13, "_ts_metadata");
1693
- function _ts_param11(paramIndex, decorator) {
2493
+ __name(_ts_metadata15, "_ts_metadata");
2494
+ function _ts_param14(paramIndex, decorator) {
1694
2495
  return function(target, key) {
1695
2496
  decorator(target, key, paramIndex);
1696
2497
  };
1697
2498
  }
1698
- __name(_ts_param11, "_ts_param");
2499
+ __name(_ts_param14, "_ts_param");
1699
2500
  var AgentDiscoveryService = class _AgentDiscoveryService {
1700
2501
  static {
1701
2502
  __name(this, "AgentDiscoveryService");
@@ -1704,7 +2505,7 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
1704
2505
  scanner;
1705
2506
  registry;
1706
2507
  contributors;
1707
- logger = new Logger2(_AgentDiscoveryService.name);
2508
+ logger = new Logger3(_AgentDiscoveryService.name);
1708
2509
  constructor(discovery, scanner, registry, contributors) {
1709
2510
  this.discovery = discovery;
1710
2511
  this.scanner = scanner;
@@ -1743,23 +2544,53 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
1743
2544
  ...meta.maxSteps !== void 0 ? {
1744
2545
  maxSteps: meta.maxSteps
1745
2546
  } : {},
2547
+ ...meta.maxDelegationDepth !== void 0 ? {
2548
+ maxDelegationDepth: meta.maxDelegationDepth
2549
+ } : {},
2550
+ ...meta.maxAgentAppearances !== void 0 ? {
2551
+ maxAgentAppearances: meta.maxAgentAppearances
2552
+ } : {},
2553
+ ...meta.history !== void 0 ? {
2554
+ history: meta.history
2555
+ } : {},
2556
+ ...meta.outputSchema !== void 0 ? {
2557
+ outputSchema: meta.outputSchema
2558
+ } : {},
2559
+ ...meta.intake !== void 0 ? {
2560
+ intake: meta.intake
2561
+ } : {},
2562
+ ...meta.ask !== void 0 ? {
2563
+ ask: meta.ask
2564
+ } : {},
2565
+ ...meta.outputRepairAttempts !== void 0 ? {
2566
+ outputRepairAttempts: meta.outputRepairAttempts
2567
+ } : {},
1746
2568
  ...meta.handoff !== void 0 ? {
1747
- delegatesTo: this.handoffNames(meta)
2569
+ delegatesTo: this.handoffEdges(meta)
1748
2570
  } : {}
1749
2571
  };
1750
2572
  }
1751
- /** Resolve each handoff `@Agent` class to its registered name (skipping non-agent classes). */
1752
- handoffNames(meta) {
1753
- const names = [];
1754
- for (const target of meta.handoff ?? []) {
2573
+ /**
2574
+ * Resolve each handoff entry to an {@link AgentDelegation} — its target's registered name, plus
2575
+ * whether that edge detaches (skipping non-agent classes). A bare name is kept for a plain edge
2576
+ * rather than an always-object form, so a definition an older host built by hand still reads.
2577
+ */
2578
+ handoffEdges(meta) {
2579
+ const edges = [];
2580
+ for (const entry of meta.handoff ?? []) {
2581
+ const target = typeof entry === "function" ? entry : entry.agent;
2582
+ const detached = typeof entry === "function" ? false : entry.detached === true;
1755
2583
  const targetMeta = readAgentMetadata(target);
1756
2584
  if (targetMeta === void 0) {
1757
2585
  this.logger.warn(`Agent "${meta.name}" hands off to ${target.name}, which is not an @Agent \u2014 skipped.`);
1758
2586
  continue;
1759
2587
  }
1760
- names.push(targetMeta.name);
2588
+ edges.push(detached ? {
2589
+ agent: targetMeta.name,
2590
+ detached: true
2591
+ } : targetMeta.name);
1761
2592
  }
1762
- return names;
2593
+ return edges;
1763
2594
  }
1764
2595
  /** Push every `@SystemPromptContributor()` method on `instance` (bound) into the contributor list. */
1765
2596
  collectContributors(instance) {
@@ -1792,12 +2623,12 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
1792
2623
  return (ctx) => candidate.call(instance, ctx);
1793
2624
  }
1794
2625
  };
1795
- AgentDiscoveryService = _ts_decorate14([
2626
+ AgentDiscoveryService = _ts_decorate16([
1796
2627
  Injectable7(),
1797
- _ts_param11(2, Inject11(AGENT_REGISTRY3)),
1798
- _ts_param11(3, Inject11(AGENT_PROMPT_CONTRIBUTORS2)),
1799
- _ts_metadata13("design:type", Function),
1800
- _ts_metadata13("design:paramtypes", [
2628
+ _ts_param14(2, Inject14(AGENT_REGISTRY3)),
2629
+ _ts_param14(3, Inject14(AGENT_PROMPT_CONTRIBUTORS2)),
2630
+ _ts_metadata15("design:type", Function),
2631
+ _ts_metadata15("design:paramtypes", [
1801
2632
  typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
1802
2633
  typeof MetadataScanner === "undefined" ? Object : MetadataScanner,
1803
2634
  typeof AgentRegistry4 === "undefined" ? Object : AgentRegistry4,
@@ -1806,8 +2637,8 @@ AgentDiscoveryService = _ts_decorate14([
1806
2637
  ], AgentDiscoveryService);
1807
2638
 
1808
2639
  // src/discovery/ai-tool-discovery.service.ts
1809
- import { AGENT_OPTIONS as AGENT_OPTIONS4, AGENT_REGISTRY as AGENT_REGISTRY4, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY2 } from "@dudousxd/nestjs-agent-core";
1810
- import { Inject as Inject12, Injectable as Injectable8, Logger as Logger3 } from "@nestjs/common";
2640
+ import { AGENT_OPTIONS as AGENT_OPTIONS4, AGENT_REGISTRY as AGENT_REGISTRY4, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY2, normalizeDelegation as normalizeDelegation2 } from "@dudousxd/nestjs-agent-core";
2641
+ import { Inject as Inject15, Injectable as Injectable8, Logger as Logger4 } from "@nestjs/common";
1811
2642
  import { DiscoveryService as DiscoveryService2 } from "@nestjs/core";
1812
2643
  import { z } from "zod";
1813
2644
 
@@ -1827,23 +2658,23 @@ function isBrandedFunctionalTool(value) {
1827
2658
  __name(isBrandedFunctionalTool, "isBrandedFunctionalTool");
1828
2659
 
1829
2660
  // src/discovery/ai-tool-discovery.service.ts
1830
- function _ts_decorate15(decorators, target, key, desc) {
2661
+ function _ts_decorate17(decorators, target, key, desc) {
1831
2662
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1832
2663
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1833
2664
  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;
1834
2665
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1835
2666
  }
1836
- __name(_ts_decorate15, "_ts_decorate");
1837
- function _ts_metadata14(k, v) {
2667
+ __name(_ts_decorate17, "_ts_decorate");
2668
+ function _ts_metadata16(k, v) {
1838
2669
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1839
2670
  }
1840
- __name(_ts_metadata14, "_ts_metadata");
1841
- function _ts_param12(paramIndex, decorator) {
2671
+ __name(_ts_metadata16, "_ts_metadata");
2672
+ function _ts_param15(paramIndex, decorator) {
1842
2673
  return function(target, key) {
1843
2674
  decorator(target, key, paramIndex);
1844
2675
  };
1845
2676
  }
1846
- __name(_ts_param12, "_ts_param");
2677
+ __name(_ts_param15, "_ts_param");
1847
2678
  var AiToolDiscoveryService = class _AiToolDiscoveryService {
1848
2679
  static {
1849
2680
  __name(this, "AiToolDiscoveryService");
@@ -1852,7 +2683,7 @@ var AiToolDiscoveryService = class _AiToolDiscoveryService {
1852
2683
  registry;
1853
2684
  agents;
1854
2685
  options;
1855
- logger = new Logger3(_AiToolDiscoveryService.name);
2686
+ logger = new Logger4(_AiToolDiscoveryService.name);
1856
2687
  constructor(discovery, registry, agents, options) {
1857
2688
  this.discovery = discovery;
1858
2689
  this.registry = registry;
@@ -1919,22 +2750,30 @@ var AiToolDiscoveryService = class _AiToolDiscoveryService {
1919
2750
  }
1920
2751
  let delegates = 0;
1921
2752
  for (const definition of this.agents.list()) {
1922
- for (const target of definition.delegatesTo ?? []) {
2753
+ for (const edge of definition.delegatesTo ?? []) {
2754
+ const { agent: target, detached } = normalizeDelegation2(edge);
1923
2755
  const targetDefinition = this.agents.get(target);
1924
2756
  if (targetDefinition === void 0) {
1925
2757
  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.`);
1926
2758
  }
1927
- const name = delegateToolName(target);
2759
+ const name = delegateToolName({
2760
+ target,
2761
+ detached
2762
+ });
1928
2763
  if (this.registry.has(name)) {
1929
2764
  continue;
1930
2765
  }
1931
2766
  const targetBlurb = typeof targetDefinition.systemPrompt === "string" ? ` It is: ${targetDefinition.systemPrompt}` : "";
2767
+ 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}`;
1932
2768
  this.registry.register(
1933
2769
  {
1934
2770
  name,
1935
2771
  kind: "agent",
1936
2772
  targetAgent: target,
1937
- description: `Delegate a task to the "${target}" agent and get its answer.${targetBlurb}`,
2773
+ ...detached ? {
2774
+ detached: true
2775
+ } : {},
2776
+ description,
1938
2777
  inputSchema: z.object({
1939
2778
  task: z.string()
1940
2779
  })
@@ -1962,13 +2801,13 @@ var AiToolDiscoveryService = class _AiToolDiscoveryService {
1962
2801
  return true;
1963
2802
  }
1964
2803
  };
1965
- AiToolDiscoveryService = _ts_decorate15([
2804
+ AiToolDiscoveryService = _ts_decorate17([
1966
2805
  Injectable8(),
1967
- _ts_param12(1, Inject12(AGENT_TOOL_REGISTRY2)),
1968
- _ts_param12(2, Inject12(AGENT_REGISTRY4)),
1969
- _ts_param12(3, Inject12(AGENT_OPTIONS4)),
1970
- _ts_metadata14("design:type", Function),
1971
- _ts_metadata14("design:paramtypes", [
2806
+ _ts_param15(1, Inject15(AGENT_TOOL_REGISTRY2)),
2807
+ _ts_param15(2, Inject15(AGENT_REGISTRY4)),
2808
+ _ts_param15(3, Inject15(AGENT_OPTIONS4)),
2809
+ _ts_metadata16("design:type", Function),
2810
+ _ts_metadata16("design:paramtypes", [
1972
2811
  typeof DiscoveryService2 === "undefined" ? Object : DiscoveryService2,
1973
2812
  typeof ToolRegistry === "undefined" ? Object : ToolRegistry,
1974
2813
  typeof AgentRegistry === "undefined" ? Object : AgentRegistry,
@@ -1976,6 +2815,95 @@ AiToolDiscoveryService = _ts_decorate15([
1976
2815
  ])
1977
2816
  ], AiToolDiscoveryService);
1978
2817
 
2818
+ // src/discovery/skill-discovery.service.ts
2819
+ import { AGENT_SKILL_SOURCES } from "@dudousxd/nestjs-agent-core";
2820
+ import { Inject as Inject16, Injectable as Injectable9, Logger as Logger5 } from "@nestjs/common";
2821
+ import { DiscoveryService as DiscoveryService3 } from "@nestjs/core";
2822
+
2823
+ // src/decorator/skill.decorator.ts
2824
+ import "reflect-metadata";
2825
+ import { GLOBAL_SCOPE } from "@dudousxd/nestjs-agent-core";
2826
+ var SKILL_METADATA = Symbol.for("@dudousxd/nestjs-agent:skill-metadata");
2827
+ function readSkillMetadata(target) {
2828
+ return Reflect.getMetadata(SKILL_METADATA, target);
2829
+ }
2830
+ __name(readSkillMetadata, "readSkillMetadata");
2831
+ function skillScope(options) {
2832
+ return options.scope ?? GLOBAL_SCOPE;
2833
+ }
2834
+ __name(skillScope, "skillScope");
2835
+
2836
+ // src/discovery/skill-discovery.service.ts
2837
+ function _ts_decorate18(decorators, target, key, desc) {
2838
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2839
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2840
+ 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;
2841
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2842
+ }
2843
+ __name(_ts_decorate18, "_ts_decorate");
2844
+ function _ts_metadata17(k, v) {
2845
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2846
+ }
2847
+ __name(_ts_metadata17, "_ts_metadata");
2848
+ function _ts_param16(paramIndex, decorator) {
2849
+ return function(target, key) {
2850
+ decorator(target, key, paramIndex);
2851
+ };
2852
+ }
2853
+ __name(_ts_param16, "_ts_param");
2854
+ var SkillDiscoveryService = class _SkillDiscoveryService {
2855
+ static {
2856
+ __name(this, "SkillDiscoveryService");
2857
+ }
2858
+ discovery;
2859
+ declared;
2860
+ logger = new Logger5(_SkillDiscoveryService.name);
2861
+ constructor(discovery, declared) {
2862
+ this.discovery = discovery;
2863
+ this.declared = declared;
2864
+ }
2865
+ onModuleInit() {
2866
+ for (const wrapper of this.discovery.getProviders()) {
2867
+ const instance = wrapper.instance;
2868
+ if (instance === null || typeof instance !== "object") {
2869
+ continue;
2870
+ }
2871
+ const meta = readSkillMetadata(instance.constructor);
2872
+ if (meta === void 0) {
2873
+ continue;
2874
+ }
2875
+ const body = this.bodyOf(instance, meta.body);
2876
+ if (body === void 0) {
2877
+ this.logger.warn(`Skill "${meta.name}" declares no body \u2014 add a \`body()\` method or \`@Skill({ body })\`. Skipped.`);
2878
+ continue;
2879
+ }
2880
+ this.declared.push({
2881
+ name: meta.name,
2882
+ description: meta.description,
2883
+ scope: skillScope(meta),
2884
+ body
2885
+ });
2886
+ }
2887
+ }
2888
+ /** The instance's `body()` method (bound), else the flat string, else nothing to offer. */
2889
+ bodyOf(instance, flat) {
2890
+ const candidate = instance.body;
2891
+ if (typeof candidate === "function") {
2892
+ return (ctx) => candidate.call(instance, ctx);
2893
+ }
2894
+ return flat === void 0 ? void 0 : () => flat;
2895
+ }
2896
+ };
2897
+ SkillDiscoveryService = _ts_decorate18([
2898
+ Injectable9(),
2899
+ _ts_param16(1, Inject16(AGENT_SKILL_SOURCES)),
2900
+ _ts_metadata17("design:type", Function),
2901
+ _ts_metadata17("design:paramtypes", [
2902
+ typeof DiscoveryService3 === "undefined" ? Object : DiscoveryService3,
2903
+ Array
2904
+ ])
2905
+ ], SkillDiscoveryService);
2906
+
1979
2907
  // src/ledger-quota-store.ts
1980
2908
  var LedgerQuotaStore = class {
1981
2909
  static {
@@ -1995,38 +2923,44 @@ var LedgerQuotaStore = class {
1995
2923
  withinLimit: usedTokens < this.limitTokens
1996
2924
  };
1997
2925
  }
1998
- async bump() {
2926
+ async bump(_actorRef, _day, _tokens) {
1999
2927
  }
2000
2928
  };
2001
2929
 
2002
2930
  // src/runner/inline-agent-runner.ts
2003
- import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY5, AGENT_STORE as AGENT_STORE4, QuotaExceededError as QuotaExceededError2, publishAgentRunFailed as publishAgentRunFailed2, runAgentLoop as runAgentLoop2 } from "@dudousxd/nestjs-agent-core";
2004
- import { Inject as Inject13, Injectable as Injectable9, Logger as Logger4 } from "@nestjs/common";
2005
- function _ts_decorate16(decorators, target, key, desc) {
2931
+ import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY5, AGENT_STORE as AGENT_STORE5, RunCancelledError as RunCancelledError2, agentFailureCode as agentFailureCode2, encodeStreamEvent as encodeStreamEvent2, publishAgentRunFailed as publishAgentRunFailed2, runAgentLoop as runAgentLoop2, settleAll as settleAll2, settleUnsettledDelegation as settleUnsettledDelegation2 } from "@dudousxd/nestjs-agent-core";
2932
+ import { Inject as Inject17, Injectable as Injectable10, Logger as Logger6 } from "@nestjs/common";
2933
+ function _ts_decorate19(decorators, target, key, desc) {
2006
2934
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2007
2935
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2008
2936
  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;
2009
2937
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2010
2938
  }
2011
- __name(_ts_decorate16, "_ts_decorate");
2012
- function _ts_metadata15(k, v) {
2939
+ __name(_ts_decorate19, "_ts_decorate");
2940
+ function _ts_metadata18(k, v) {
2013
2941
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2014
2942
  }
2015
- __name(_ts_metadata15, "_ts_metadata");
2016
- function _ts_param13(paramIndex, decorator) {
2943
+ __name(_ts_metadata18, "_ts_metadata");
2944
+ function _ts_param17(paramIndex, decorator) {
2017
2945
  return function(target, key) {
2018
2946
  decorator(target, key, paramIndex);
2019
2947
  };
2020
2948
  }
2021
- __name(_ts_param13, "_ts_param");
2949
+ __name(_ts_param17, "_ts_param");
2022
2950
  var InlineAgentRunner = class _InlineAgentRunner {
2023
2951
  static {
2024
2952
  __name(this, "InlineAgentRunner");
2025
2953
  }
2026
2954
  factory;
2027
2955
  store;
2028
- logger = new Logger4(_InlineAgentRunner.name);
2956
+ logger = new Logger6(_InlineAgentRunner.name);
2029
2957
  pending = /* @__PURE__ */ new Map();
2958
+ /**
2959
+ * Runs someone has asked to stop. In-process, like `pending`, and for the same reason: this runner
2960
+ * is single-replica by construction, so the request is readable by the only loop that could be
2961
+ * running it. A cancelled id is dropped once the run settles, so the set tracks live runs only.
2962
+ */
2963
+ cancelled = /* @__PURE__ */ new Set();
2030
2964
  constructor(factory, store) {
2031
2965
  this.factory = factory;
2032
2966
  this.store = store;
@@ -2035,13 +2969,29 @@ var InlineAgentRunner = class _InlineAgentRunner {
2035
2969
  const runId = crypto.randomUUID();
2036
2970
  const day = input.day ?? utcDay();
2037
2971
  const deps = this.factory.forAgent(input.agentName);
2038
- const hooks = this.topLevelHooks(runId, deps, input.actor, day);
2972
+ const hooks = this.topLevelHooks({
2973
+ runId,
2974
+ deps,
2975
+ actor: input.actor,
2976
+ day,
2977
+ threadId: input.threadId,
2978
+ chainBelow: [
2979
+ ...input.delegationPath ?? [],
2980
+ ...input.agentName !== void 0 ? [
2981
+ input.agentName
2982
+ ] : []
2983
+ ]
2984
+ });
2039
2985
  void runAgentLoop2({
2040
2986
  ...deps,
2041
2987
  day
2042
2988
  }, input, hooks).then(() => this.store.setActiveStream(input.threadId, null)).catch(async (error) => {
2989
+ if (error instanceof RunCancelledError2) {
2990
+ await this.settleCancelled(runId, input.threadId, deps);
2991
+ return;
2992
+ }
2043
2993
  const message = error instanceof Error ? error.message : String(error);
2044
- const code = error instanceof QuotaExceededError2 ? "quota_exceeded" : "run_failed";
2994
+ const code = agentFailureCode2(error);
2045
2995
  this.logger.error(`agent run ${runId} failed (${code}): ${message}`);
2046
2996
  publishAgentRunFailed2({
2047
2997
  runId,
@@ -2060,33 +3010,216 @@ var InlineAgentRunner = class _InlineAgentRunner {
2060
3010
  code,
2061
3011
  message
2062
3012
  });
2063
- });
3013
+ }).finally(() => this.cancelled.delete(runId));
2064
3014
  return {
2065
3015
  runId
2066
3016
  };
2067
3017
  }
2068
- async signal(runId, toolCallId, decision) {
2069
- const key = `${runId}:${toolCallId}`;
2070
- const resolve = this.pending.get(key);
2071
- if (resolve !== void 0) {
3018
+ async signal(runId, toolCallId2, reply) {
3019
+ const key = `${runId}:${toolCallId2}`;
3020
+ const waiter = this.pending.get(key);
3021
+ if (waiter !== void 0) {
2072
3022
  this.pending.delete(key);
2073
- resolve(decision);
3023
+ waiter.resolve(reply);
2074
3024
  }
2075
3025
  }
3026
+ /**
3027
+ * Ask a run to stop, and settle whatever it was parked on.
3028
+ *
3029
+ * The loop observes the request at its next safe point (between steps, before the turn's tools) and
3030
+ * unwinds from there — so a tool already executing is left to finish, and its result is recorded
3031
+ * exactly as it would have been. What this cannot wait for is a turn parked on a human: that wait
3032
+ * is a promise nobody is going to resolve, so it is REJECTED here with the same
3033
+ * {@link RunCancelledError} the loop would have thrown, and the run unwinds through the identical
3034
+ * path.
3035
+ *
3036
+ * Settlement — the run row, the thread's active stream, the stream's terminal — belongs to the
3037
+ * `RunCancelledError` catch in `start`, so a cancel and an ordinary failure settle in one place
3038
+ * each and can never both write.
3039
+ */
2076
3040
  async cancel(runId) {
2077
- const deps = this.factory.forAgent();
3041
+ this.cancelled.add(runId);
3042
+ for (const [key, waiter] of this.pending) {
3043
+ if (key.startsWith(`${runId}:`)) {
3044
+ this.pending.delete(key);
3045
+ waiter.reject(new RunCancelledError2());
3046
+ }
3047
+ }
3048
+ await Promise.resolve();
3049
+ }
3050
+ /**
3051
+ * The single place a cancelled run's state is settled: recorded as its own terminal (never
3052
+ * `failed` — a user pressing Stop must not land in someone's error rate), the thread released, and
3053
+ * the stream ENDED after a `cancelled` frame rather than failed, so a client that retries failed
3054
+ * streams does not retry this one.
3055
+ */
3056
+ async settleCancelled(runId, threadId, deps) {
3057
+ this.logger.log(`agent run ${runId} cancelled`);
3058
+ await this.store.setActiveStream(threadId, null);
2078
3059
  const writer = await deps.sink.open(runId);
3060
+ await writer.write(encodeStreamEvent2({
3061
+ kind: "cancelled"
3062
+ }));
2079
3063
  await writer.end();
3064
+ await this.store.recordRunEnd?.({
3065
+ runId,
3066
+ status: "cancelled"
3067
+ });
3068
+ }
3069
+ /**
3070
+ * Park a run on a human, keyed by run + tool call. One map for approvals and for question-set
3071
+ * answers alike: both wait on the same key, and `signal` cannot tell (or need to tell) which
3072
+ * shape it is delivering — the loop asked for one and only ever gets what the caller sent. A
3073
+ * cancel settles the wait too, by rejecting it.
3074
+ */
3075
+ park(runId, toolCallId2) {
3076
+ return new Promise((resolve, reject) => {
3077
+ this.pending.set(`${runId}:${toolCallId2}`, {
3078
+ resolve,
3079
+ reject
3080
+ });
3081
+ });
2080
3082
  }
2081
- topLevelHooks(runId, deps, actor, day) {
3083
+ topLevelHooks(args) {
3084
+ const { runId, deps, actor, day, threadId, chainBelow } = args;
2082
3085
  return {
2083
3086
  runId,
2084
3087
  openSink: /* @__PURE__ */ __name(() => deps.sink.open(runId), "openSink"),
2085
- awaitApproval: /* @__PURE__ */ __name((call) => new Promise((resolve) => {
2086
- this.pending.set(`${runId}:${call.id}`, resolve);
2087
- }), "awaitApproval"),
3088
+ awaitApproval: /* @__PURE__ */ __name((call) => this.park(runId, call.id), "awaitApproval"),
3089
+ awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
3090
+ step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
3091
+ parallel: settleAll2,
3092
+ cancelled: /* @__PURE__ */ __name(async () => this.cancelled.has(runId), "cancelled"),
3093
+ runAgent: /* @__PURE__ */ __name((agentName, task) => this.runNested({
3094
+ agentName,
3095
+ task,
3096
+ actor,
3097
+ day,
3098
+ depth: 1,
3099
+ path: chainBelow,
3100
+ sinkRunId: runId,
3101
+ parentRunId: runId
3102
+ }), "runAgent"),
3103
+ startAgent: /* @__PURE__ */ __name(({ agentName, task, toolCallId: toolCallId2 }) => this.startDetached({
3104
+ agentName,
3105
+ task,
3106
+ actor,
3107
+ day,
3108
+ depth: 1,
3109
+ path: chainBelow,
3110
+ parentRunId: runId,
3111
+ deliverTo: {
3112
+ threadId,
3113
+ toolCallId: toolCallId2
3114
+ }
3115
+ }), "startAgent")
3116
+ };
3117
+ }
3118
+ /**
3119
+ * Delegate WITHOUT waiting: a nested loop nobody awaits, on its own thread and its own sink, which
3120
+ * posts its answer back into the delegating thread when it lands (`deliverTo`, settled by the
3121
+ * loop). The calling turn gets the run id straight back and finishes.
3122
+ *
3123
+ * No `sinkRunId`, unlike {@link runNested}: forwarding into the stream the human is watching would
3124
+ * write into a turn that has already ended. Its own approvals resolve through the shared pending
3125
+ * map keyed by its own runId — which is what the pending-approvals surface routes to.
3126
+ */
3127
+ async startDetached(args) {
3128
+ const { agentName, task, actor, day, depth, path, parentRunId, deliverTo } = args;
3129
+ const subThread = await this.store.createThread({
3130
+ actor,
3131
+ transient: true
3132
+ });
3133
+ const runId = crypto.randomUUID();
3134
+ await this.store.setActiveStream(subThread.id, runId);
3135
+ const deps = this.factory.forAgent(agentName);
3136
+ const hooks = {
3137
+ runId,
3138
+ openSink: /* @__PURE__ */ __name(() => deps.sink.open(runId), "openSink"),
3139
+ awaitApproval: /* @__PURE__ */ __name((call) => this.park(runId, call.id), "awaitApproval"),
3140
+ awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
2088
3141
  step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
2089
- runAgent: /* @__PURE__ */ __name((agentName, task) => this.runNested(agentName, task, actor, day, 1, runId), "runAgent")
3142
+ parallel: settleAll2,
3143
+ cancelled: /* @__PURE__ */ __name(async () => this.cancelled.has(runId), "cancelled"),
3144
+ runAgent: /* @__PURE__ */ __name((childName, childTask) => this.runNested({
3145
+ agentName: childName,
3146
+ task: childTask,
3147
+ actor,
3148
+ day,
3149
+ depth: depth + 1,
3150
+ path: [
3151
+ ...path,
3152
+ agentName
3153
+ ],
3154
+ // A detached run owns a stream of its own, so ITS sub-agents forward into that one.
3155
+ sinkRunId: runId,
3156
+ parentRunId: runId
3157
+ }), "runAgent"),
3158
+ startAgent: /* @__PURE__ */ __name(({ agentName: childName, task: childTask, toolCallId: toolCallId2 }) => this.startDetached({
3159
+ agentName: childName,
3160
+ task: childTask,
3161
+ actor,
3162
+ day,
3163
+ depth: depth + 1,
3164
+ path: [
3165
+ ...path,
3166
+ agentName
3167
+ ],
3168
+ parentRunId: runId,
3169
+ deliverTo: {
3170
+ threadId: subThread.id,
3171
+ toolCallId: toolCallId2
3172
+ }
3173
+ }), "startAgent")
3174
+ };
3175
+ void runAgentLoop2({
3176
+ ...deps,
3177
+ day
3178
+ }, {
3179
+ threadId: subThread.id,
3180
+ actor,
3181
+ userText: task,
3182
+ agentName,
3183
+ day,
3184
+ delegationDepth: depth,
3185
+ delegationPath: path,
3186
+ parentRunId,
3187
+ deliverTo
3188
+ }, hooks).catch(async (error) => {
3189
+ const cancelled = error instanceof RunCancelledError2;
3190
+ const message = error instanceof Error ? error.message : String(error);
3191
+ if (!cancelled) {
3192
+ this.logger.error(`detached agent run ${runId} failed: ${message}`);
3193
+ publishAgentRunFailed2({
3194
+ runId,
3195
+ code: agentFailureCode2(error),
3196
+ message
3197
+ });
3198
+ }
3199
+ await this.store.recordRunEnd?.({
3200
+ runId,
3201
+ status: cancelled ? "cancelled" : "failed",
3202
+ ...cancelled ? {} : {
3203
+ errorCode: agentFailureCode2(error),
3204
+ errorMessage: message
3205
+ }
3206
+ });
3207
+ await settleUnsettledDelegation2({
3208
+ store: this.store,
3209
+ delivery: deliverTo,
3210
+ agent: agentName,
3211
+ runId,
3212
+ status: cancelled ? "cancelled" : "failed",
3213
+ ...cancelled ? {} : {
3214
+ error: message
3215
+ }
3216
+ });
3217
+ }).finally(async () => {
3218
+ this.cancelled.delete(runId);
3219
+ await this.store.setActiveStream(subThread.id, null);
3220
+ });
3221
+ return {
3222
+ runId
2090
3223
  };
2091
3224
  }
2092
3225
  /**
@@ -2094,7 +3227,8 @@ var InlineAgentRunner = class _InlineAgentRunner {
2094
3227
  * (the top-level run the human is watching) so its output and any pending action tool are visible,
2095
3228
  * and its own approvals resolve through the shared pending map keyed by its own runId.
2096
3229
  */
2097
- async runNested(agentName, task, actor, day, depth, sinkRunId) {
3230
+ async runNested(args) {
3231
+ const { agentName, task, actor, day, depth, path, sinkRunId, parentRunId } = args;
2098
3232
  const subThread = await this.store.createThread({
2099
3233
  actor,
2100
3234
  transient: true
@@ -2106,11 +3240,42 @@ var InlineAgentRunner = class _InlineAgentRunner {
2106
3240
  runId,
2107
3241
  // Forward into the top-level stream; the top-level run owns end/fail on that shared sink.
2108
3242
  openSink: /* @__PURE__ */ __name(async () => childSinkWriter(await deps.sink.open(sinkRunId)), "openSink"),
2109
- awaitApproval: /* @__PURE__ */ __name((call) => new Promise((resolve) => {
2110
- this.pending.set(`${runId}:${call.id}`, resolve);
2111
- }), "awaitApproval"),
3243
+ awaitApproval: /* @__PURE__ */ __name((call) => this.park(runId, call.id), "awaitApproval"),
3244
+ awaitAnswers: /* @__PURE__ */ __name((request) => this.park(runId, request.id), "awaitAnswers"),
2112
3245
  step: /* @__PURE__ */ __name((_name, fn) => fn(), "step"),
2113
- runAgent: /* @__PURE__ */ __name((childName, childTask) => this.runNested(childName, childTask, actor, day, depth + 1, sinkRunId), "runAgent")
3246
+ parallel: settleAll2,
3247
+ // A child stops when it is cancelled OR when the run a human is actually watching is: the
3248
+ // canceller names the top-level run, which is the only id that ever left the server.
3249
+ cancelled: /* @__PURE__ */ __name(async () => this.cancelled.has(runId) || this.cancelled.has(sinkRunId), "cancelled"),
3250
+ runAgent: /* @__PURE__ */ __name((childName, childTask) => this.runNested({
3251
+ agentName: childName,
3252
+ task: childTask,
3253
+ actor,
3254
+ day,
3255
+ depth: depth + 1,
3256
+ path: [
3257
+ ...path,
3258
+ agentName
3259
+ ],
3260
+ sinkRunId,
3261
+ parentRunId: runId
3262
+ }), "runAgent"),
3263
+ startAgent: /* @__PURE__ */ __name(({ agentName: childName, task: childTask, toolCallId: toolCallId2 }) => this.startDetached({
3264
+ agentName: childName,
3265
+ task: childTask,
3266
+ actor,
3267
+ day,
3268
+ depth: depth + 1,
3269
+ path: [
3270
+ ...path,
3271
+ agentName
3272
+ ],
3273
+ parentRunId: runId,
3274
+ deliverTo: {
3275
+ threadId: subThread.id,
3276
+ toolCallId: toolCallId2
3277
+ }
3278
+ }), "startAgent")
2114
3279
  };
2115
3280
  try {
2116
3281
  return await runAgentLoop2({
@@ -2123,6 +3288,8 @@ var InlineAgentRunner = class _InlineAgentRunner {
2123
3288
  agentName,
2124
3289
  day,
2125
3290
  delegationDepth: depth,
3291
+ delegationPath: path,
3292
+ parentRunId,
2126
3293
  sinkRunId
2127
3294
  }, hooks);
2128
3295
  } finally {
@@ -2130,25 +3297,63 @@ var InlineAgentRunner = class _InlineAgentRunner {
2130
3297
  }
2131
3298
  }
2132
3299
  };
2133
- InlineAgentRunner = _ts_decorate16([
2134
- Injectable9(),
2135
- _ts_param13(0, Inject13(AGENT_DEPS_FACTORY5)),
2136
- _ts_param13(1, Inject13(AGENT_STORE4)),
2137
- _ts_metadata15("design:type", Function),
2138
- _ts_metadata15("design:paramtypes", [
3300
+ InlineAgentRunner = _ts_decorate19([
3301
+ Injectable10(),
3302
+ _ts_param17(0, Inject17(AGENT_DEPS_FACTORY5)),
3303
+ _ts_param17(1, Inject17(AGENT_STORE5)),
3304
+ _ts_metadata18("design:type", Function),
3305
+ _ts_metadata18("design:paramtypes", [
2139
3306
  typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory,
2140
3307
  typeof AgentStore === "undefined" ? Object : AgentStore
2141
3308
  ])
2142
3309
  ], InlineAgentRunner);
2143
3310
 
3311
+ // src/skills-config.ts
3312
+ import { compositeSkillProvider } from "@dudousxd/nestjs-agent-core";
3313
+ function declaredSkillProvider(declared) {
3314
+ return {
3315
+ list: /* @__PURE__ */ __name(({ scopes }) => declared.filter((skill) => scopes.includes(skill.scope)).map(({ name, description, scope }) => ({
3316
+ name,
3317
+ description,
3318
+ scope
3319
+ })), "list"),
3320
+ load: /* @__PURE__ */ __name(async ({ name, scope, ctx }) => {
3321
+ const match = declared.find((skill) => skill.name === name && skill.scope === scope);
3322
+ return match === void 0 ? null : await match.body(ctx);
3323
+ }, "load")
3324
+ };
3325
+ }
3326
+ __name(declaredSkillProvider, "declaredSkillProvider");
3327
+ function resolveSkillsConfig(options, declared) {
3328
+ if (options === void 0) {
3329
+ return void 0;
3330
+ }
3331
+ const providers = options.provider === void 0 ? [
3332
+ declaredSkillProvider(declared)
3333
+ ] : [
3334
+ options.provider,
3335
+ declaredSkillProvider(declared)
3336
+ ];
3337
+ return {
3338
+ provider: providers.length === 1 ? providers[0] : compositeSkillProvider(providers),
3339
+ ...options.scopes !== void 0 ? {
3340
+ scopes: options.scopes
3341
+ } : {},
3342
+ ...options.maxSkills !== void 0 ? {
3343
+ maxSkills: options.maxSkills
3344
+ } : {}
3345
+ };
3346
+ }
3347
+ __name(resolveSkillsConfig, "resolveSkillsConfig");
3348
+
2144
3349
  // src/agent.module.ts
2145
- function _ts_decorate17(decorators, target, key, desc) {
3350
+ function _ts_decorate20(decorators, target, key, desc) {
2146
3351
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2147
3352
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2148
3353
  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;
2149
3354
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2150
3355
  }
2151
- __name(_ts_decorate17, "_ts_decorate");
3356
+ __name(_ts_decorate20, "_ts_decorate");
2152
3357
  var DEFAULT_PATH = "agent";
2153
3358
  function sharedProviders(durable, includeStore) {
2154
3359
  const providers = [
@@ -2166,8 +3371,34 @@ function sharedProviders(durable, includeStore) {
2166
3371
  provide: AGENT_PROMPT_CONTRIBUTORS3,
2167
3372
  useFactory: /* @__PURE__ */ __name(() => [], "useFactory")
2168
3373
  },
3374
+ // Likewise for `@Skill`-decorated providers — filled by SkillDiscoveryService at onModuleInit,
3375
+ // read lazily by the AGENT_SKILLS provider below (which DI builds before discovery has run).
3376
+ {
3377
+ provide: AGENT_SKILL_SOURCES2,
3378
+ useFactory: /* @__PURE__ */ __name(() => [], "useFactory")
3379
+ },
3380
+ {
3381
+ // One resolution, shared by the loop's deps and the listing endpoint: a second one would
3382
+ // drift, and a user would be offered a skill the agent cannot reach. `undefined` when the
3383
+ // host configured none — which is what keeps the turn's checkpoint sequence unchanged.
3384
+ provide: AGENT_SKILLS3,
3385
+ useFactory: /* @__PURE__ */ __name((options, declared) => resolveSkillsConfig(options.skills, declared), "useFactory"),
3386
+ inject: [
3387
+ AGENT_OPTIONS5,
3388
+ AGENT_SKILL_SOURCES2
3389
+ ]
3390
+ },
3391
+ {
3392
+ // One resolution, shared by the loop's deps and the read-back endpoint: a person has to be
3393
+ // shown what the model was shown, and two resolutions would eventually disagree about that.
3394
+ provide: AGENT_MEMORY3,
3395
+ useFactory: /* @__PURE__ */ __name((options) => options.memory, "useFactory"),
3396
+ inject: [
3397
+ AGENT_OPTIONS5
3398
+ ]
3399
+ },
2169
3400
  {
2170
- provide: AGENT_SINK3,
3401
+ provide: AGENT_SINK4,
2171
3402
  useFactory: /* @__PURE__ */ __name((options) => options.sink ?? new InProcessTokenStreamSink(), "useFactory"),
2172
3403
  inject: [
2173
3404
  AGENT_OPTIONS5
@@ -2181,7 +3412,7 @@ function sharedProviders(durable, includeStore) {
2181
3412
  ]
2182
3413
  },
2183
3414
  {
2184
- provide: AGENT_ACTOR_RESOLVER6,
3415
+ provide: AGENT_ACTOR_RESOLVER8,
2185
3416
  useFactory: /* @__PURE__ */ __name((options) => options.actorResolver, "useFactory"),
2186
3417
  inject: [
2187
3418
  AGENT_OPTIONS5
@@ -2202,7 +3433,7 @@ function sharedProviders(durable, includeStore) {
2202
3433
  useFactory: /* @__PURE__ */ __name((o, store) => o.quota ?? (o.quotaLimitTokens !== void 0 ? new LedgerQuotaStore(store, o.quotaLimitTokens) : void 0), "useFactory"),
2203
3434
  inject: [
2204
3435
  AGENT_OPTIONS5,
2205
- AGENT_STORE5
3436
+ AGENT_STORE6
2206
3437
  ]
2207
3438
  },
2208
3439
  AgentDepsFactory2,
@@ -2214,6 +3445,7 @@ function sharedProviders(durable, includeStore) {
2214
3445
  // handoff tools (onApplicationBootstrap) reads the registry.
2215
3446
  AgentDiscoveryService,
2216
3447
  AiToolDiscoveryService,
3448
+ SkillDiscoveryService,
2217
3449
  InlineAgentRunner,
2218
3450
  AgentService,
2219
3451
  // Bound ALWAYS (durable or inline) — the console's cross-thread approvals inbox routes decisions
@@ -2226,7 +3458,7 @@ function sharedProviders(durable, includeStore) {
2226
3458
  ];
2227
3459
  if (includeStore) {
2228
3460
  providers.push({
2229
- provide: AGENT_STORE5,
3461
+ provide: AGENT_STORE6,
2230
3462
  useFactory: /* @__PURE__ */ __name((o) => o.store, "useFactory"),
2231
3463
  inject: [
2232
3464
  AGENT_OPTIONS5
@@ -2263,15 +3495,17 @@ function exportsFor(includeStore) {
2263
3495
  AGENT_OPTIONS5,
2264
3496
  AGENT_TOOL_REGISTRY3,
2265
3497
  AGENT_REGISTRY5,
2266
- AGENT_SINK3,
3498
+ AGENT_SINK4,
2267
3499
  AGENT_ROLES_POLICY2,
2268
- AGENT_ACTOR_RESOLVER6,
3500
+ AGENT_ACTOR_RESOLVER8,
2269
3501
  AGENT_MODEL2,
2270
3502
  ...includeStore ? [
2271
- AGENT_STORE5
3503
+ AGENT_STORE6
2272
3504
  ] : [],
2273
3505
  AGENT_QUOTA_STORE3,
2274
3506
  AGENT_PROMPT_CONTRIBUTORS3,
3507
+ AGENT_SKILLS3,
3508
+ AGENT_MEMORY3,
2275
3509
  AGENT_DEPS_FACTORY6,
2276
3510
  AgentDepsFactory2,
2277
3511
  AgentService,
@@ -2285,7 +3519,9 @@ var BASE_CONTROLLERS = [
2285
3519
  ThreadsController,
2286
3520
  ToolCallController,
2287
3521
  QuotaController,
2288
- AgentsController
3522
+ AgentsController,
3523
+ SkillsController,
3524
+ MemoriesController
2289
3525
  ];
2290
3526
  var GUARDABLE_CONTROLLERS = [
2291
3527
  ...BASE_CONTROLLERS,
@@ -2400,7 +3636,7 @@ var AgentModule = class _AgentModule {
2400
3636
  };
2401
3637
  }
2402
3638
  };
2403
- AgentModule = _ts_decorate17([
3639
+ AgentModule = _ts_decorate20([
2404
3640
  Global2(),
2405
3641
  Module2({})
2406
3642
  ], AgentModule);