@dudousxd/nestjs-agent 1.19.3 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,14 +2,14 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // src/agent.module.ts
5
- import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER13, AGENT_APPROVAL_PORT, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY5, AGENT_DURABLE_RUNNER, AGENT_MEMORY as AGENT_MEMORY3, AGENT_MODEL as AGENT_MODEL2, AGENT_MODEL_CATALOG as AGENT_MODEL_CATALOG3, AGENT_OPTIONS as AGENT_OPTIONS7, AGENT_PROMPT_CONTRIBUTORS as AGENT_PROMPT_CONTRIBUTORS3, AGENT_QUOTA_PROVIDER as AGENT_QUOTA_PROVIDER3, AGENT_REGISTRY as AGENT_REGISTRY6, AGENT_ROLES_POLICY as AGENT_ROLES_POLICY2, AGENT_RUNNER as AGENT_RUNNER2, AGENT_SINK as AGENT_SINK3, AGENT_SKILLS as AGENT_SKILLS3, AGENT_SKILL_SOURCES as AGENT_SKILL_SOURCES2, AGENT_STORE as AGENT_STORE5, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY3, AgentRegistry as AgentRegistry6, ClosedRolesPolicy, DefaultRolesPolicy, InMemoryAgentStore, ToolRegistry as ToolRegistry3 } from "@dudousxd/nestjs-agent-core";
5
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER13, AGENT_APPROVAL_PORT, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY6, AGENT_DURABLE_RUNNER, AGENT_MEMORY as AGENT_MEMORY3, AGENT_MODEL as AGENT_MODEL2, AGENT_MODEL_CATALOG as AGENT_MODEL_CATALOG3, AGENT_OPTIONS as AGENT_OPTIONS7, AGENT_PROMPT_CONTRIBUTORS as AGENT_PROMPT_CONTRIBUTORS3, AGENT_QUOTA_PROVIDER as AGENT_QUOTA_PROVIDER3, AGENT_REGISTRY as AGENT_REGISTRY6, AGENT_ROLES_POLICY as AGENT_ROLES_POLICY2, AGENT_RUNNER as AGENT_RUNNER2, AGENT_SINK as AGENT_SINK3, AGENT_SKILLS as AGENT_SKILLS3, AGENT_SKILL_SOURCES as AGENT_SKILL_SOURCES2, AGENT_STORE as AGENT_STORE5, AGENT_TOOL_REGISTRY as AGENT_TOOL_REGISTRY3, AgentRegistry as AgentRegistry6, ClosedRolesPolicy, DefaultRolesPolicy, InMemoryAgentStore, ToolRegistry as ToolRegistry3 } from "@dudousxd/nestjs-agent-core";
6
6
  import { Global, Logger as Logger6, Module } from "@nestjs/common";
7
7
  import { DiscoveryModule, ModulesContainer, RouterModule } from "@nestjs/core";
8
8
 
9
9
  // src/agent-deps.factory.ts
10
- import { AGENT_MEMORY, AGENT_MODEL, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_REGISTRY, AGENT_ROLES_POLICY, AGENT_SINK, AGENT_SKILLS, AGENT_STORE, AGENT_TOOL_REGISTRY, AgentRegistry as AgentRegistry2, ToolRegistry as ToolRegistry2, normalizeDelegation, summarizeWithModel, windowHistory } from "@dudousxd/nestjs-agent-core";
10
+ import { AGENT_MEMORY, AGENT_MODEL, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_REGISTRY, AGENT_ROLES_POLICY, AGENT_SINK, AGENT_SKILLS, AGENT_STORE, AGENT_TOOL_REGISTRY, AgentRegistry as AgentRegistry2, ToolRegistry as ToolRegistry2, findPersona, intersectAllowLists, normalizeDelegation, personaCatalogEntry, resolvePersonaAlias, summarizeWithModel, windowHistory } from "@dudousxd/nestjs-agent-core";
11
11
  import { AGENT_OPTIONS } from "@dudousxd/nestjs-agent-core";
12
- import { Inject, Injectable, Optional } from "@nestjs/common";
12
+ import { BadRequestException, Inject, Injectable, Optional } from "@nestjs/common";
13
13
  function _ts_decorate(decorators, target, key, desc) {
14
14
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
15
15
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -107,8 +107,64 @@ var AgentDepsFactory2 = class {
107
107
  ...delegated
108
108
  ];
109
109
  }
110
+ /**
111
+ * The agent a name stands for: itself when it is a registered agent (or unknown — the bare
112
+ * assistant), else the agent whose persona took the name over (`Persona.aliases`) and that persona.
113
+ */
114
+ resolveAgent(name) {
115
+ if (this.agents.has(name)) {
116
+ return {
117
+ agentName: name
118
+ };
119
+ }
120
+ const alias = resolvePersonaAlias(this.agents.list(), name);
121
+ return alias === void 0 ? {
122
+ agentName: name
123
+ } : {
124
+ agentName: alias.agent,
125
+ persona: alias.persona
126
+ };
127
+ }
128
+ /** `agentName`'s personas as a picker reads them. Empty when it declares none. */
129
+ personaCatalog(agentName) {
130
+ return (this.agents.get(agentName)?.personas ?? []).map(personaCatalogEntry);
131
+ }
132
+ /** The persona `agentName` runs under when nothing names one. Undefined → none. */
133
+ defaultPersona(agentName) {
134
+ return this.agents.get(agentName)?.defaultPersona;
135
+ }
136
+ /**
137
+ * The persona a turn of `agentName` runs under, most specific first: the one the send names
138
+ * (refused with `400 persona_not_found` when the agent does not declare it), else the thread's
139
+ * pinned one when this agent declares it, else the agent's default. Undefined → none.
140
+ */
141
+ resolvePersona(args) {
142
+ const definition = this.agents.get(args.agentName);
143
+ if (args.requested !== void 0) {
144
+ if (findPersona(definition, args.requested) === void 0) {
145
+ throw new BadRequestException({
146
+ statusCode: 400,
147
+ code: "persona_not_found",
148
+ message: `agent "${args.agentName}" has no persona "${args.requested}"`
149
+ });
150
+ }
151
+ return args.requested;
152
+ }
153
+ const pinned = args.threadPersona ?? void 0;
154
+ if (pinned !== void 0 && findPersona(definition, pinned) !== void 0) {
155
+ return pinned;
156
+ }
157
+ return definition?.defaultPersona;
158
+ }
110
159
  forAgent(agentName) {
111
160
  const name = agentName ?? this.defaultAgentName();
161
+ if (agentName !== void 0 && !this.agents.has(agentName)) {
162
+ const alias = resolvePersonaAlias(this.agents.list(), agentName);
163
+ const persona = alias === void 0 ? void 0 : findPersona(this.agents.get(alias.agent), alias.persona);
164
+ if (alias !== void 0 && persona !== void 0) {
165
+ return this.withPersona(this.forAgent(alias.agent), persona);
166
+ }
167
+ }
112
168
  const definition = this.agents.get(name);
113
169
  const toolAllowList = this.effectiveTools(definition);
114
170
  const followUpsCount = this.followUpsCount();
@@ -173,6 +229,9 @@ var AgentDepsFactory2 = class {
173
229
  ...toolAllowList !== void 0 ? {
174
230
  toolAllowList
175
231
  } : {},
232
+ ...definition?.personas !== void 0 ? {
233
+ personas: definition.personas
234
+ } : {},
176
235
  ...this.options.toolTimeoutMs !== void 0 ? {
177
236
  toolTimeoutMs: this.options.toolTimeoutMs
178
237
  } : {},
@@ -191,6 +250,45 @@ var AgentDepsFactory2 = class {
191
250
  };
192
251
  }
193
252
  /**
253
+ * A persona baked into an agent's deps — how a run journaled under an agent name that a persona
254
+ * has since taken over (`Persona.aliases`) is served. That run names no persona of its own (it
255
+ * predates them, and its input cannot change), so the persona applies the way the old agent's own
256
+ * config did: as config, re-read on every replay, with no checkpoint of its own.
257
+ */
258
+ withPersona(deps, persona) {
259
+ const base = deps.systemPrompt;
260
+ const ref = {
261
+ id: persona.id,
262
+ label: persona.label
263
+ };
264
+ const resolve = /* @__PURE__ */ __name(async (prompt, ctx) => typeof prompt === "function" ? prompt(ctx) : prompt, "resolve");
265
+ const own = persona.systemPrompt;
266
+ const systemPrompt = /* @__PURE__ */ __name(async (ctx) => {
267
+ const scoped = {
268
+ ...ctx,
269
+ persona: ref
270
+ };
271
+ if (own === void 0) {
272
+ return resolve(base, scoped);
273
+ }
274
+ const basePrompt = typeof own === "function" ? await resolve(base, scoped) : void 0;
275
+ return resolve(own, {
276
+ ...scoped,
277
+ ...basePrompt !== void 0 ? {
278
+ basePrompt
279
+ } : {}
280
+ });
281
+ }, "systemPrompt");
282
+ const toolAllowList = intersectAllowLists(deps.toolAllowList, persona.allowedTools);
283
+ return {
284
+ ...deps,
285
+ systemPrompt,
286
+ ...toolAllowList !== void 0 ? {
287
+ toolAllowList
288
+ } : {}
289
+ };
290
+ }
291
+ /**
194
292
  * The ceiling on how much of a thread this agent's turns carry, most specific wins: the agent's
195
293
  * own `@Agent({ history })`, else a module-wide custom `historyPolicy`, else the module-wide
196
294
  * `history` window. Undefined → unbounded, the behaviour of a module that configures none.
@@ -265,8 +363,8 @@ AgentDepsFactory2 = _ts_decorate([
265
363
  ], AgentDepsFactory2);
266
364
 
267
365
  // src/agent.service.ts
268
- import { AGENT_ATTACHMENT_STAGING as AGENT_ATTACHMENT_STAGING2, AGENT_DEPS_FACTORY, AGENT_MODEL_CATALOG, AGENT_OPTIONS as AGENT_OPTIONS3, AGENT_QUOTA_PROVIDER as AGENT_QUOTA_PROVIDER2, AGENT_RUNNER, AGENT_STORE as AGENT_STORE3, REQUESTER_APPROVER, findCatalogModel, mayDecideApproval, readElicitationQuestions, settleDeadRun, validateElicitationAnswer } from "@dudousxd/nestjs-agent-core";
269
- import { BadRequestException, ConflictException as ConflictException2, ForbiddenException, GoneException, HttpException, HttpStatus, Inject as Inject3, Injectable as Injectable3, NotFoundException, NotImplementedException as NotImplementedException2, Optional as Optional3 } from "@nestjs/common";
366
+ import { AGENT_ATTACHMENT_STAGING as AGENT_ATTACHMENT_STAGING2, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY2, AGENT_MODEL_CATALOG, AGENT_OPTIONS as AGENT_OPTIONS3, AGENT_QUOTA_PROVIDER as AGENT_QUOTA_PROVIDER2, AGENT_RUNNER, AGENT_STORE as AGENT_STORE3, REQUESTER_APPROVER, findCatalogModel, mayDecideApproval, readElicitationQuestions, settleDeadRun, validateElicitationAnswer } from "@dudousxd/nestjs-agent-core";
367
+ import { BadRequestException as BadRequestException2, ConflictException as ConflictException2, ForbiddenException, GoneException, HttpException, HttpStatus, Inject as Inject3, Injectable as Injectable3, NotFoundException, NotImplementedException as NotImplementedException2, Optional as Optional3 } from "@nestjs/common";
270
368
 
271
369
  // src/agent-deps.ts
272
370
  function utcDay(date = /* @__PURE__ */ new Date()) {
@@ -291,8 +389,20 @@ function childSinkWriter(writer) {
291
389
  __name(childSinkWriter, "childSinkWriter");
292
390
 
293
391
  // src/queue/chat-queue.service.ts
294
- import { AGENT_ATTACHMENT_STAGING, AGENT_OPTIONS as AGENT_OPTIONS2, AGENT_QUOTA_PROVIDER, AGENT_SINK as AGENT_SINK2, AGENT_STORE as AGENT_STORE2, encodeStreamEvent, isChatQueueStore, queuedMessageView } from "@dudousxd/nestjs-agent-core";
392
+ import { AGENT_ATTACHMENT_STAGING, AGENT_DEPS_FACTORY, AGENT_OPTIONS as AGENT_OPTIONS2, AGENT_QUOTA_PROVIDER, AGENT_SINK as AGENT_SINK2, AGENT_STORE as AGENT_STORE2, encodeStreamEvent, isChatQueueStore, queuedMessageView } from "@dudousxd/nestjs-agent-core";
295
393
  import { Inject as Inject2, Injectable as Injectable2, Logger, NotImplementedException, Optional as Optional2 } from "@nestjs/common";
394
+
395
+ // src/thread-persona.ts
396
+ async function threadPersona(store, threadId) {
397
+ const projecting = store;
398
+ if (typeof projecting.personaForThread === "function") {
399
+ return projecting.personaForThread(threadId);
400
+ }
401
+ return (await store.getThread(threadId))?.persona ?? null;
402
+ }
403
+ __name(threadPersona, "threadPersona");
404
+
405
+ // src/queue/chat-queue.service.ts
296
406
  function _ts_decorate2(decorators, target, key, desc) {
297
407
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
298
408
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -329,18 +439,20 @@ var ChatQueueService = class _ChatQueueService {
329
439
  quotaProvider;
330
440
  options;
331
441
  staging;
442
+ deps;
332
443
  logger = new Logger(_ChatQueueService.name);
333
444
  /**
334
445
  * Runs this process claimed a thread for and has not handed to a runner yet. A holder in here is
335
446
  * alive whatever the runner says — it simply has not registered the run yet.
336
447
  */
337
448
  starting = /* @__PURE__ */ new Set();
338
- constructor(store, sink, quotaProvider, options, staging) {
449
+ constructor(store, sink, quotaProvider, options, staging, deps) {
339
450
  this.store = store;
340
451
  this.sink = sink;
341
452
  this.quotaProvider = quotaProvider;
342
453
  this.options = options;
343
454
  this.staging = staging;
455
+ this.deps = deps;
344
456
  }
345
457
  /** Whether the bound store can hold a queue. Without one, a busy thread is not queued. */
346
458
  get supported() {
@@ -638,13 +750,17 @@ var ChatQueueService = class _ChatQueueService {
638
750
  */
639
751
  async inputFor(message) {
640
752
  const attachments = await this.freshAttachments(message.actor, message.attachments ?? []);
753
+ const { agentName, persona } = await this.targetFor(message);
641
754
  return {
642
755
  threadId: message.threadId,
643
756
  actor: message.actor,
644
757
  userText: message.content,
645
758
  day: utcDay(),
646
- ...message.agentName !== void 0 ? {
647
- agentName: message.agentName
759
+ ...agentName !== void 0 ? {
760
+ agentName
761
+ } : {},
762
+ ...persona !== void 0 ? {
763
+ persona
648
764
  } : {},
649
765
  ...attachments.length > 0 ? {
650
766
  attachments
@@ -657,6 +773,37 @@ var ChatQueueService = class _ChatQueueService {
657
773
  } : {}
658
774
  };
659
775
  }
776
+ /**
777
+ * The agent and persona a queued message starts as. A message carries the persona its send
778
+ * resolved; one that carries none — queued before personas existed, or by a host that sets none —
779
+ * resolves it now, exactly as a send would: an agent name a persona took over, then the thread's
780
+ * pinned persona, then the agent's default. Never refused here: nobody is waiting on a 400.
781
+ */
782
+ async targetFor(message) {
783
+ const deps = this.deps;
784
+ if (message.persona !== void 0 || deps === void 0 || message.agentName === void 0) {
785
+ return {
786
+ ...message.agentName !== void 0 ? {
787
+ agentName: message.agentName
788
+ } : {},
789
+ ...message.persona !== void 0 ? {
790
+ persona: message.persona
791
+ } : {}
792
+ };
793
+ }
794
+ const target = deps.resolveAgent(message.agentName);
795
+ const hasPersonas = deps.personaCatalog(target.agentName).length > 0;
796
+ const persona = target.persona ?? (hasPersonas ? deps.resolvePersona({
797
+ agentName: target.agentName,
798
+ threadPersona: await threadPersona(this.store, message.threadId)
799
+ }) : void 0);
800
+ return {
801
+ agentName: target.agentName,
802
+ ...persona !== void 0 ? {
803
+ persona
804
+ } : {}
805
+ };
806
+ }
660
807
  async freshAttachments(actor, attachments) {
661
808
  const staging = this.staging;
662
809
  if (attachments.length === 0 || staging === void 0 || typeof staging.resolve !== "function") {
@@ -684,13 +831,16 @@ ChatQueueService = _ts_decorate2([
684
831
  _ts_param2(3, Inject2(AGENT_OPTIONS2)),
685
832
  _ts_param2(4, Optional2()),
686
833
  _ts_param2(4, Inject2(AGENT_ATTACHMENT_STAGING)),
834
+ _ts_param2(5, Optional2()),
835
+ _ts_param2(5, Inject2(AGENT_DEPS_FACTORY)),
687
836
  _ts_metadata2("design:type", Function),
688
837
  _ts_metadata2("design:paramtypes", [
689
838
  typeof AgentStore === "undefined" ? Object : AgentStore,
690
839
  typeof TokenStreamSink === "undefined" ? Object : TokenStreamSink,
691
840
  typeof QuotaProvider === "undefined" ? Object : QuotaProvider,
692
841
  typeof Pick === "undefined" ? Object : Pick,
693
- typeof AttachmentStagingStore === "undefined" ? Object : AttachmentStagingStore
842
+ typeof AttachmentStagingStore === "undefined" ? Object : AttachmentStagingStore,
843
+ typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory
694
844
  ])
695
845
  ], ChatQueueService);
696
846
  function pauseReasonFor(outcome, head) {
@@ -862,17 +1012,17 @@ var AgentService = class {
862
1012
  */
863
1013
  async assertModelAllowed(actor, agent, model) {
864
1014
  if (this.models === void 0) {
865
- throw new BadRequestException(`model "${model}" cannot be selected: no ModelCatalog is configured (AgentModule.forRoot({ models }))`);
1015
+ throw new BadRequestException2(`model "${model}" cannot be selected: no ModelCatalog is configured (AgentModule.forRoot({ models }))`);
866
1016
  }
867
1017
  const entry = findCatalogModel(await this.models.list({
868
1018
  actor,
869
1019
  agent
870
1020
  }), model);
871
1021
  if (entry === void 0) {
872
- throw new BadRequestException(`model "${model}" is not offered`);
1022
+ throw new BadRequestException2(`model "${model}" is not offered`);
873
1023
  }
874
1024
  if (!entry.available) {
875
- throw new BadRequestException(`model "${model}" is not available${entry.unavailableReason ? `: ${entry.unavailableReason}` : ""}`);
1025
+ throw new BadRequestException2(`model "${model}" is not available${entry.unavailableReason ? `: ${entry.unavailableReason}` : ""}`);
876
1026
  }
877
1027
  return entry.id;
878
1028
  }
@@ -888,7 +1038,7 @@ var AgentService = class {
888
1038
  })).locked : void 0;
889
1039
  if (locked !== void 0) {
890
1040
  if (requested !== void 0 && requested !== locked.model) {
891
- throw new BadRequestException(`model "${requested}" cannot be selected: ${locked.reason ?? `this agent always uses "${locked.model}"`}`);
1041
+ throw new BadRequestException2(`model "${requested}" cannot be selected: ${locked.reason ?? `this agent always uses "${locked.model}"`}`);
892
1042
  }
893
1043
  return locked.model;
894
1044
  }
@@ -944,13 +1094,15 @@ var AgentService = class {
944
1094
  */
945
1095
  async send(params) {
946
1096
  await this.assertWithinQuota(params.actor);
947
- const agentName = await this.resolveAgentName(params.agentName, params.threadId);
1097
+ const target = this.deps.resolveAgent(await this.resolveAgentName(params.agentName, params.threadId));
1098
+ const agentName = target.agentName;
1099
+ const persona = await this.resolvePersona(agentName, params.personaId ?? target.persona, params.threadId);
948
1100
  const model = await this.resolveModel(params.actor, agentName, params.model, params.threadId);
949
1101
  const attachments = await this.resolveAttachments(params.actor, params.attachments ?? []);
950
1102
  let threadId = params.threadId;
951
1103
  if (threadId === void 0) {
952
1104
  if (params.regenerate === true) {
953
- throw new BadRequestException("regenerate requires an existing threadId");
1105
+ throw new BadRequestException2("regenerate requires an existing threadId");
954
1106
  }
955
1107
  const created = await this.store.createThread({
956
1108
  actor: params.actor,
@@ -959,11 +1111,17 @@ var AgentService = class {
959
1111
  } : {},
960
1112
  ...params.newThreadId !== void 0 ? {
961
1113
  id: params.newThreadId
1114
+ } : {},
1115
+ ...params.personaId !== void 0 ? {
1116
+ persona: params.personaId
962
1117
  } : {}
963
1118
  });
964
1119
  threadId = created.id;
965
1120
  } else {
966
1121
  await this.assertOwnsThread(params.actor, threadId);
1122
+ if (params.personaId !== void 0) {
1123
+ await this.pinThreadPersona(threadId, params.personaId);
1124
+ }
967
1125
  }
968
1126
  const input = {
969
1127
  threadId,
@@ -971,6 +1129,9 @@ var AgentService = class {
971
1129
  userText: params.message,
972
1130
  day: utcDay(),
973
1131
  agentName,
1132
+ ...persona !== void 0 ? {
1133
+ persona
1134
+ } : {},
974
1135
  ...params.regenerate === true ? {
975
1136
  regenerate: true
976
1137
  } : {},
@@ -1057,6 +1218,9 @@ var AgentService = class {
1057
1218
  ...input.agentName !== void 0 ? {
1058
1219
  agentName: input.agentName
1059
1220
  } : {},
1221
+ ...input.persona !== void 0 ? {
1222
+ persona: input.persona
1223
+ } : {},
1060
1224
  ...input.model !== void 0 ? {
1061
1225
  model: input.model
1062
1226
  } : {},
@@ -1109,7 +1273,7 @@ var AgentService = class {
1109
1273
  const store = queue.queueStore();
1110
1274
  const threadId = await this.assertOwnsQueuedMessage(actor, messageId);
1111
1275
  if (patch.message !== void 0 && patch.message.trim().length === 0) {
1112
- throw new BadRequestException("message must not be empty");
1276
+ throw new BadRequestException2("message must not be empty");
1113
1277
  }
1114
1278
  const attachments = patch.attachments === void 0 || patch.attachments === null ? patch.attachments : await this.resolveAttachments(actor, patch.attachments);
1115
1279
  if (patch.message !== void 0 || attachments !== void 0) {
@@ -1127,7 +1291,7 @@ var AgentService = class {
1127
1291
  }
1128
1292
  if (patch.position !== void 0) {
1129
1293
  if (!Number.isInteger(patch.position) || patch.position < 0) {
1130
- throw new BadRequestException("position must be a non-negative integer");
1294
+ throw new BadRequestException2("position must be a non-negative integer");
1131
1295
  }
1132
1296
  if (!await store.moveQueuedMessage(messageId, patch.position)) {
1133
1297
  throw new GoneException(`queued message ${messageId} already started or was removed`);
@@ -1451,16 +1615,22 @@ var AgentService = class {
1451
1615
  async updateThread(actor, threadId, patch) {
1452
1616
  const title = patch.title !== void 0 ? this.validateTitle(patch.title) : void 0;
1453
1617
  await this.assertOwnsThread(actor, threadId);
1454
- if (patch.defaultAgent === void 0 && patch.model === void 0) {
1618
+ if (patch.defaultAgent === void 0 && patch.model === void 0 && patch.persona === void 0) {
1455
1619
  if (title !== void 0) {
1456
1620
  await this.store.setTitle(threadId, title);
1457
1621
  }
1458
1622
  return;
1459
1623
  }
1460
1624
  if (this.store.updateThread === void 0) {
1461
- throw new NotImplementedException2("Setting a thread's defaultAgent or model requires an AgentStore that implements updateThread(); the bound store does not support it.");
1625
+ throw new NotImplementedException2("Setting a thread's defaultAgent, model or persona requires an AgentStore that implements updateThread(); the bound store does not support it.");
1462
1626
  }
1463
1627
  const model = patch.model === void 0 || patch.model === null ? patch.model : await this.assertModelAllowed(actor, patch.defaultAgent ?? await this.resolveAgentName(void 0, threadId), patch.model);
1628
+ if (patch.persona !== void 0 && patch.persona !== null) {
1629
+ this.deps.resolvePersona({
1630
+ agentName: this.deps.resolveAgent(patch.defaultAgent ?? await this.resolveAgentName(void 0, threadId)).agentName,
1631
+ requested: patch.persona
1632
+ });
1633
+ }
1464
1634
  await this.store.updateThread(threadId, {
1465
1635
  ...title !== void 0 ? {
1466
1636
  title
@@ -1470,16 +1640,46 @@ var AgentService = class {
1470
1640
  } : {},
1471
1641
  ...model !== void 0 ? {
1472
1642
  model
1643
+ } : {},
1644
+ ...patch.persona !== void 0 ? {
1645
+ persona: patch.persona
1473
1646
  } : {}
1474
1647
  });
1475
1648
  }
1649
+ /**
1650
+ * The persona this send runs under — see {@link AgentDepsFactory.resolvePersona}. The thread's
1651
+ * pinned persona is read only when the send names none AND the agent has personas to pick from.
1652
+ */
1653
+ async resolvePersona(agentName, requested, threadId) {
1654
+ const needsThread = requested === void 0 && threadId !== void 0 && this.deps.personaCatalog(agentName).length > 0;
1655
+ return this.deps.resolvePersona({
1656
+ agentName,
1657
+ ...requested !== void 0 ? {
1658
+ requested
1659
+ } : {},
1660
+ ...needsThread ? {
1661
+ threadPersona: await threadPersona(this.store, threadId)
1662
+ } : {}
1663
+ });
1664
+ }
1665
+ /** Pin `persona` on the thread when it is not already — a no-op on a store that cannot. */
1666
+ async pinThreadPersona(threadId, persona) {
1667
+ if (this.store.updateThread === void 0) {
1668
+ return;
1669
+ }
1670
+ if (await threadPersona(this.store, threadId) !== persona) {
1671
+ await this.store.updateThread(threadId, {
1672
+ persona
1673
+ });
1674
+ }
1675
+ }
1476
1676
  validateTitle(title) {
1477
1677
  const trimmed = title.trim();
1478
1678
  if (trimmed.length === 0) {
1479
- throw new BadRequestException("title must not be empty");
1679
+ throw new BadRequestException2("title must not be empty");
1480
1680
  }
1481
1681
  if (trimmed.length > 200) {
1482
- throw new BadRequestException("title must be at most 200 characters");
1682
+ throw new BadRequestException2("title must be at most 200 characters");
1483
1683
  }
1484
1684
  return trimmed;
1485
1685
  }
@@ -1518,6 +1718,7 @@ var AgentService = class {
1518
1718
  ...thread,
1519
1719
  defaultAgent: thread.defaultAgent ?? null,
1520
1720
  model: thread.model ?? null,
1721
+ persona: thread.persona ?? null,
1521
1722
  activeRunId: await this.store.activeRunForThread?.(thread.id) ?? thread.activeRunId ?? null
1522
1723
  };
1523
1724
  }
@@ -1582,14 +1783,14 @@ var AgentService = class {
1582
1783
  */
1583
1784
  async setMessageFeedback(actor, messageId, input) {
1584
1785
  if (input.value !== null && input.value !== "up" && input.value !== "down") {
1585
- throw new BadRequestException("value must be 'up', 'down' or null");
1786
+ throw new BadRequestException2("value must be 'up', 'down' or null");
1586
1787
  }
1587
1788
  if (input.comment !== void 0 && typeof input.comment !== "string") {
1588
- throw new BadRequestException("comment must be a string");
1789
+ throw new BadRequestException2("comment must be a string");
1589
1790
  }
1590
1791
  const comment = input.comment?.trim();
1591
1792
  if (comment !== void 0 && comment.length > 2e3) {
1592
- throw new BadRequestException("comment must be at most 2000 characters");
1793
+ throw new BadRequestException2("comment must be at most 2000 characters");
1593
1794
  }
1594
1795
  if (this.store.threadOfMessage === void 0 || this.store.setMessageFeedback === void 0) {
1595
1796
  throw new NotImplementedException2("Message feedback requires an AgentStore that implements threadOfMessage() and setMessageFeedback(); the bound store does not support it.");
@@ -1714,7 +1915,7 @@ var AgentService = class {
1714
1915
  const submitted = Object.hasOwn(answers2, question.id) ? answers2[question.id] : void 0;
1715
1916
  const problem = validateElicitationAnswer(question, submitted ?? question.defaults ?? []);
1716
1917
  if (problem !== null) {
1717
- throw new BadRequestException(`answers["${question.id}"] ${problem}`);
1918
+ throw new BadRequestException2(`answers["${question.id}"] ${problem}`);
1718
1919
  }
1719
1920
  }
1720
1921
  }
@@ -1788,7 +1989,7 @@ AgentService = _ts_decorate3([
1788
1989
  Injectable3(),
1789
1990
  _ts_param3(0, Inject3(AGENT_RUNNER)),
1790
1991
  _ts_param3(1, Inject3(AGENT_STORE3)),
1791
- _ts_param3(2, Inject3(AGENT_DEPS_FACTORY)),
1992
+ _ts_param3(2, Inject3(AGENT_DEPS_FACTORY2)),
1792
1993
  _ts_param3(3, Optional3()),
1793
1994
  _ts_param3(3, Inject3(AGENT_ATTACHMENT_STAGING2)),
1794
1995
  _ts_param3(4, Optional3()),
@@ -1879,7 +2080,7 @@ AgentApprovalPortAdapter = _ts_decorate4([
1879
2080
  ], AgentApprovalPortAdapter);
1880
2081
 
1881
2082
  // src/controller/agents.controller.ts
1882
- import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY2, AGENT_REGISTRY as AGENT_REGISTRY2, AgentRegistry as AgentRegistry3 } from "@dudousxd/nestjs-agent-core";
2083
+ import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY3, AGENT_REGISTRY as AGENT_REGISTRY2, AgentRegistry as AgentRegistry3 } from "@dudousxd/nestjs-agent-core";
1883
2084
  import { Controller, Get, Inject as Inject4 } from "@nestjs/common";
1884
2085
  function _ts_decorate5(decorators, target, key, desc) {
1885
2086
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -1919,13 +2120,22 @@ var AgentsController = class {
1919
2120
  }
1920
2121
  list() {
1921
2122
  const defaultAgentName = this.depsFactory.defaultAgentName();
1922
- return this.agents.list().map((definition) => ({
1923
- name: definition.name,
1924
- description: definition.description ?? "",
1925
- ...definition.name === defaultAgentName ? {
1926
- isDefault: true
1927
- } : {}
1928
- }));
2123
+ return this.agents.list().map((definition) => {
2124
+ const personas = this.depsFactory.personaCatalog(definition.name);
2125
+ return {
2126
+ name: definition.name,
2127
+ description: definition.description ?? "",
2128
+ ...definition.name === defaultAgentName ? {
2129
+ isDefault: true
2130
+ } : {},
2131
+ ...personas.length > 0 ? {
2132
+ personas
2133
+ } : {},
2134
+ ...definition.defaultPersona !== void 0 ? {
2135
+ defaultPersona: definition.defaultPersona
2136
+ } : {}
2137
+ };
2138
+ });
1929
2139
  }
1930
2140
  };
1931
2141
  _ts_decorate5([
@@ -1937,7 +2147,7 @@ _ts_decorate5([
1937
2147
  AgentsController = _ts_decorate5([
1938
2148
  Controller("agents"),
1939
2149
  _ts_param4(0, Inject4(AGENT_REGISTRY2)),
1940
- _ts_param4(1, Inject4(AGENT_DEPS_FACTORY2)),
2150
+ _ts_param4(1, Inject4(AGENT_DEPS_FACTORY3)),
1941
2151
  _ts_metadata5("design:type", Function),
1942
2152
  _ts_metadata5("design:paramtypes", [
1943
2153
  typeof AgentRegistry3 === "undefined" ? Object : AgentRegistry3,
@@ -1947,7 +2157,7 @@ AgentsController = _ts_decorate5([
1947
2157
 
1948
2158
  // src/controller/attachments.controller.ts
1949
2159
  import { AGENT_ACTOR_RESOLVER, AGENT_ATTACHMENT_STAGING as AGENT_ATTACHMENT_STAGING3, AGENT_OPTIONS as AGENT_OPTIONS4 } from "@dudousxd/nestjs-agent-core";
1950
- import { BadRequestException as BadRequestException2, Controller as Controller2, Get as Get2, Inject as Inject5, NotImplementedException as NotImplementedException3, Optional as Optional4, PayloadTooLargeException, Post, Req, UnsupportedMediaTypeException, UploadedFile, UseInterceptors } from "@nestjs/common";
2160
+ import { BadRequestException as BadRequestException3, Controller as Controller2, Get as Get2, Inject as Inject5, NotImplementedException as NotImplementedException3, Optional as Optional4, PayloadTooLargeException, Post, Req, UnsupportedMediaTypeException, UploadedFile, UseInterceptors } from "@nestjs/common";
1951
2161
  import { FileInterceptor } from "@nestjs/platform-express";
1952
2162
 
1953
2163
  // src/attachment-limits.ts
@@ -2038,7 +2248,7 @@ var AttachmentsController = class {
2038
2248
  throw new NotImplementedException3("Attachment upload is not configured on this server: bind AGENT_ATTACHMENT_STAGING (e.g. import AgentMediaAttachmentsModule).");
2039
2249
  }
2040
2250
  if (file === void 0) {
2041
- throw new BadRequestException2('multipart field "file" is required');
2251
+ throw new BadRequestException3('multipart field "file" is required');
2042
2252
  }
2043
2253
  const { allowedContentTypes, maxBytes } = attachmentLimits(this.options, this.staging);
2044
2254
  if (!allowedContentTypes.includes(file.mimetype)) {
@@ -2099,7 +2309,7 @@ AttachmentsController = _ts_decorate6([
2099
2309
 
2100
2310
  // src/controller/chat.controller.ts
2101
2311
  import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER2, AgentStreamError, streamFailure } from "@dudousxd/nestjs-agent-core";
2102
- import { BadRequestException as BadRequestException3, Body, Controller as Controller3, Get as Get3, Inject as Inject6, Param, Post as Post2, Query, Req as Req2, Res } from "@nestjs/common";
2312
+ import { BadRequestException as BadRequestException4, Body, Controller as Controller3, Get as Get3, Inject as Inject6, Param, Post as Post2, Query, Req as Req2, Res } from "@nestjs/common";
2103
2313
  function _ts_decorate7(decorators, target, key, desc) {
2104
2314
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2105
2315
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -2136,7 +2346,7 @@ function sendMode(body) {
2136
2346
  if (body.mode === "auto" || body.mode === "queue" || body.mode === "interrupt") {
2137
2347
  return body.mode;
2138
2348
  }
2139
- throw new BadRequestException3("mode must be 'auto', 'queue' or 'interrupt'");
2349
+ throw new BadRequestException4("mode must be 'auto', 'queue' or 'interrupt'");
2140
2350
  }
2141
2351
  __name(sendMode, "sendMode");
2142
2352
  function attachmentRefs(claimed) {
@@ -2144,21 +2354,21 @@ function attachmentRefs(claimed) {
2144
2354
  return [];
2145
2355
  }
2146
2356
  if (!Array.isArray(claimed)) {
2147
- throw new BadRequestException3("attachments must be an array of { mediaId }");
2357
+ throw new BadRequestException4("attachments must be an array of { mediaId }");
2148
2358
  }
2149
2359
  if (claimed.length > MAX_ATTACHMENTS_PER_MESSAGE) {
2150
- throw new BadRequestException3(`a message may carry at most ${MAX_ATTACHMENTS_PER_MESSAGE} attachments`);
2360
+ throw new BadRequestException4(`a message may carry at most ${MAX_ATTACHMENTS_PER_MESSAGE} attachments`);
2151
2361
  }
2152
2362
  return claimed.map((entry) => {
2153
2363
  if (typeof entry !== "object" || entry === null || !("mediaId" in entry)) {
2154
- throw new BadRequestException3("each attachment must be an object with a mediaId");
2364
+ throw new BadRequestException4("each attachment must be an object with a mediaId");
2155
2365
  }
2156
2366
  if (Object.keys(entry).some((key) => key !== "mediaId")) {
2157
- throw new BadRequestException3("attachments are { mediaId } refs \u2014 send nothing but the id");
2367
+ throw new BadRequestException4("attachments are { mediaId } refs \u2014 send nothing but the id");
2158
2368
  }
2159
2369
  const { mediaId } = entry;
2160
2370
  if (typeof mediaId !== "string" || mediaId.length === 0) {
2161
- throw new BadRequestException3("each attachment must carry a non-empty string mediaId");
2371
+ throw new BadRequestException4("each attachment must carry a non-empty string mediaId");
2162
2372
  }
2163
2373
  return {
2164
2374
  mediaId
@@ -2195,6 +2405,9 @@ var ChatController = class {
2195
2405
  ...body.agent !== void 0 ? {
2196
2406
  agentName: body.agent
2197
2407
  } : {},
2408
+ ...typeof body.persona === "string" && body.persona.length > 0 ? {
2409
+ personaId: body.persona
2410
+ } : {},
2198
2411
  ...attachments.length > 0 ? {
2199
2412
  attachments
2200
2413
  } : {},
@@ -2821,7 +3034,7 @@ ModelsController = _ts_decorate11([
2821
3034
 
2822
3035
  // src/controller/queue.controller.ts
2823
3036
  import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER7 } from "@dudousxd/nestjs-agent-core";
2824
- import { BadRequestException as BadRequestException4, Body as Body3, Controller as Controller8, Delete as Delete2, Get as Get7, Inject as Inject11, Param as Param4, Patch, Post as Post4, Req as Req7 } from "@nestjs/common";
3037
+ import { BadRequestException as BadRequestException5, Body as Body3, Controller as Controller8, Delete as Delete2, Get as Get7, Inject as Inject11, Param as Param4, Patch, Post as Post4, Req as Req7 } from "@nestjs/common";
2825
3038
  function _ts_decorate12(decorators, target, key, desc) {
2826
3039
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2827
3040
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -2871,10 +3084,10 @@ var QueueController = class {
2871
3084
  async update(req, messageId, body) {
2872
3085
  const actor = await this.actorResolver.resolve(req);
2873
3086
  if (body.message !== void 0 && typeof body.message !== "string") {
2874
- throw new BadRequestException4("message must be a string");
3087
+ throw new BadRequestException5("message must be a string");
2875
3088
  }
2876
3089
  if (body.position !== void 0 && typeof body.position !== "number") {
2877
- throw new BadRequestException4("position must be a number");
3090
+ throw new BadRequestException5("position must be a number");
2878
3091
  }
2879
3092
  const attachments = body.attachments === void 0 ? void 0 : body.attachments === null ? null : attachmentRefs(body.attachments);
2880
3093
  return this.agent.updateQueuedMessage(actor, messageId, {
@@ -3194,6 +3407,9 @@ var ThreadsController = class {
3194
3407
  } : {},
3195
3408
  ..."model" in body ? {
3196
3409
  model: body.model ?? null
3410
+ } : {},
3411
+ ..."persona" in body ? {
3412
+ persona: body.persona ?? null
3197
3413
  } : {}
3198
3414
  });
3199
3415
  return {
@@ -3308,7 +3524,7 @@ ThreadsController = _ts_decorate15([
3308
3524
 
3309
3525
  // src/controller/tool-call.controller.ts
3310
3526
  import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER11 } from "@dudousxd/nestjs-agent-core";
3311
- import { BadRequestException as BadRequestException5, Body as Body5, Controller as Controller12, Inject as Inject15, Post as Post6, Req as Req11 } from "@nestjs/common";
3527
+ import { BadRequestException as BadRequestException6, Body as Body5, Controller as Controller12, Inject as Inject15, Post as Post6, Req as Req11 } from "@nestjs/common";
3312
3528
  function _ts_decorate16(decorators, target, key, desc) {
3313
3529
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3314
3530
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -3343,7 +3559,7 @@ var MAX_VIA_LENGTH = 64;
3343
3559
  var DEFAULT_VIA = "web";
3344
3560
  function toolCallId(claimed) {
3345
3561
  if (typeof claimed !== "string" || claimed.length === 0) {
3346
- throw new BadRequestException5("toolCallId must be a non-empty string");
3562
+ throw new BadRequestException6("toolCallId must be a non-empty string");
3347
3563
  }
3348
3564
  return claimed;
3349
3565
  }
@@ -3353,10 +3569,10 @@ function rejectReason(claimed) {
3353
3569
  return void 0;
3354
3570
  }
3355
3571
  if (typeof claimed !== "string") {
3356
- throw new BadRequestException5("reason must be a string");
3572
+ throw new BadRequestException6("reason must be a string");
3357
3573
  }
3358
3574
  if (claimed.length > MAX_REJECT_REASON_LENGTH) {
3359
- throw new BadRequestException5(`reason must be at most ${MAX_REJECT_REASON_LENGTH} characters`);
3575
+ throw new BadRequestException6(`reason must be at most ${MAX_REJECT_REASON_LENGTH} characters`);
3360
3576
  }
3361
3577
  return claimed;
3362
3578
  }
@@ -3366,7 +3582,7 @@ function remember(claimed) {
3366
3582
  return false;
3367
3583
  }
3368
3584
  if (typeof claimed !== "boolean") {
3369
- throw new BadRequestException5("remember must be a boolean");
3585
+ throw new BadRequestException6("remember must be a boolean");
3370
3586
  }
3371
3587
  return claimed;
3372
3588
  }
@@ -3376,7 +3592,7 @@ function via(claimed) {
3376
3592
  return DEFAULT_VIA;
3377
3593
  }
3378
3594
  if (typeof claimed !== "string" || claimed.length === 0 || claimed.length > MAX_VIA_LENGTH) {
3379
- throw new BadRequestException5(`via must be a string of 1-${MAX_VIA_LENGTH} characters`);
3595
+ throw new BadRequestException6(`via must be a string of 1-${MAX_VIA_LENGTH} characters`);
3380
3596
  }
3381
3597
  return claimed;
3382
3598
  }
@@ -3386,26 +3602,26 @@ function answers(claimed) {
3386
3602
  return void 0;
3387
3603
  }
3388
3604
  if (typeof claimed !== "object" || claimed === null || Array.isArray(claimed)) {
3389
- throw new BadRequestException5("answers must be an object of questionId \u2192 chosen values");
3605
+ throw new BadRequestException6("answers must be an object of questionId \u2192 chosen values");
3390
3606
  }
3391
3607
  const entries = Object.entries(claimed);
3392
3608
  if (entries.length > MAX_ANSWERED_QUESTIONS) {
3393
- throw new BadRequestException5(`answers may cover at most ${MAX_ANSWERED_QUESTIONS} questions`);
3609
+ throw new BadRequestException6(`answers may cover at most ${MAX_ANSWERED_QUESTIONS} questions`);
3394
3610
  }
3395
3611
  const validated = /* @__PURE__ */ Object.create(null);
3396
3612
  for (const [questionId, values] of entries) {
3397
3613
  if (!Array.isArray(values)) {
3398
- throw new BadRequestException5(`answers["${questionId}"] must be an array of chosen values`);
3614
+ throw new BadRequestException6(`answers["${questionId}"] must be an array of chosen values`);
3399
3615
  }
3400
3616
  if (values.length > MAX_VALUES_PER_QUESTION) {
3401
- throw new BadRequestException5(`answers["${questionId}"] may carry at most ${MAX_VALUES_PER_QUESTION} values`);
3617
+ throw new BadRequestException6(`answers["${questionId}"] may carry at most ${MAX_VALUES_PER_QUESTION} values`);
3402
3618
  }
3403
3619
  for (const value of values) {
3404
3620
  if (typeof value !== "string") {
3405
- throw new BadRequestException5(`answers["${questionId}"] must contain only strings`);
3621
+ throw new BadRequestException6(`answers["${questionId}"] must contain only strings`);
3406
3622
  }
3407
3623
  if (value.length > MAX_ANSWER_VALUE_LENGTH) {
3408
- throw new BadRequestException5(`answers["${questionId}"] values must be at most ${MAX_ANSWER_VALUE_LENGTH} characters`);
3624
+ throw new BadRequestException6(`answers["${questionId}"] values must be at most ${MAX_ANSWER_VALUE_LENGTH} characters`);
3409
3625
  }
3410
3626
  }
3411
3627
  validated[questionId] = values;
@@ -3522,7 +3738,7 @@ ToolCallController = _ts_decorate16([
3522
3738
  ], ToolCallController);
3523
3739
 
3524
3740
  // src/controller/tools.controller.ts
3525
- import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER12, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY3, AGENT_REGISTRY as AGENT_REGISTRY3, ALL_AGENTS, AgentRegistry as AgentRegistry4 } from "@dudousxd/nestjs-agent-core";
3741
+ import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER12, AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY4, AGENT_REGISTRY as AGENT_REGISTRY3, ALL_AGENTS, AgentRegistry as AgentRegistry4 } from "@dudousxd/nestjs-agent-core";
3526
3742
  import { Controller as Controller13, Get as Get11, Inject as Inject16, NotFoundException as NotFoundException3, Query as Query5, Req as Req12 } from "@nestjs/common";
3527
3743
  function _ts_decorate17(decorators, target, key, desc) {
3528
3744
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -3607,7 +3823,7 @@ _ts_decorate17([
3607
3823
  ToolsController = _ts_decorate17([
3608
3824
  Controller13("tools"),
3609
3825
  _ts_param16(0, Inject16(AGENT_ACTOR_RESOLVER12)),
3610
- _ts_param16(1, Inject16(AGENT_DEPS_FACTORY3)),
3826
+ _ts_param16(1, Inject16(AGENT_DEPS_FACTORY4)),
3611
3827
  _ts_param16(2, Inject16(AGENT_REGISTRY3)),
3612
3828
  _ts_metadata17("design:type", Function),
3613
3829
  _ts_metadata17("design:paramtypes", [
@@ -3707,6 +3923,7 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
3707
3923
  }
3708
3924
  this.collectContributors(instance);
3709
3925
  }
3926
+ this.assertPersonas(this.registry.list());
3710
3927
  }
3711
3928
  /** Build the internal {@link AgentDefinition} from an `@Agent` instance and its options. */
3712
3929
  definitionFrom(instance, meta) {
@@ -3750,10 +3967,48 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
3750
3967
  } : {},
3751
3968
  ...meta.handoff !== void 0 ? {
3752
3969
  delegatesTo: this.handoffEdges(meta)
3970
+ } : {},
3971
+ ...meta.personas !== void 0 ? {
3972
+ personas: meta.personas
3973
+ } : {},
3974
+ ...meta.defaultPersona !== void 0 ? {
3975
+ defaultPersona: meta.defaultPersona
3753
3976
  } : {}
3754
3977
  };
3755
3978
  }
3756
3979
  /**
3980
+ * Refuse to boot on a persona declaration that could only fail later, per request: a default that
3981
+ * names no persona, an id declared twice, an alias two personas claim, or an alias that is a real
3982
+ * agent's name (which would never be reached — a registered agent always wins).
3983
+ */
3984
+ assertPersonas(definitions) {
3985
+ const agentNames = new Set(definitions.map((definition) => definition.name));
3986
+ const aliasOwner = /* @__PURE__ */ new Map();
3987
+ for (const definition of definitions) {
3988
+ const ids = /* @__PURE__ */ new Set();
3989
+ for (const persona of definition.personas ?? []) {
3990
+ if (ids.has(persona.id)) {
3991
+ throw new Error(`Agent "${definition.name}" declares persona "${persona.id}" twice.`);
3992
+ }
3993
+ ids.add(persona.id);
3994
+ for (const alias of persona.aliases ?? []) {
3995
+ const owner = `${definition.name}/${persona.id}`;
3996
+ if (agentNames.has(alias)) {
3997
+ throw new Error(`Persona ${owner} claims the alias "${alias}", which is a registered agent's name.`);
3998
+ }
3999
+ const previous = aliasOwner.get(alias);
4000
+ if (previous !== void 0) {
4001
+ throw new Error(`Personas ${previous} and ${owner} both claim the alias "${alias}".`);
4002
+ }
4003
+ aliasOwner.set(alias, owner);
4004
+ }
4005
+ }
4006
+ if (definition.defaultPersona !== void 0 && !ids.has(definition.defaultPersona)) {
4007
+ throw new Error(`Agent "${definition.name}" names "${definition.defaultPersona}" as its default persona, but declares no persona with that id.`);
4008
+ }
4009
+ }
4010
+ }
4011
+ /**
3757
4012
  * Resolve each handoff entry to an {@link AgentDelegation} — its target's registered name, plus
3758
4013
  * whether that edge detaches (skipping non-agent classes). A bare name is kept for a plain edge
3759
4014
  * rather than an always-object form, so a definition an older host built by hand still reads.
@@ -4313,7 +4568,7 @@ var LedgerQuotaProvider = class {
4313
4568
  var AGENT_CHAT_QUEUE = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:chat-queue");
4314
4569
 
4315
4570
  // src/runner/inline-agent-runner.ts
4316
- import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY4, AGENT_STORE as AGENT_STORE4, RUN_ENDED_BEFORE_TOOL_CALL, RunCancelledError, agentFailureCode, encodeStreamEvent as encodeStreamEvent2, publishAgentRunFailed, releaseThreadRun, runAgentLoop, settleAll, settleUnsettledDelegation, streamFailure as streamFailure2 } from "@dudousxd/nestjs-agent-core";
4571
+ import { AGENT_DEPS_FACTORY as AGENT_DEPS_FACTORY5, AGENT_STORE as AGENT_STORE4, RUN_ENDED_BEFORE_TOOL_CALL, RunCancelledError, agentFailureCode, encodeStreamEvent as encodeStreamEvent2, publishAgentRunFailed, releaseThreadRun, runAgentLoop, settleAll, settleUnsettledDelegation, streamFailure as streamFailure2 } from "@dudousxd/nestjs-agent-core";
4317
4572
  import { Inject as Inject20, Injectable as Injectable8, Logger as Logger5, Optional as Optional8 } from "@nestjs/common";
4318
4573
  function _ts_decorate21(decorators, target, key, desc) {
4319
4574
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -4793,7 +5048,7 @@ var InlineAgentRunner = class _InlineAgentRunner {
4793
5048
  };
4794
5049
  InlineAgentRunner = _ts_decorate21([
4795
5050
  Injectable8(),
4796
- _ts_param20(0, Inject20(AGENT_DEPS_FACTORY4)),
5051
+ _ts_param20(0, Inject20(AGENT_DEPS_FACTORY5)),
4797
5052
  _ts_param20(1, Inject20(AGENT_STORE4)),
4798
5053
  _ts_param20(2, Optional8()),
4799
5054
  _ts_metadata21("design:type", Function),
@@ -5005,7 +5260,7 @@ function sharedProviders(durable) {
5005
5260
  },
5006
5261
  AgentDepsFactory2,
5007
5262
  {
5008
- provide: AGENT_DEPS_FACTORY5,
5263
+ provide: AGENT_DEPS_FACTORY6,
5009
5264
  useExisting: AgentDepsFactory2
5010
5265
  },
5011
5266
  // Populates the registry + contributors (onModuleInit) BEFORE AiToolDiscoveryService synthesizes
@@ -5078,7 +5333,7 @@ function exportsFor() {
5078
5333
  AGENT_PROMPT_CONTRIBUTORS3,
5079
5334
  AGENT_SKILLS3,
5080
5335
  AGENT_MEMORY3,
5081
- AGENT_DEPS_FACTORY5,
5336
+ AGENT_DEPS_FACTORY6,
5082
5337
  AgentDepsFactory2,
5083
5338
  AgentService,
5084
5339
  ChatQueueService,
@@ -5215,7 +5470,7 @@ AgentModule = _ts_decorate22([
5215
5470
  // src/ag-ui/ag-ui.adapter.ts
5216
5471
  import { AGENT_ACTOR_RESOLVER as AGENT_ACTOR_RESOLVER14, AGENT_ATTACHMENT_STAGING as AGENT_ATTACHMENT_STAGING5, AGENT_OPTIONS as AGENT_OPTIONS8 } from "@dudousxd/nestjs-agent-core";
5217
5472
  import { AG_UI_CUSTOM, agUiEvents, agUiFramesFromNdjson, agUiSse, parseRunInput, planResume, readAnswersPayload, readApprovalPayload, readContext, readForwardedProps, readUserTurn } from "@dudousxd/nestjs-agent-core/ag-ui";
5218
- import { BadRequestException as BadRequestException6, Body as Body6, Controller as Controller14, ForbiddenException as ForbiddenException3, HttpCode as HttpCode2, Inject as Inject21, Optional as Optional9, Post as Post7, Req as Req13, Res as Res2 } from "@nestjs/common";
5473
+ import { BadRequestException as BadRequestException7, Body as Body6, Controller as Controller14, ForbiddenException as ForbiddenException3, HttpCode as HttpCode2, Inject as Inject21, Optional as Optional9, Post as Post7, Req as Req13, Res as Res2 } from "@nestjs/common";
5219
5474
  function _ts_decorate23(decorators, target, key, desc) {
5220
5475
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
5221
5476
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -5244,7 +5499,7 @@ function _ts_param21(paramIndex, decorator) {
5244
5499
  __name(_ts_param21, "_ts_param");
5245
5500
  var AG_UI_VIA = "ag-ui";
5246
5501
  function invalid(code, message) {
5247
- return new BadRequestException6({
5502
+ return new BadRequestException7({
5248
5503
  statusCode: 400,
5249
5504
  code,
5250
5505
  message
@@ -5347,6 +5602,9 @@ var AgUiRunHandler = class {
5347
5602
  ...forwarded.agent !== void 0 ? {
5348
5603
  agentName: forwarded.agent
5349
5604
  } : {},
5605
+ ...forwarded.persona !== void 0 ? {
5606
+ personaId: forwarded.persona
5607
+ } : {},
5350
5608
  ...forwarded.model !== void 0 ? {
5351
5609
  model: forwarded.model
5352
5610
  } : {},
@@ -5661,6 +5919,7 @@ export {
5661
5919
  requestUserActorResolver,
5662
5920
  resolveSkillsConfig,
5663
5921
  skillScope,
5922
+ threadPersona,
5664
5923
  toolNameFromClass,
5665
5924
  utcDay
5666
5925
  };