@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/{chat-queue.service-CKZkjlMK.d.cts → chat-queue.service-C0hr7dqx.d.cts} +41 -2
- package/dist/{chat-queue.service-CKZkjlMK.d.ts → chat-queue.service-C0hr7dqx.d.ts} +41 -2
- package/dist/durable/index.cjs +287 -23
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +1 -1
- package/dist/durable/index.d.ts +1 -1
- package/dist/durable/index.js +340 -76
- package/dist/durable/index.js.map +1 -1
- package/dist/index.cjs +274 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -4
- package/dist/index.d.ts +50 -4
- package/dist/index.js +327 -68
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ModelProvider, AgentStore, ToolRegistry, RolesPolicy, ApprovalPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, QuotaProvider, QuotaQuery, QuotaReport, ModelCatalog, EmptyRoles, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry, AttachmentStagingStore, ChatQueueStore, ChatQueueState, AgentRunner, AgentRunInput, AgentStreamEvent, QueuePauseReason, Actor } from '@dudousxd/nestjs-agent-core';
|
|
1
|
+
import { ModelProvider, AgentStore, ToolRegistry, RolesPolicy, ApprovalPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, Persona, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, QuotaProvider, QuotaQuery, QuotaReport, ModelCatalog, EmptyRoles, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry, PersonaCatalogEntry, AttachmentStagingStore, ChatQueueStore, ChatQueueState, AgentRunner, AgentRunInput, AgentStreamEvent, QueuePauseReason, Actor } from '@dudousxd/nestjs-agent-core';
|
|
2
2
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
3
|
import { Provider, FactoryProvider, Type, CanActivate, ModuleMetadata, InjectionToken, OptionalFactoryDependency } from '@nestjs/common';
|
|
4
4
|
|
|
@@ -24,6 +24,8 @@ interface AgentDeps {
|
|
|
24
24
|
maxAgentAppearances?: number;
|
|
25
25
|
/** Agent-level tool allow-list. Undefined → all tools (after role filtering). */
|
|
26
26
|
toolAllowList?: string[];
|
|
27
|
+
/** The agent's personas (`@Agent({ personas })`) — read by the loop only for a run that names one. */
|
|
28
|
+
personas?: Persona[];
|
|
27
29
|
/** Per-tool execution timeout in ms (from module options). Undefined → no timeout. */
|
|
28
30
|
toolTimeoutMs?: number;
|
|
29
31
|
/**
|
|
@@ -534,7 +536,36 @@ declare class AgentDepsFactory {
|
|
|
534
536
|
*/
|
|
535
537
|
defaultAgentName(): string;
|
|
536
538
|
private effectiveTools;
|
|
539
|
+
/**
|
|
540
|
+
* The agent a name stands for: itself when it is a registered agent (or unknown — the bare
|
|
541
|
+
* assistant), else the agent whose persona took the name over (`Persona.aliases`) and that persona.
|
|
542
|
+
*/
|
|
543
|
+
resolveAgent(name: string): {
|
|
544
|
+
agentName: string;
|
|
545
|
+
persona?: string;
|
|
546
|
+
};
|
|
547
|
+
/** `agentName`'s personas as a picker reads them. Empty when it declares none. */
|
|
548
|
+
personaCatalog(agentName: string): PersonaCatalogEntry[];
|
|
549
|
+
/** The persona `agentName` runs under when nothing names one. Undefined → none. */
|
|
550
|
+
defaultPersona(agentName: string): string | undefined;
|
|
551
|
+
/**
|
|
552
|
+
* The persona a turn of `agentName` runs under, most specific first: the one the send names
|
|
553
|
+
* (refused with `400 persona_not_found` when the agent does not declare it), else the thread's
|
|
554
|
+
* pinned one when this agent declares it, else the agent's default. Undefined → none.
|
|
555
|
+
*/
|
|
556
|
+
resolvePersona(args: {
|
|
557
|
+
agentName: string;
|
|
558
|
+
requested?: string;
|
|
559
|
+
threadPersona?: string | null;
|
|
560
|
+
}): string | undefined;
|
|
537
561
|
forAgent(agentName?: string): AgentDeps;
|
|
562
|
+
/**
|
|
563
|
+
* A persona baked into an agent's deps — how a run journaled under an agent name that a persona
|
|
564
|
+
* has since taken over (`Persona.aliases`) is served. That run names no persona of its own (it
|
|
565
|
+
* predates them, and its input cannot change), so the persona applies the way the old agent's own
|
|
566
|
+
* config did: as config, re-read on every replay, with no checkpoint of its own.
|
|
567
|
+
*/
|
|
568
|
+
private withPersona;
|
|
538
569
|
/**
|
|
539
570
|
* The ceiling on how much of a thread this agent's turns carry, most specific wins: the agent's
|
|
540
571
|
* own `@Agent({ history })`, else a module-wide custom `historyPolicy`, else the module-wide
|
|
@@ -591,13 +622,14 @@ declare class ChatQueueService {
|
|
|
591
622
|
private readonly quotaProvider?;
|
|
592
623
|
private readonly options?;
|
|
593
624
|
private readonly staging?;
|
|
625
|
+
private readonly deps?;
|
|
594
626
|
private readonly logger;
|
|
595
627
|
/**
|
|
596
628
|
* Runs this process claimed a thread for and has not handed to a runner yet. A holder in here is
|
|
597
629
|
* alive whatever the runner says — it simply has not registered the run yet.
|
|
598
630
|
*/
|
|
599
631
|
private readonly starting;
|
|
600
|
-
constructor(store: AgentStore, sink: TokenStreamSink, quotaProvider?: QuotaProvider | undefined, options?: Pick<AgentModuleOptions, "quota"> | undefined, staging?: AttachmentStagingStore | undefined);
|
|
632
|
+
constructor(store: AgentStore, sink: TokenStreamSink, quotaProvider?: QuotaProvider | undefined, options?: Pick<AgentModuleOptions, "quota"> | undefined, staging?: AttachmentStagingStore | undefined, deps?: AgentDepsFactory | undefined);
|
|
601
633
|
/** Whether the bound store can hold a queue. Without one, a busy thread is not queued. */
|
|
602
634
|
get supported(): boolean;
|
|
603
635
|
/** The bound store as a queue store, or a 501 naming what is missing. */
|
|
@@ -673,6 +705,13 @@ declare class ChatQueueService {
|
|
|
673
705
|
* waited. An attachment the staging store no longer resolves keeps its stored url.
|
|
674
706
|
*/
|
|
675
707
|
private inputFor;
|
|
708
|
+
/**
|
|
709
|
+
* The agent and persona a queued message starts as. A message carries the persona its send
|
|
710
|
+
* resolved; one that carries none — queued before personas existed, or by a host that sets none —
|
|
711
|
+
* resolves it now, exactly as a send would: an agent name a persona took over, then the thread's
|
|
712
|
+
* pinned persona, then the agent's default. Never refused here: nobody is waiting on a 400.
|
|
713
|
+
*/
|
|
714
|
+
private targetFor;
|
|
676
715
|
private freshAttachments;
|
|
677
716
|
}
|
|
678
717
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ModelProvider, AgentStore, ToolRegistry, RolesPolicy, ApprovalPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, QuotaProvider, QuotaQuery, QuotaReport, ModelCatalog, EmptyRoles, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry, AttachmentStagingStore, ChatQueueStore, ChatQueueState, AgentRunner, AgentRunInput, AgentStreamEvent, QueuePauseReason, Actor } from '@dudousxd/nestjs-agent-core';
|
|
1
|
+
import { ModelProvider, AgentStore, ToolRegistry, RolesPolicy, ApprovalPolicy, QuotaStore, TokenStreamSink, PromptBuilder, PromptContributor, Persona, ToolTransientRetrySetting, Retriever, AgentPricingStore, HistoryPolicy, InputProcessor, OutputProcessor, AgentIntake, SkillsConfig, MemoryConfig, ToolSpec, ToolHandler, QuotaProvider, QuotaQuery, QuotaReport, ModelCatalog, EmptyRoles, ActorResolver, AgentHistoryWindow, SkillProvider, ScopeResolver, MemoryProvider, AgentRegistry, PersonaCatalogEntry, AttachmentStagingStore, ChatQueueStore, ChatQueueState, AgentRunner, AgentRunInput, AgentStreamEvent, QueuePauseReason, Actor } from '@dudousxd/nestjs-agent-core';
|
|
2
2
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
3
|
import { Provider, FactoryProvider, Type, CanActivate, ModuleMetadata, InjectionToken, OptionalFactoryDependency } from '@nestjs/common';
|
|
4
4
|
|
|
@@ -24,6 +24,8 @@ interface AgentDeps {
|
|
|
24
24
|
maxAgentAppearances?: number;
|
|
25
25
|
/** Agent-level tool allow-list. Undefined → all tools (after role filtering). */
|
|
26
26
|
toolAllowList?: string[];
|
|
27
|
+
/** The agent's personas (`@Agent({ personas })`) — read by the loop only for a run that names one. */
|
|
28
|
+
personas?: Persona[];
|
|
27
29
|
/** Per-tool execution timeout in ms (from module options). Undefined → no timeout. */
|
|
28
30
|
toolTimeoutMs?: number;
|
|
29
31
|
/**
|
|
@@ -534,7 +536,36 @@ declare class AgentDepsFactory {
|
|
|
534
536
|
*/
|
|
535
537
|
defaultAgentName(): string;
|
|
536
538
|
private effectiveTools;
|
|
539
|
+
/**
|
|
540
|
+
* The agent a name stands for: itself when it is a registered agent (or unknown — the bare
|
|
541
|
+
* assistant), else the agent whose persona took the name over (`Persona.aliases`) and that persona.
|
|
542
|
+
*/
|
|
543
|
+
resolveAgent(name: string): {
|
|
544
|
+
agentName: string;
|
|
545
|
+
persona?: string;
|
|
546
|
+
};
|
|
547
|
+
/** `agentName`'s personas as a picker reads them. Empty when it declares none. */
|
|
548
|
+
personaCatalog(agentName: string): PersonaCatalogEntry[];
|
|
549
|
+
/** The persona `agentName` runs under when nothing names one. Undefined → none. */
|
|
550
|
+
defaultPersona(agentName: string): string | undefined;
|
|
551
|
+
/**
|
|
552
|
+
* The persona a turn of `agentName` runs under, most specific first: the one the send names
|
|
553
|
+
* (refused with `400 persona_not_found` when the agent does not declare it), else the thread's
|
|
554
|
+
* pinned one when this agent declares it, else the agent's default. Undefined → none.
|
|
555
|
+
*/
|
|
556
|
+
resolvePersona(args: {
|
|
557
|
+
agentName: string;
|
|
558
|
+
requested?: string;
|
|
559
|
+
threadPersona?: string | null;
|
|
560
|
+
}): string | undefined;
|
|
537
561
|
forAgent(agentName?: string): AgentDeps;
|
|
562
|
+
/**
|
|
563
|
+
* A persona baked into an agent's deps — how a run journaled under an agent name that a persona
|
|
564
|
+
* has since taken over (`Persona.aliases`) is served. That run names no persona of its own (it
|
|
565
|
+
* predates them, and its input cannot change), so the persona applies the way the old agent's own
|
|
566
|
+
* config did: as config, re-read on every replay, with no checkpoint of its own.
|
|
567
|
+
*/
|
|
568
|
+
private withPersona;
|
|
538
569
|
/**
|
|
539
570
|
* The ceiling on how much of a thread this agent's turns carry, most specific wins: the agent's
|
|
540
571
|
* own `@Agent({ history })`, else a module-wide custom `historyPolicy`, else the module-wide
|
|
@@ -591,13 +622,14 @@ declare class ChatQueueService {
|
|
|
591
622
|
private readonly quotaProvider?;
|
|
592
623
|
private readonly options?;
|
|
593
624
|
private readonly staging?;
|
|
625
|
+
private readonly deps?;
|
|
594
626
|
private readonly logger;
|
|
595
627
|
/**
|
|
596
628
|
* Runs this process claimed a thread for and has not handed to a runner yet. A holder in here is
|
|
597
629
|
* alive whatever the runner says — it simply has not registered the run yet.
|
|
598
630
|
*/
|
|
599
631
|
private readonly starting;
|
|
600
|
-
constructor(store: AgentStore, sink: TokenStreamSink, quotaProvider?: QuotaProvider | undefined, options?: Pick<AgentModuleOptions, "quota"> | undefined, staging?: AttachmentStagingStore | undefined);
|
|
632
|
+
constructor(store: AgentStore, sink: TokenStreamSink, quotaProvider?: QuotaProvider | undefined, options?: Pick<AgentModuleOptions, "quota"> | undefined, staging?: AttachmentStagingStore | undefined, deps?: AgentDepsFactory | undefined);
|
|
601
633
|
/** Whether the bound store can hold a queue. Without one, a busy thread is not queued. */
|
|
602
634
|
get supported(): boolean;
|
|
603
635
|
/** The bound store as a queue store, or a 501 naming what is missing. */
|
|
@@ -673,6 +705,13 @@ declare class ChatQueueService {
|
|
|
673
705
|
* waited. An attachment the staging store no longer resolves keeps its stored url.
|
|
674
706
|
*/
|
|
675
707
|
private inputFor;
|
|
708
|
+
/**
|
|
709
|
+
* The agent and persona a queued message starts as. A message carries the persona its send
|
|
710
|
+
* resolved; one that carries none — queued before personas existed, or by a host that sets none —
|
|
711
|
+
* resolves it now, exactly as a send would: an agent name a persona took over, then the thread's
|
|
712
|
+
* pinned persona, then the agent's default. Never refused here: nobody is waiting on a 400.
|
|
713
|
+
*/
|
|
714
|
+
private targetFor;
|
|
676
715
|
private freshAttachments;
|
|
677
716
|
}
|
|
678
717
|
|
package/dist/durable/index.cjs
CHANGED
|
@@ -125,14 +125,21 @@ var AgentRunSteps = class {
|
|
|
125
125
|
const tools = (0, import_nestjs_agent_core.withMemoryTool)({
|
|
126
126
|
tools: (0, import_nestjs_agent_core.withSkillTool)({
|
|
127
127
|
tools: (0, import_nestjs_agent_core.withAskTool)({
|
|
128
|
-
tools: await deps.registry.definitionsFor(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
128
|
+
tools: await deps.registry.definitionsFor(
|
|
129
|
+
input.actor,
|
|
130
|
+
deps.rolesPolicy,
|
|
131
|
+
// The persona narrowing the loop journaled in `persona:resolve` rides the envelope:
|
|
132
|
+
// this worker's own persona config may already say something else.
|
|
133
|
+
(0, import_nestjs_agent_core.intersectAllowLists)(deps.toolAllowList, input.personaAllowedTools),
|
|
134
|
+
{
|
|
135
|
+
...input.threadId !== void 0 ? {
|
|
136
|
+
threadId: input.threadId
|
|
137
|
+
} : {},
|
|
138
|
+
...input.agentName !== void 0 ? {
|
|
139
|
+
agentName: input.agentName
|
|
140
|
+
} : {}
|
|
141
|
+
}
|
|
142
|
+
),
|
|
136
143
|
ask: deps.ask
|
|
137
144
|
}),
|
|
138
145
|
enabled: deps.skills !== void 0
|
|
@@ -205,7 +212,9 @@ var AgentRunSteps = class {
|
|
|
205
212
|
toolType: input.toolType
|
|
206
213
|
}, () => (0, import_nestjs_agent_core.invokeWithTransientRetry)(() => {
|
|
207
214
|
ui.restart();
|
|
208
|
-
const invocation = deps.registry.invoke(input.toolName, input.input, ctx, deps.rolesPolicy
|
|
215
|
+
const invocation = deps.registry.invoke(input.toolName, input.input, ctx, deps.rolesPolicy, input.allowedTools !== void 0 ? {
|
|
216
|
+
allowedTools: input.allowedTools
|
|
217
|
+
} : {});
|
|
209
218
|
return input.timeoutMs !== void 0 ? (0, import_nestjs_agent_core.withToolTimeout)(invocation, input.timeoutMs, input.toolName) : invocation;
|
|
210
219
|
}, transientRetry, {
|
|
211
220
|
onRetry: /* @__PURE__ */ __name((attempt, error) => {
|
|
@@ -1218,8 +1227,64 @@ var AgentDepsFactory2 = class {
|
|
|
1218
1227
|
...delegated
|
|
1219
1228
|
];
|
|
1220
1229
|
}
|
|
1230
|
+
/**
|
|
1231
|
+
* The agent a name stands for: itself when it is a registered agent (or unknown — the bare
|
|
1232
|
+
* assistant), else the agent whose persona took the name over (`Persona.aliases`) and that persona.
|
|
1233
|
+
*/
|
|
1234
|
+
resolveAgent(name) {
|
|
1235
|
+
if (this.agents.has(name)) {
|
|
1236
|
+
return {
|
|
1237
|
+
agentName: name
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
const alias = (0, import_nestjs_agent_core6.resolvePersonaAlias)(this.agents.list(), name);
|
|
1241
|
+
return alias === void 0 ? {
|
|
1242
|
+
agentName: name
|
|
1243
|
+
} : {
|
|
1244
|
+
agentName: alias.agent,
|
|
1245
|
+
persona: alias.persona
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
/** `agentName`'s personas as a picker reads them. Empty when it declares none. */
|
|
1249
|
+
personaCatalog(agentName) {
|
|
1250
|
+
return (this.agents.get(agentName)?.personas ?? []).map(import_nestjs_agent_core6.personaCatalogEntry);
|
|
1251
|
+
}
|
|
1252
|
+
/** The persona `agentName` runs under when nothing names one. Undefined → none. */
|
|
1253
|
+
defaultPersona(agentName) {
|
|
1254
|
+
return this.agents.get(agentName)?.defaultPersona;
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* The persona a turn of `agentName` runs under, most specific first: the one the send names
|
|
1258
|
+
* (refused with `400 persona_not_found` when the agent does not declare it), else the thread's
|
|
1259
|
+
* pinned one when this agent declares it, else the agent's default. Undefined → none.
|
|
1260
|
+
*/
|
|
1261
|
+
resolvePersona(args) {
|
|
1262
|
+
const definition = this.agents.get(args.agentName);
|
|
1263
|
+
if (args.requested !== void 0) {
|
|
1264
|
+
if ((0, import_nestjs_agent_core6.findPersona)(definition, args.requested) === void 0) {
|
|
1265
|
+
throw new import_common5.BadRequestException({
|
|
1266
|
+
statusCode: 400,
|
|
1267
|
+
code: "persona_not_found",
|
|
1268
|
+
message: `agent "${args.agentName}" has no persona "${args.requested}"`
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
return args.requested;
|
|
1272
|
+
}
|
|
1273
|
+
const pinned = args.threadPersona ?? void 0;
|
|
1274
|
+
if (pinned !== void 0 && (0, import_nestjs_agent_core6.findPersona)(definition, pinned) !== void 0) {
|
|
1275
|
+
return pinned;
|
|
1276
|
+
}
|
|
1277
|
+
return definition?.defaultPersona;
|
|
1278
|
+
}
|
|
1221
1279
|
forAgent(agentName) {
|
|
1222
1280
|
const name = agentName ?? this.defaultAgentName();
|
|
1281
|
+
if (agentName !== void 0 && !this.agents.has(agentName)) {
|
|
1282
|
+
const alias = (0, import_nestjs_agent_core6.resolvePersonaAlias)(this.agents.list(), agentName);
|
|
1283
|
+
const persona = alias === void 0 ? void 0 : (0, import_nestjs_agent_core6.findPersona)(this.agents.get(alias.agent), alias.persona);
|
|
1284
|
+
if (alias !== void 0 && persona !== void 0) {
|
|
1285
|
+
return this.withPersona(this.forAgent(alias.agent), persona);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1223
1288
|
const definition = this.agents.get(name);
|
|
1224
1289
|
const toolAllowList = this.effectiveTools(definition);
|
|
1225
1290
|
const followUpsCount = this.followUpsCount();
|
|
@@ -1284,6 +1349,9 @@ var AgentDepsFactory2 = class {
|
|
|
1284
1349
|
...toolAllowList !== void 0 ? {
|
|
1285
1350
|
toolAllowList
|
|
1286
1351
|
} : {},
|
|
1352
|
+
...definition?.personas !== void 0 ? {
|
|
1353
|
+
personas: definition.personas
|
|
1354
|
+
} : {},
|
|
1287
1355
|
...this.options.toolTimeoutMs !== void 0 ? {
|
|
1288
1356
|
toolTimeoutMs: this.options.toolTimeoutMs
|
|
1289
1357
|
} : {},
|
|
@@ -1302,6 +1370,45 @@ var AgentDepsFactory2 = class {
|
|
|
1302
1370
|
};
|
|
1303
1371
|
}
|
|
1304
1372
|
/**
|
|
1373
|
+
* A persona baked into an agent's deps — how a run journaled under an agent name that a persona
|
|
1374
|
+
* has since taken over (`Persona.aliases`) is served. That run names no persona of its own (it
|
|
1375
|
+
* predates them, and its input cannot change), so the persona applies the way the old agent's own
|
|
1376
|
+
* config did: as config, re-read on every replay, with no checkpoint of its own.
|
|
1377
|
+
*/
|
|
1378
|
+
withPersona(deps, persona) {
|
|
1379
|
+
const base = deps.systemPrompt;
|
|
1380
|
+
const ref = {
|
|
1381
|
+
id: persona.id,
|
|
1382
|
+
label: persona.label
|
|
1383
|
+
};
|
|
1384
|
+
const resolve = /* @__PURE__ */ __name(async (prompt, ctx) => typeof prompt === "function" ? prompt(ctx) : prompt, "resolve");
|
|
1385
|
+
const own = persona.systemPrompt;
|
|
1386
|
+
const systemPrompt = /* @__PURE__ */ __name(async (ctx) => {
|
|
1387
|
+
const scoped = {
|
|
1388
|
+
...ctx,
|
|
1389
|
+
persona: ref
|
|
1390
|
+
};
|
|
1391
|
+
if (own === void 0) {
|
|
1392
|
+
return resolve(base, scoped);
|
|
1393
|
+
}
|
|
1394
|
+
const basePrompt = typeof own === "function" ? await resolve(base, scoped) : void 0;
|
|
1395
|
+
return resolve(own, {
|
|
1396
|
+
...scoped,
|
|
1397
|
+
...basePrompt !== void 0 ? {
|
|
1398
|
+
basePrompt
|
|
1399
|
+
} : {}
|
|
1400
|
+
});
|
|
1401
|
+
}, "systemPrompt");
|
|
1402
|
+
const toolAllowList = (0, import_nestjs_agent_core6.intersectAllowLists)(deps.toolAllowList, persona.allowedTools);
|
|
1403
|
+
return {
|
|
1404
|
+
...deps,
|
|
1405
|
+
systemPrompt,
|
|
1406
|
+
...toolAllowList !== void 0 ? {
|
|
1407
|
+
toolAllowList
|
|
1408
|
+
} : {}
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1305
1412
|
* The ceiling on how much of a thread this agent's turns carry, most specific wins: the agent's
|
|
1306
1413
|
* own `@Agent({ history })`, else a module-wide custom `historyPolicy`, else the module-wide
|
|
1307
1414
|
* `history` window. Undefined → unbounded, the behaviour of a module that configures none.
|
|
@@ -1382,6 +1489,18 @@ var import_common8 = require("@nestjs/common");
|
|
|
1382
1489
|
// src/queue/chat-queue.service.ts
|
|
1383
1490
|
var import_nestjs_agent_core8 = require("@dudousxd/nestjs-agent-core");
|
|
1384
1491
|
var import_common6 = require("@nestjs/common");
|
|
1492
|
+
|
|
1493
|
+
// src/thread-persona.ts
|
|
1494
|
+
async function threadPersona(store, threadId) {
|
|
1495
|
+
const projecting = store;
|
|
1496
|
+
if (typeof projecting.personaForThread === "function") {
|
|
1497
|
+
return projecting.personaForThread(threadId);
|
|
1498
|
+
}
|
|
1499
|
+
return (await store.getThread(threadId))?.persona ?? null;
|
|
1500
|
+
}
|
|
1501
|
+
__name(threadPersona, "threadPersona");
|
|
1502
|
+
|
|
1503
|
+
// src/queue/chat-queue.service.ts
|
|
1385
1504
|
function _ts_decorate6(decorators, target, key, desc) {
|
|
1386
1505
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1387
1506
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -1418,18 +1537,20 @@ var ChatQueueService2 = class _ChatQueueService {
|
|
|
1418
1537
|
quotaProvider;
|
|
1419
1538
|
options;
|
|
1420
1539
|
staging;
|
|
1540
|
+
deps;
|
|
1421
1541
|
logger = new import_common6.Logger(_ChatQueueService.name);
|
|
1422
1542
|
/**
|
|
1423
1543
|
* Runs this process claimed a thread for and has not handed to a runner yet. A holder in here is
|
|
1424
1544
|
* alive whatever the runner says — it simply has not registered the run yet.
|
|
1425
1545
|
*/
|
|
1426
1546
|
starting = /* @__PURE__ */ new Set();
|
|
1427
|
-
constructor(store, sink, quotaProvider, options, staging) {
|
|
1547
|
+
constructor(store, sink, quotaProvider, options, staging, deps) {
|
|
1428
1548
|
this.store = store;
|
|
1429
1549
|
this.sink = sink;
|
|
1430
1550
|
this.quotaProvider = quotaProvider;
|
|
1431
1551
|
this.options = options;
|
|
1432
1552
|
this.staging = staging;
|
|
1553
|
+
this.deps = deps;
|
|
1433
1554
|
}
|
|
1434
1555
|
/** Whether the bound store can hold a queue. Without one, a busy thread is not queued. */
|
|
1435
1556
|
get supported() {
|
|
@@ -1727,13 +1848,17 @@ var ChatQueueService2 = class _ChatQueueService {
|
|
|
1727
1848
|
*/
|
|
1728
1849
|
async inputFor(message) {
|
|
1729
1850
|
const attachments = await this.freshAttachments(message.actor, message.attachments ?? []);
|
|
1851
|
+
const { agentName, persona } = await this.targetFor(message);
|
|
1730
1852
|
return {
|
|
1731
1853
|
threadId: message.threadId,
|
|
1732
1854
|
actor: message.actor,
|
|
1733
1855
|
userText: message.content,
|
|
1734
1856
|
day: utcDay(),
|
|
1735
|
-
...
|
|
1736
|
-
agentName
|
|
1857
|
+
...agentName !== void 0 ? {
|
|
1858
|
+
agentName
|
|
1859
|
+
} : {},
|
|
1860
|
+
...persona !== void 0 ? {
|
|
1861
|
+
persona
|
|
1737
1862
|
} : {},
|
|
1738
1863
|
...attachments.length > 0 ? {
|
|
1739
1864
|
attachments
|
|
@@ -1746,6 +1871,37 @@ var ChatQueueService2 = class _ChatQueueService {
|
|
|
1746
1871
|
} : {}
|
|
1747
1872
|
};
|
|
1748
1873
|
}
|
|
1874
|
+
/**
|
|
1875
|
+
* The agent and persona a queued message starts as. A message carries the persona its send
|
|
1876
|
+
* resolved; one that carries none — queued before personas existed, or by a host that sets none —
|
|
1877
|
+
* resolves it now, exactly as a send would: an agent name a persona took over, then the thread's
|
|
1878
|
+
* pinned persona, then the agent's default. Never refused here: nobody is waiting on a 400.
|
|
1879
|
+
*/
|
|
1880
|
+
async targetFor(message) {
|
|
1881
|
+
const deps = this.deps;
|
|
1882
|
+
if (message.persona !== void 0 || deps === void 0 || message.agentName === void 0) {
|
|
1883
|
+
return {
|
|
1884
|
+
...message.agentName !== void 0 ? {
|
|
1885
|
+
agentName: message.agentName
|
|
1886
|
+
} : {},
|
|
1887
|
+
...message.persona !== void 0 ? {
|
|
1888
|
+
persona: message.persona
|
|
1889
|
+
} : {}
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
const target = deps.resolveAgent(message.agentName);
|
|
1893
|
+
const hasPersonas = deps.personaCatalog(target.agentName).length > 0;
|
|
1894
|
+
const persona = target.persona ?? (hasPersonas ? deps.resolvePersona({
|
|
1895
|
+
agentName: target.agentName,
|
|
1896
|
+
threadPersona: await threadPersona(this.store, message.threadId)
|
|
1897
|
+
}) : void 0);
|
|
1898
|
+
return {
|
|
1899
|
+
agentName: target.agentName,
|
|
1900
|
+
...persona !== void 0 ? {
|
|
1901
|
+
persona
|
|
1902
|
+
} : {}
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1749
1905
|
async freshAttachments(actor, attachments) {
|
|
1750
1906
|
const staging = this.staging;
|
|
1751
1907
|
if (attachments.length === 0 || staging === void 0 || typeof staging.resolve !== "function") {
|
|
@@ -1773,13 +1929,16 @@ ChatQueueService2 = _ts_decorate6([
|
|
|
1773
1929
|
_ts_param5(3, (0, import_common6.Inject)(import_nestjs_agent_core8.AGENT_OPTIONS)),
|
|
1774
1930
|
_ts_param5(4, (0, import_common6.Optional)()),
|
|
1775
1931
|
_ts_param5(4, (0, import_common6.Inject)(import_nestjs_agent_core8.AGENT_ATTACHMENT_STAGING)),
|
|
1932
|
+
_ts_param5(5, (0, import_common6.Optional)()),
|
|
1933
|
+
_ts_param5(5, (0, import_common6.Inject)(import_nestjs_agent_core8.AGENT_DEPS_FACTORY)),
|
|
1776
1934
|
_ts_metadata5("design:type", Function),
|
|
1777
1935
|
_ts_metadata5("design:paramtypes", [
|
|
1778
1936
|
typeof AgentStore === "undefined" ? Object : AgentStore,
|
|
1779
1937
|
typeof TokenStreamSink === "undefined" ? Object : TokenStreamSink,
|
|
1780
1938
|
typeof QuotaProvider === "undefined" ? Object : QuotaProvider,
|
|
1781
1939
|
typeof Pick === "undefined" ? Object : Pick,
|
|
1782
|
-
typeof AttachmentStagingStore === "undefined" ? Object : AttachmentStagingStore
|
|
1940
|
+
typeof AttachmentStagingStore === "undefined" ? Object : AttachmentStagingStore,
|
|
1941
|
+
typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory
|
|
1783
1942
|
])
|
|
1784
1943
|
], ChatQueueService2);
|
|
1785
1944
|
function pauseReasonFor(outcome, head) {
|
|
@@ -2033,7 +2192,9 @@ var AgentService = class {
|
|
|
2033
2192
|
*/
|
|
2034
2193
|
async send(params) {
|
|
2035
2194
|
await this.assertWithinQuota(params.actor);
|
|
2036
|
-
const
|
|
2195
|
+
const target = this.deps.resolveAgent(await this.resolveAgentName(params.agentName, params.threadId));
|
|
2196
|
+
const agentName = target.agentName;
|
|
2197
|
+
const persona = await this.resolvePersona(agentName, params.personaId ?? target.persona, params.threadId);
|
|
2037
2198
|
const model = await this.resolveModel(params.actor, agentName, params.model, params.threadId);
|
|
2038
2199
|
const attachments = await this.resolveAttachments(params.actor, params.attachments ?? []);
|
|
2039
2200
|
let threadId = params.threadId;
|
|
@@ -2048,11 +2209,17 @@ var AgentService = class {
|
|
|
2048
2209
|
} : {},
|
|
2049
2210
|
...params.newThreadId !== void 0 ? {
|
|
2050
2211
|
id: params.newThreadId
|
|
2212
|
+
} : {},
|
|
2213
|
+
...params.personaId !== void 0 ? {
|
|
2214
|
+
persona: params.personaId
|
|
2051
2215
|
} : {}
|
|
2052
2216
|
});
|
|
2053
2217
|
threadId = created.id;
|
|
2054
2218
|
} else {
|
|
2055
2219
|
await this.assertOwnsThread(params.actor, threadId);
|
|
2220
|
+
if (params.personaId !== void 0) {
|
|
2221
|
+
await this.pinThreadPersona(threadId, params.personaId);
|
|
2222
|
+
}
|
|
2056
2223
|
}
|
|
2057
2224
|
const input = {
|
|
2058
2225
|
threadId,
|
|
@@ -2060,6 +2227,9 @@ var AgentService = class {
|
|
|
2060
2227
|
userText: params.message,
|
|
2061
2228
|
day: utcDay(),
|
|
2062
2229
|
agentName,
|
|
2230
|
+
...persona !== void 0 ? {
|
|
2231
|
+
persona
|
|
2232
|
+
} : {},
|
|
2063
2233
|
...params.regenerate === true ? {
|
|
2064
2234
|
regenerate: true
|
|
2065
2235
|
} : {},
|
|
@@ -2146,6 +2316,9 @@ var AgentService = class {
|
|
|
2146
2316
|
...input.agentName !== void 0 ? {
|
|
2147
2317
|
agentName: input.agentName
|
|
2148
2318
|
} : {},
|
|
2319
|
+
...input.persona !== void 0 ? {
|
|
2320
|
+
persona: input.persona
|
|
2321
|
+
} : {},
|
|
2149
2322
|
...input.model !== void 0 ? {
|
|
2150
2323
|
model: input.model
|
|
2151
2324
|
} : {},
|
|
@@ -2540,16 +2713,22 @@ var AgentService = class {
|
|
|
2540
2713
|
async updateThread(actor, threadId, patch) {
|
|
2541
2714
|
const title = patch.title !== void 0 ? this.validateTitle(patch.title) : void 0;
|
|
2542
2715
|
await this.assertOwnsThread(actor, threadId);
|
|
2543
|
-
if (patch.defaultAgent === void 0 && patch.model === void 0) {
|
|
2716
|
+
if (patch.defaultAgent === void 0 && patch.model === void 0 && patch.persona === void 0) {
|
|
2544
2717
|
if (title !== void 0) {
|
|
2545
2718
|
await this.store.setTitle(threadId, title);
|
|
2546
2719
|
}
|
|
2547
2720
|
return;
|
|
2548
2721
|
}
|
|
2549
2722
|
if (this.store.updateThread === void 0) {
|
|
2550
|
-
throw new import_common8.NotImplementedException("Setting a thread's defaultAgent or
|
|
2723
|
+
throw new import_common8.NotImplementedException("Setting a thread's defaultAgent, model or persona requires an AgentStore that implements updateThread(); the bound store does not support it.");
|
|
2551
2724
|
}
|
|
2552
2725
|
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);
|
|
2726
|
+
if (patch.persona !== void 0 && patch.persona !== null) {
|
|
2727
|
+
this.deps.resolvePersona({
|
|
2728
|
+
agentName: this.deps.resolveAgent(patch.defaultAgent ?? await this.resolveAgentName(void 0, threadId)).agentName,
|
|
2729
|
+
requested: patch.persona
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2553
2732
|
await this.store.updateThread(threadId, {
|
|
2554
2733
|
...title !== void 0 ? {
|
|
2555
2734
|
title
|
|
@@ -2559,9 +2738,39 @@ var AgentService = class {
|
|
|
2559
2738
|
} : {},
|
|
2560
2739
|
...model !== void 0 ? {
|
|
2561
2740
|
model
|
|
2741
|
+
} : {},
|
|
2742
|
+
...patch.persona !== void 0 ? {
|
|
2743
|
+
persona: patch.persona
|
|
2744
|
+
} : {}
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2747
|
+
/**
|
|
2748
|
+
* The persona this send runs under — see {@link AgentDepsFactory.resolvePersona}. The thread's
|
|
2749
|
+
* pinned persona is read only when the send names none AND the agent has personas to pick from.
|
|
2750
|
+
*/
|
|
2751
|
+
async resolvePersona(agentName, requested, threadId) {
|
|
2752
|
+
const needsThread = requested === void 0 && threadId !== void 0 && this.deps.personaCatalog(agentName).length > 0;
|
|
2753
|
+
return this.deps.resolvePersona({
|
|
2754
|
+
agentName,
|
|
2755
|
+
...requested !== void 0 ? {
|
|
2756
|
+
requested
|
|
2757
|
+
} : {},
|
|
2758
|
+
...needsThread ? {
|
|
2759
|
+
threadPersona: await threadPersona(this.store, threadId)
|
|
2562
2760
|
} : {}
|
|
2563
2761
|
});
|
|
2564
2762
|
}
|
|
2763
|
+
/** Pin `persona` on the thread when it is not already — a no-op on a store that cannot. */
|
|
2764
|
+
async pinThreadPersona(threadId, persona) {
|
|
2765
|
+
if (this.store.updateThread === void 0) {
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2768
|
+
if (await threadPersona(this.store, threadId) !== persona) {
|
|
2769
|
+
await this.store.updateThread(threadId, {
|
|
2770
|
+
persona
|
|
2771
|
+
});
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2565
2774
|
validateTitle(title) {
|
|
2566
2775
|
const trimmed = title.trim();
|
|
2567
2776
|
if (trimmed.length === 0) {
|
|
@@ -2607,6 +2816,7 @@ var AgentService = class {
|
|
|
2607
2816
|
...thread,
|
|
2608
2817
|
defaultAgent: thread.defaultAgent ?? null,
|
|
2609
2818
|
model: thread.model ?? null,
|
|
2819
|
+
persona: thread.persona ?? null,
|
|
2610
2820
|
activeRunId: await this.store.activeRunForThread?.(thread.id) ?? thread.activeRunId ?? null
|
|
2611
2821
|
};
|
|
2612
2822
|
}
|
|
@@ -3008,13 +3218,22 @@ var AgentsController = class {
|
|
|
3008
3218
|
}
|
|
3009
3219
|
list() {
|
|
3010
3220
|
const defaultAgentName = this.depsFactory.defaultAgentName();
|
|
3011
|
-
return this.agents.list().map((definition) =>
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3221
|
+
return this.agents.list().map((definition) => {
|
|
3222
|
+
const personas = this.depsFactory.personaCatalog(definition.name);
|
|
3223
|
+
return {
|
|
3224
|
+
name: definition.name,
|
|
3225
|
+
description: definition.description ?? "",
|
|
3226
|
+
...definition.name === defaultAgentName ? {
|
|
3227
|
+
isDefault: true
|
|
3228
|
+
} : {},
|
|
3229
|
+
...personas.length > 0 ? {
|
|
3230
|
+
personas
|
|
3231
|
+
} : {},
|
|
3232
|
+
...definition.defaultPersona !== void 0 ? {
|
|
3233
|
+
defaultPersona: definition.defaultPersona
|
|
3234
|
+
} : {}
|
|
3235
|
+
};
|
|
3236
|
+
});
|
|
3018
3237
|
}
|
|
3019
3238
|
};
|
|
3020
3239
|
_ts_decorate9([
|
|
@@ -3284,6 +3503,9 @@ var ChatController = class {
|
|
|
3284
3503
|
...body.agent !== void 0 ? {
|
|
3285
3504
|
agentName: body.agent
|
|
3286
3505
|
} : {},
|
|
3506
|
+
...typeof body.persona === "string" && body.persona.length > 0 ? {
|
|
3507
|
+
personaId: body.persona
|
|
3508
|
+
} : {},
|
|
3287
3509
|
...attachments.length > 0 ? {
|
|
3288
3510
|
attachments
|
|
3289
3511
|
} : {},
|
|
@@ -4283,6 +4505,9 @@ var ThreadsController = class {
|
|
|
4283
4505
|
} : {},
|
|
4284
4506
|
..."model" in body ? {
|
|
4285
4507
|
model: body.model ?? null
|
|
4508
|
+
} : {},
|
|
4509
|
+
..."persona" in body ? {
|
|
4510
|
+
persona: body.persona ?? null
|
|
4286
4511
|
} : {}
|
|
4287
4512
|
});
|
|
4288
4513
|
return {
|
|
@@ -4778,6 +5003,7 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
|
|
|
4778
5003
|
}
|
|
4779
5004
|
this.collectContributors(instance);
|
|
4780
5005
|
}
|
|
5006
|
+
this.assertPersonas(this.registry.list());
|
|
4781
5007
|
}
|
|
4782
5008
|
/** Build the internal {@link AgentDefinition} from an `@Agent` instance and its options. */
|
|
4783
5009
|
definitionFrom(instance, meta) {
|
|
@@ -4821,10 +5047,48 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
|
|
|
4821
5047
|
} : {},
|
|
4822
5048
|
...meta.handoff !== void 0 ? {
|
|
4823
5049
|
delegatesTo: this.handoffEdges(meta)
|
|
5050
|
+
} : {},
|
|
5051
|
+
...meta.personas !== void 0 ? {
|
|
5052
|
+
personas: meta.personas
|
|
5053
|
+
} : {},
|
|
5054
|
+
...meta.defaultPersona !== void 0 ? {
|
|
5055
|
+
defaultPersona: meta.defaultPersona
|
|
4824
5056
|
} : {}
|
|
4825
5057
|
};
|
|
4826
5058
|
}
|
|
4827
5059
|
/**
|
|
5060
|
+
* Refuse to boot on a persona declaration that could only fail later, per request: a default that
|
|
5061
|
+
* names no persona, an id declared twice, an alias two personas claim, or an alias that is a real
|
|
5062
|
+
* agent's name (which would never be reached — a registered agent always wins).
|
|
5063
|
+
*/
|
|
5064
|
+
assertPersonas(definitions) {
|
|
5065
|
+
const agentNames = new Set(definitions.map((definition) => definition.name));
|
|
5066
|
+
const aliasOwner = /* @__PURE__ */ new Map();
|
|
5067
|
+
for (const definition of definitions) {
|
|
5068
|
+
const ids = /* @__PURE__ */ new Set();
|
|
5069
|
+
for (const persona of definition.personas ?? []) {
|
|
5070
|
+
if (ids.has(persona.id)) {
|
|
5071
|
+
throw new Error(`Agent "${definition.name}" declares persona "${persona.id}" twice.`);
|
|
5072
|
+
}
|
|
5073
|
+
ids.add(persona.id);
|
|
5074
|
+
for (const alias of persona.aliases ?? []) {
|
|
5075
|
+
const owner = `${definition.name}/${persona.id}`;
|
|
5076
|
+
if (agentNames.has(alias)) {
|
|
5077
|
+
throw new Error(`Persona ${owner} claims the alias "${alias}", which is a registered agent's name.`);
|
|
5078
|
+
}
|
|
5079
|
+
const previous = aliasOwner.get(alias);
|
|
5080
|
+
if (previous !== void 0) {
|
|
5081
|
+
throw new Error(`Personas ${previous} and ${owner} both claim the alias "${alias}".`);
|
|
5082
|
+
}
|
|
5083
|
+
aliasOwner.set(alias, owner);
|
|
5084
|
+
}
|
|
5085
|
+
}
|
|
5086
|
+
if (definition.defaultPersona !== void 0 && !ids.has(definition.defaultPersona)) {
|
|
5087
|
+
throw new Error(`Agent "${definition.name}" names "${definition.defaultPersona}" as its default persona, but declares no persona with that id.`);
|
|
5088
|
+
}
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
5091
|
+
/**
|
|
4828
5092
|
* Resolve each handoff entry to an {@link AgentDelegation} — its target's registered name, plus
|
|
4829
5093
|
* whether that edge detaches (skipping non-agent classes). A bare name is kept for a plain edge
|
|
4830
5094
|
* rather than an always-object form, so a definition an older host built by hand still reads.
|