@dudousxd/nestjs-agent 1.19.2 → 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/README.md +2 -0
- 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 +424 -32
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +41 -5
- package/dist/durable/index.d.ts +41 -5
- package/dist/durable/index.js +482 -90
- 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
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) => {
|
|
@@ -282,12 +291,60 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
|
282
291
|
store;
|
|
283
292
|
runs;
|
|
284
293
|
queue;
|
|
294
|
+
workflows;
|
|
285
295
|
logger = new import_common2.Logger(_AgentRunWorkflow.name);
|
|
286
|
-
constructor(factory, store, runs, queue) {
|
|
296
|
+
constructor(factory, store, runs, queue, workflows) {
|
|
287
297
|
this.factory = factory;
|
|
288
298
|
this.store = store;
|
|
289
299
|
this.runs = runs;
|
|
290
300
|
this.queue = queue;
|
|
301
|
+
this.workflows = workflows;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Start a detached delegation as a TOP-LEVEL run, under an id derived from this run and the call.
|
|
305
|
+
*
|
|
306
|
+
* Not `ctx.startChild`, because the runtime treats a `spawn:` child as part of its parent: a Stop
|
|
307
|
+
* on the parent cascades to every run it spawned. A detached run is exactly the one the person did
|
|
308
|
+
* NOT ask to stop — they stopped the turn they were watching, and the work it handed off is meant
|
|
309
|
+
* to outlive that turn (it is meant to outlive it finishing, after all). Cascaded, it died parked
|
|
310
|
+
* on its approval, where its body never runs again, so nothing settled the delegation and the card
|
|
311
|
+
* said "started" for ever. The agent-level edge stays: the child's input still carries
|
|
312
|
+
* `parentRunId`, which is what the run rows record.
|
|
313
|
+
*
|
|
314
|
+
* Journaled as one `detach:<toolCallId>` localStep whose body runs outside the ambient workflow
|
|
315
|
+
* ctx (so the start goes to the engine and writes no position in this run). Its recorded result
|
|
316
|
+
* keeps a replay from starting it again, and a body re-run after a crash between the start and
|
|
317
|
+
* the checkpoint asks for the same id, which the runtime answers with the run it already has.
|
|
318
|
+
* The child is stamped with this run's namespace, as `ctx.startChild` would have stamped it, so an
|
|
319
|
+
* operator re-driving a tenant's turn does not move its delegate off that tenant's workers.
|
|
320
|
+
*/
|
|
321
|
+
startDetached(ctx, toolCallId2, childInput) {
|
|
322
|
+
return stepOf(ctx)(`detach:${toolCallId2}`, async () => {
|
|
323
|
+
const workflows = this.workflows;
|
|
324
|
+
if (workflows === void 0) {
|
|
325
|
+
throw new Error("A detached delegation needs WorkflowService (DurableModule) to start its run, and none is bound.");
|
|
326
|
+
}
|
|
327
|
+
const childRunId = `${ctx.runId}.detached.${toolCallId2}`;
|
|
328
|
+
const namespace = await this.namespaceOf(ctx.runId);
|
|
329
|
+
try {
|
|
330
|
+
await workflows.start(_AgentRunWorkflow, childInput, childRunId, namespace !== void 0 ? {
|
|
331
|
+
namespace
|
|
332
|
+
} : void 0);
|
|
333
|
+
} catch (error) {
|
|
334
|
+
if (!(0, import_nestjs_durable_core2.isWorkflowControlFlowSignal)(error)) {
|
|
335
|
+
throw error;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return childRunId;
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
/** The namespace this run is stamped with, where the runtime can say. */
|
|
342
|
+
async namespaceOf(runId) {
|
|
343
|
+
try {
|
|
344
|
+
return (await this.runs?.getRunDetail(runId))?.run.namespace;
|
|
345
|
+
} catch {
|
|
346
|
+
return void 0;
|
|
347
|
+
}
|
|
291
348
|
}
|
|
292
349
|
/**
|
|
293
350
|
* Move the thread past this settling turn — to the next queued message, started here as a
|
|
@@ -488,6 +545,10 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
|
488
545
|
// OWN runId, and that is what `runForToolCall` answers with.
|
|
489
546
|
// - `deliverTo` instead. The parent's tool result is a receipt, so the answer needs an
|
|
490
547
|
// address of its own, and by the time it exists nobody is holding one.
|
|
548
|
+
// - no runtime parent. A Stop on this turn must not reach the work it handed off, so the
|
|
549
|
+
// run is started on its own (`startDetached`), not as a `spawn:` child. A run that
|
|
550
|
+
// journaled the `spawn:` before this release replays it: `ctx.patched` answers false at a
|
|
551
|
+
// position that already holds one.
|
|
491
552
|
startAgent: /* @__PURE__ */ __name(async ({ agentName, task, toolCallId: toolCallId2 }) => {
|
|
492
553
|
const subThreadId = await stepOf(ctx)(`subthread:${agentName}`, async () => {
|
|
493
554
|
const thread = await this.store.createThread({
|
|
@@ -496,7 +557,7 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
|
496
557
|
});
|
|
497
558
|
return thread.id;
|
|
498
559
|
});
|
|
499
|
-
const
|
|
560
|
+
const childInput = {
|
|
500
561
|
agentName,
|
|
501
562
|
threadId: subThreadId,
|
|
502
563
|
actor: input.actor,
|
|
@@ -509,9 +570,14 @@ var AgentRunWorkflow = class _AgentRunWorkflow {
|
|
|
509
570
|
threadId: input.threadId,
|
|
510
571
|
toolCallId: toolCallId2
|
|
511
572
|
}
|
|
512
|
-
}
|
|
573
|
+
};
|
|
574
|
+
if (!await ctx.patched("agent:detached-unlinked")) {
|
|
575
|
+
return {
|
|
576
|
+
runId: await ctx.startChild(_AgentRunWorkflow, childInput)
|
|
577
|
+
};
|
|
578
|
+
}
|
|
513
579
|
return {
|
|
514
|
-
runId:
|
|
580
|
+
runId: await this.startDetached(ctx, toolCallId2, childInput)
|
|
515
581
|
};
|
|
516
582
|
}, "startAgent"),
|
|
517
583
|
// The two long steps as engine-dispatched `ctx.step`s, so a turn isn't pinned to this
|
|
@@ -624,12 +690,14 @@ AgentRunWorkflow = _ts_decorate2([
|
|
|
624
690
|
_ts_param2(2, (0, import_common2.Inject)(import_nestjs_durable2.RUN_GATEWAY)),
|
|
625
691
|
_ts_param2(3, (0, import_common2.Optional)()),
|
|
626
692
|
_ts_param2(3, (0, import_common2.Inject)(AGENT_CHAT_QUEUE)),
|
|
693
|
+
_ts_param2(4, (0, import_common2.Optional)()),
|
|
627
694
|
_ts_metadata2("design:type", Function),
|
|
628
695
|
_ts_metadata2("design:paramtypes", [
|
|
629
696
|
typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory,
|
|
630
697
|
typeof AgentStore === "undefined" ? Object : AgentStore,
|
|
631
698
|
typeof RunGateway === "undefined" ? Object : RunGateway,
|
|
632
|
-
typeof ChatQueueService === "undefined" ? Object : ChatQueueService
|
|
699
|
+
typeof ChatQueueService === "undefined" ? Object : ChatQueueService,
|
|
700
|
+
typeof import_nestjs_durable2.WorkflowService === "undefined" ? Object : import_nestjs_durable2.WorkflowService
|
|
633
701
|
])
|
|
634
702
|
], AgentRunWorkflow);
|
|
635
703
|
function isSignalTimeout(error) {
|
|
@@ -750,6 +818,20 @@ function isThreadTurn(input) {
|
|
|
750
818
|
return typeof input === "object" && input !== null && input.sinkRunId === void 0 && input.deliverTo === void 0;
|
|
751
819
|
}
|
|
752
820
|
__name(isThreadTurn, "isThreadTurn");
|
|
821
|
+
function deliveryOf(input) {
|
|
822
|
+
if (typeof input !== "object" || input === null) {
|
|
823
|
+
return void 0;
|
|
824
|
+
}
|
|
825
|
+
const { deliverTo, agentName } = input;
|
|
826
|
+
if (typeof deliverTo !== "object" || deliverTo === null || typeof deliverTo.threadId !== "string" || typeof deliverTo.toolCallId !== "string") {
|
|
827
|
+
return void 0;
|
|
828
|
+
}
|
|
829
|
+
return {
|
|
830
|
+
delivery: deliverTo,
|
|
831
|
+
agent: typeof agentName === "string" ? agentName : "default"
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
__name(deliveryOf, "deliveryOf");
|
|
753
835
|
var DurableAgentRunner = class _DurableAgentRunner {
|
|
754
836
|
static {
|
|
755
837
|
__name(this, "DurableAgentRunner");
|
|
@@ -829,11 +911,16 @@ var DurableAgentRunner = class _DurableAgentRunner {
|
|
|
829
911
|
*/
|
|
830
912
|
async cancel(runId) {
|
|
831
913
|
this.logger.log(`cancelling agent run ${runId}`);
|
|
832
|
-
const
|
|
914
|
+
const detail = await this.detailOf(runId);
|
|
915
|
+
const input = detail?.run.input;
|
|
833
916
|
const threadId = threadOfRun(input);
|
|
834
917
|
await this.runs.cancel(runId, {
|
|
835
918
|
compensate: true
|
|
836
919
|
});
|
|
920
|
+
await this.settleCancelledDelegation(runId, input);
|
|
921
|
+
for (const childId of detail?.children ?? []) {
|
|
922
|
+
await this.settleCascadedDetached(childId);
|
|
923
|
+
}
|
|
837
924
|
const writer = await this.sink.open(runId);
|
|
838
925
|
if (threadId !== void 0) {
|
|
839
926
|
if (this.queue?.supported === true && isThreadTurn(input)) {
|
|
@@ -864,7 +951,57 @@ var DurableAgentRunner = class _DurableAgentRunner {
|
|
|
864
951
|
});
|
|
865
952
|
}
|
|
866
953
|
/**
|
|
867
|
-
*
|
|
954
|
+
* Settle the delegation a DETACHED run was started for, once it has been stopped. A run parked on
|
|
955
|
+
* a human never runs its body again — the runtime settles it from outside — so the body's own
|
|
956
|
+
* `deliver:detached:unsettled` is not reached, and without this the card in the calling thread says
|
|
957
|
+
* "started" for ever. Settles once (see `settleUnsettledDelegation`), so a body that did observe
|
|
958
|
+
* the Stop and a run that had already answered are both left as they are. A no-op for any run
|
|
959
|
+
* that does not deliver into another thread.
|
|
960
|
+
*/
|
|
961
|
+
async settleCancelledDelegation(runId, input) {
|
|
962
|
+
const target = deliveryOf(input);
|
|
963
|
+
if (target === void 0) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
try {
|
|
967
|
+
await (0, import_nestjs_agent_core4.settleUnsettledDelegation)({
|
|
968
|
+
store: this.store,
|
|
969
|
+
delivery: target.delivery,
|
|
970
|
+
agent: target.agent,
|
|
971
|
+
runId,
|
|
972
|
+
status: "cancelled"
|
|
973
|
+
});
|
|
974
|
+
} catch (error) {
|
|
975
|
+
this.logger.error(`could not settle the delegation of cancelled run ${runId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* A detached child the runtime cancelled along with its parent (a `spawn:` journaled before
|
|
980
|
+
* detached runs were started on their own): end it the way a Stop on its own id would have.
|
|
981
|
+
*/
|
|
982
|
+
async settleCascadedDetached(childId) {
|
|
983
|
+
const child = await this.detailOf(childId);
|
|
984
|
+
const status = child?.run.status;
|
|
985
|
+
if (child === void 0 || deliveryOf(child.run.input) === void 0 || status !== "cancelled" && status !== "cancelling") {
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
const subThreadId = threadOfRun(child.run.input);
|
|
989
|
+
if (subThreadId !== void 0) {
|
|
990
|
+
await (0, import_nestjs_agent_core4.releaseThreadRun)(this.store, subThreadId, childId);
|
|
991
|
+
}
|
|
992
|
+
await this.settleCancelledDelegation(childId, child.run.input);
|
|
993
|
+
const writer = await this.sink.open(childId);
|
|
994
|
+
await writer.write((0, import_nestjs_agent_core4.encodeStreamEvent)({
|
|
995
|
+
kind: "cancelled"
|
|
996
|
+
}));
|
|
997
|
+
await writer.end();
|
|
998
|
+
await this.store.recordRunEnd?.({
|
|
999
|
+
runId: childId,
|
|
1000
|
+
status: "cancelled"
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* What the runtime recorded for a run — the input it was started with (which thread it was streaming), its status and the runs it spawned — or `undefined` where the gateway cannot say.
|
|
868
1005
|
*
|
|
869
1006
|
* A read failure answers `undefined` instead of propagating, the same posture `AgentRunWorkflow`
|
|
870
1007
|
* takes for its own cancel observation: failing to ask is not an answer worth failing a cancel
|
|
@@ -873,9 +1010,9 @@ var DurableAgentRunner = class _DurableAgentRunner {
|
|
|
873
1010
|
* with a subscriber holding a stream that never settles, from the one call whose entire job is to
|
|
874
1011
|
* make a run stop.
|
|
875
1012
|
*/
|
|
876
|
-
async
|
|
1013
|
+
async detailOf(runId) {
|
|
877
1014
|
try {
|
|
878
|
-
return
|
|
1015
|
+
return await this.runs.getRunDetail(runId) ?? void 0;
|
|
879
1016
|
} catch {
|
|
880
1017
|
return void 0;
|
|
881
1018
|
}
|
|
@@ -1090,8 +1227,64 @@ var AgentDepsFactory2 = class {
|
|
|
1090
1227
|
...delegated
|
|
1091
1228
|
];
|
|
1092
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
|
+
}
|
|
1093
1279
|
forAgent(agentName) {
|
|
1094
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
|
+
}
|
|
1095
1288
|
const definition = this.agents.get(name);
|
|
1096
1289
|
const toolAllowList = this.effectiveTools(definition);
|
|
1097
1290
|
const followUpsCount = this.followUpsCount();
|
|
@@ -1156,6 +1349,9 @@ var AgentDepsFactory2 = class {
|
|
|
1156
1349
|
...toolAllowList !== void 0 ? {
|
|
1157
1350
|
toolAllowList
|
|
1158
1351
|
} : {},
|
|
1352
|
+
...definition?.personas !== void 0 ? {
|
|
1353
|
+
personas: definition.personas
|
|
1354
|
+
} : {},
|
|
1159
1355
|
...this.options.toolTimeoutMs !== void 0 ? {
|
|
1160
1356
|
toolTimeoutMs: this.options.toolTimeoutMs
|
|
1161
1357
|
} : {},
|
|
@@ -1174,6 +1370,45 @@ var AgentDepsFactory2 = class {
|
|
|
1174
1370
|
};
|
|
1175
1371
|
}
|
|
1176
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
|
+
/**
|
|
1177
1412
|
* The ceiling on how much of a thread this agent's turns carry, most specific wins: the agent's
|
|
1178
1413
|
* own `@Agent({ history })`, else a module-wide custom `historyPolicy`, else the module-wide
|
|
1179
1414
|
* `history` window. Undefined → unbounded, the behaviour of a module that configures none.
|
|
@@ -1254,6 +1489,18 @@ var import_common8 = require("@nestjs/common");
|
|
|
1254
1489
|
// src/queue/chat-queue.service.ts
|
|
1255
1490
|
var import_nestjs_agent_core8 = require("@dudousxd/nestjs-agent-core");
|
|
1256
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
|
|
1257
1504
|
function _ts_decorate6(decorators, target, key, desc) {
|
|
1258
1505
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1259
1506
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
@@ -1290,18 +1537,20 @@ var ChatQueueService2 = class _ChatQueueService {
|
|
|
1290
1537
|
quotaProvider;
|
|
1291
1538
|
options;
|
|
1292
1539
|
staging;
|
|
1540
|
+
deps;
|
|
1293
1541
|
logger = new import_common6.Logger(_ChatQueueService.name);
|
|
1294
1542
|
/**
|
|
1295
1543
|
* Runs this process claimed a thread for and has not handed to a runner yet. A holder in here is
|
|
1296
1544
|
* alive whatever the runner says — it simply has not registered the run yet.
|
|
1297
1545
|
*/
|
|
1298
1546
|
starting = /* @__PURE__ */ new Set();
|
|
1299
|
-
constructor(store, sink, quotaProvider, options, staging) {
|
|
1547
|
+
constructor(store, sink, quotaProvider, options, staging, deps) {
|
|
1300
1548
|
this.store = store;
|
|
1301
1549
|
this.sink = sink;
|
|
1302
1550
|
this.quotaProvider = quotaProvider;
|
|
1303
1551
|
this.options = options;
|
|
1304
1552
|
this.staging = staging;
|
|
1553
|
+
this.deps = deps;
|
|
1305
1554
|
}
|
|
1306
1555
|
/** Whether the bound store can hold a queue. Without one, a busy thread is not queued. */
|
|
1307
1556
|
get supported() {
|
|
@@ -1599,13 +1848,17 @@ var ChatQueueService2 = class _ChatQueueService {
|
|
|
1599
1848
|
*/
|
|
1600
1849
|
async inputFor(message) {
|
|
1601
1850
|
const attachments = await this.freshAttachments(message.actor, message.attachments ?? []);
|
|
1851
|
+
const { agentName, persona } = await this.targetFor(message);
|
|
1602
1852
|
return {
|
|
1603
1853
|
threadId: message.threadId,
|
|
1604
1854
|
actor: message.actor,
|
|
1605
1855
|
userText: message.content,
|
|
1606
1856
|
day: utcDay(),
|
|
1607
|
-
...
|
|
1608
|
-
agentName
|
|
1857
|
+
...agentName !== void 0 ? {
|
|
1858
|
+
agentName
|
|
1859
|
+
} : {},
|
|
1860
|
+
...persona !== void 0 ? {
|
|
1861
|
+
persona
|
|
1609
1862
|
} : {},
|
|
1610
1863
|
...attachments.length > 0 ? {
|
|
1611
1864
|
attachments
|
|
@@ -1618,6 +1871,37 @@ var ChatQueueService2 = class _ChatQueueService {
|
|
|
1618
1871
|
} : {}
|
|
1619
1872
|
};
|
|
1620
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
|
+
}
|
|
1621
1905
|
async freshAttachments(actor, attachments) {
|
|
1622
1906
|
const staging = this.staging;
|
|
1623
1907
|
if (attachments.length === 0 || staging === void 0 || typeof staging.resolve !== "function") {
|
|
@@ -1645,13 +1929,16 @@ ChatQueueService2 = _ts_decorate6([
|
|
|
1645
1929
|
_ts_param5(3, (0, import_common6.Inject)(import_nestjs_agent_core8.AGENT_OPTIONS)),
|
|
1646
1930
|
_ts_param5(4, (0, import_common6.Optional)()),
|
|
1647
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)),
|
|
1648
1934
|
_ts_metadata5("design:type", Function),
|
|
1649
1935
|
_ts_metadata5("design:paramtypes", [
|
|
1650
1936
|
typeof AgentStore === "undefined" ? Object : AgentStore,
|
|
1651
1937
|
typeof TokenStreamSink === "undefined" ? Object : TokenStreamSink,
|
|
1652
1938
|
typeof QuotaProvider === "undefined" ? Object : QuotaProvider,
|
|
1653
1939
|
typeof Pick === "undefined" ? Object : Pick,
|
|
1654
|
-
typeof AttachmentStagingStore === "undefined" ? Object : AttachmentStagingStore
|
|
1940
|
+
typeof AttachmentStagingStore === "undefined" ? Object : AttachmentStagingStore,
|
|
1941
|
+
typeof AgentDepsFactory === "undefined" ? Object : AgentDepsFactory
|
|
1655
1942
|
])
|
|
1656
1943
|
], ChatQueueService2);
|
|
1657
1944
|
function pauseReasonFor(outcome, head) {
|
|
@@ -1905,7 +2192,9 @@ var AgentService = class {
|
|
|
1905
2192
|
*/
|
|
1906
2193
|
async send(params) {
|
|
1907
2194
|
await this.assertWithinQuota(params.actor);
|
|
1908
|
-
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);
|
|
1909
2198
|
const model = await this.resolveModel(params.actor, agentName, params.model, params.threadId);
|
|
1910
2199
|
const attachments = await this.resolveAttachments(params.actor, params.attachments ?? []);
|
|
1911
2200
|
let threadId = params.threadId;
|
|
@@ -1920,11 +2209,17 @@ var AgentService = class {
|
|
|
1920
2209
|
} : {},
|
|
1921
2210
|
...params.newThreadId !== void 0 ? {
|
|
1922
2211
|
id: params.newThreadId
|
|
2212
|
+
} : {},
|
|
2213
|
+
...params.personaId !== void 0 ? {
|
|
2214
|
+
persona: params.personaId
|
|
1923
2215
|
} : {}
|
|
1924
2216
|
});
|
|
1925
2217
|
threadId = created.id;
|
|
1926
2218
|
} else {
|
|
1927
2219
|
await this.assertOwnsThread(params.actor, threadId);
|
|
2220
|
+
if (params.personaId !== void 0) {
|
|
2221
|
+
await this.pinThreadPersona(threadId, params.personaId);
|
|
2222
|
+
}
|
|
1928
2223
|
}
|
|
1929
2224
|
const input = {
|
|
1930
2225
|
threadId,
|
|
@@ -1932,6 +2227,9 @@ var AgentService = class {
|
|
|
1932
2227
|
userText: params.message,
|
|
1933
2228
|
day: utcDay(),
|
|
1934
2229
|
agentName,
|
|
2230
|
+
...persona !== void 0 ? {
|
|
2231
|
+
persona
|
|
2232
|
+
} : {},
|
|
1935
2233
|
...params.regenerate === true ? {
|
|
1936
2234
|
regenerate: true
|
|
1937
2235
|
} : {},
|
|
@@ -2018,6 +2316,9 @@ var AgentService = class {
|
|
|
2018
2316
|
...input.agentName !== void 0 ? {
|
|
2019
2317
|
agentName: input.agentName
|
|
2020
2318
|
} : {},
|
|
2319
|
+
...input.persona !== void 0 ? {
|
|
2320
|
+
persona: input.persona
|
|
2321
|
+
} : {},
|
|
2021
2322
|
...input.model !== void 0 ? {
|
|
2022
2323
|
model: input.model
|
|
2023
2324
|
} : {},
|
|
@@ -2412,16 +2713,22 @@ var AgentService = class {
|
|
|
2412
2713
|
async updateThread(actor, threadId, patch) {
|
|
2413
2714
|
const title = patch.title !== void 0 ? this.validateTitle(patch.title) : void 0;
|
|
2414
2715
|
await this.assertOwnsThread(actor, threadId);
|
|
2415
|
-
if (patch.defaultAgent === void 0 && patch.model === void 0) {
|
|
2716
|
+
if (patch.defaultAgent === void 0 && patch.model === void 0 && patch.persona === void 0) {
|
|
2416
2717
|
if (title !== void 0) {
|
|
2417
2718
|
await this.store.setTitle(threadId, title);
|
|
2418
2719
|
}
|
|
2419
2720
|
return;
|
|
2420
2721
|
}
|
|
2421
2722
|
if (this.store.updateThread === void 0) {
|
|
2422
|
-
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.");
|
|
2423
2724
|
}
|
|
2424
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
|
+
}
|
|
2425
2732
|
await this.store.updateThread(threadId, {
|
|
2426
2733
|
...title !== void 0 ? {
|
|
2427
2734
|
title
|
|
@@ -2431,9 +2738,39 @@ var AgentService = class {
|
|
|
2431
2738
|
} : {},
|
|
2432
2739
|
...model !== void 0 ? {
|
|
2433
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)
|
|
2434
2760
|
} : {}
|
|
2435
2761
|
});
|
|
2436
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
|
+
}
|
|
2437
2774
|
validateTitle(title) {
|
|
2438
2775
|
const trimmed = title.trim();
|
|
2439
2776
|
if (trimmed.length === 0) {
|
|
@@ -2479,6 +2816,7 @@ var AgentService = class {
|
|
|
2479
2816
|
...thread,
|
|
2480
2817
|
defaultAgent: thread.defaultAgent ?? null,
|
|
2481
2818
|
model: thread.model ?? null,
|
|
2819
|
+
persona: thread.persona ?? null,
|
|
2482
2820
|
activeRunId: await this.store.activeRunForThread?.(thread.id) ?? thread.activeRunId ?? null
|
|
2483
2821
|
};
|
|
2484
2822
|
}
|
|
@@ -2880,13 +3218,22 @@ var AgentsController = class {
|
|
|
2880
3218
|
}
|
|
2881
3219
|
list() {
|
|
2882
3220
|
const defaultAgentName = this.depsFactory.defaultAgentName();
|
|
2883
|
-
return this.agents.list().map((definition) =>
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
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
|
+
});
|
|
2890
3237
|
}
|
|
2891
3238
|
};
|
|
2892
3239
|
_ts_decorate9([
|
|
@@ -3156,6 +3503,9 @@ var ChatController = class {
|
|
|
3156
3503
|
...body.agent !== void 0 ? {
|
|
3157
3504
|
agentName: body.agent
|
|
3158
3505
|
} : {},
|
|
3506
|
+
...typeof body.persona === "string" && body.persona.length > 0 ? {
|
|
3507
|
+
personaId: body.persona
|
|
3508
|
+
} : {},
|
|
3159
3509
|
...attachments.length > 0 ? {
|
|
3160
3510
|
attachments
|
|
3161
3511
|
} : {},
|
|
@@ -4155,6 +4505,9 @@ var ThreadsController = class {
|
|
|
4155
4505
|
} : {},
|
|
4156
4506
|
..."model" in body ? {
|
|
4157
4507
|
model: body.model ?? null
|
|
4508
|
+
} : {},
|
|
4509
|
+
..."persona" in body ? {
|
|
4510
|
+
persona: body.persona ?? null
|
|
4158
4511
|
} : {}
|
|
4159
4512
|
});
|
|
4160
4513
|
return {
|
|
@@ -4650,6 +5003,7 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
|
|
|
4650
5003
|
}
|
|
4651
5004
|
this.collectContributors(instance);
|
|
4652
5005
|
}
|
|
5006
|
+
this.assertPersonas(this.registry.list());
|
|
4653
5007
|
}
|
|
4654
5008
|
/** Build the internal {@link AgentDefinition} from an `@Agent` instance and its options. */
|
|
4655
5009
|
definitionFrom(instance, meta) {
|
|
@@ -4693,10 +5047,48 @@ var AgentDiscoveryService = class _AgentDiscoveryService {
|
|
|
4693
5047
|
} : {},
|
|
4694
5048
|
...meta.handoff !== void 0 ? {
|
|
4695
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
|
|
4696
5056
|
} : {}
|
|
4697
5057
|
};
|
|
4698
5058
|
}
|
|
4699
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
|
+
/**
|
|
4700
5092
|
* Resolve each handoff entry to an {@link AgentDelegation} — its target's registered name, plus
|
|
4701
5093
|
* whether that edge detaches (skipping non-agent classes). A bare name is kept for a plain edge
|
|
4702
5094
|
* rather than an always-object form, so a definition an older host built by hand still reads.
|