@dudousxd/nestjs-agent-core 0.1.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.cjs ADDED
@@ -0,0 +1,813 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ AGENT_ACTOR_RESOLVER: () => AGENT_ACTOR_RESOLVER,
25
+ AGENT_DEPS_FACTORY: () => AGENT_DEPS_FACTORY,
26
+ AGENT_DURABLE_RUNNER: () => AGENT_DURABLE_RUNNER,
27
+ AGENT_EMBEDDING_PROVIDER: () => AGENT_EMBEDDING_PROVIDER,
28
+ AGENT_GOVERNANCE_QUERIES: () => AGENT_GOVERNANCE_QUERIES,
29
+ AGENT_MODEL: () => AGENT_MODEL,
30
+ AGENT_OPTIONS: () => AGENT_OPTIONS,
31
+ AGENT_PRICING_STORE: () => AGENT_PRICING_STORE,
32
+ AGENT_QUOTA_STORE: () => AGENT_QUOTA_STORE,
33
+ AGENT_REGISTRY: () => AGENT_REGISTRY,
34
+ AGENT_RETRIEVER: () => AGENT_RETRIEVER,
35
+ AGENT_ROLES_POLICY: () => AGENT_ROLES_POLICY,
36
+ AGENT_RUNNER: () => AGENT_RUNNER,
37
+ AGENT_SINK: () => AGENT_SINK,
38
+ AGENT_STORE: () => AGENT_STORE,
39
+ AGENT_TOOL_REGISTRY: () => AGENT_TOOL_REGISTRY,
40
+ AgentRegistry: () => AgentRegistry,
41
+ AgentStreamError: () => AgentStreamError,
42
+ DefaultRolesPolicy: () => DefaultRolesPolicy,
43
+ QuotaExceededError: () => QuotaExceededError,
44
+ ToolForbiddenError: () => ToolForbiddenError,
45
+ ToolInputInvalidError: () => ToolInputInvalidError,
46
+ ToolNotFoundError: () => ToolNotFoundError,
47
+ ToolRegistry: () => ToolRegistry,
48
+ filterToolsByRole: () => filterToolsByRole,
49
+ personaFilterTools: () => personaFilterTools,
50
+ publishAgentDelegated: () => publishAgentDelegated,
51
+ publishAgentMessage: () => publishAgentMessage,
52
+ publishAgentQuotaExceeded: () => publishAgentQuotaExceeded,
53
+ publishAgentRetrieved: () => publishAgentRetrieved,
54
+ publishAgentRunFailed: () => publishAgentRunFailed,
55
+ publishAgentRunFinished: () => publishAgentRunFinished,
56
+ publishAgentRunStarted: () => publishAgentRunStarted,
57
+ publishAgentToolCall: () => publishAgentToolCall,
58
+ runAgentLoop: () => runAgentLoop,
59
+ seedModelPrices: () => seedModelPrices
60
+ });
61
+ module.exports = __toCommonJS(index_exports);
62
+
63
+ // src/tokens.ts
64
+ var AGENT_OPTIONS = Symbol.for("@dudousxd/nestjs-agent:options");
65
+ var AGENT_STORE = Symbol.for("@dudousxd/nestjs-agent:store");
66
+ var AGENT_RUNNER = Symbol.for("@dudousxd/nestjs-agent:runner");
67
+ var AGENT_DURABLE_RUNNER = Symbol.for("@dudousxd/nestjs-agent:durable-runner");
68
+ var AGENT_SINK = Symbol.for("@dudousxd/nestjs-agent:sink");
69
+ var AGENT_MODEL = Symbol.for("@dudousxd/nestjs-agent:model");
70
+ var AGENT_ROLES_POLICY = Symbol.for("@dudousxd/nestjs-agent:roles-policy");
71
+ var AGENT_QUOTA_STORE = Symbol.for("@dudousxd/nestjs-agent:quota-store");
72
+ var AGENT_TOOL_REGISTRY = Symbol.for("@dudousxd/nestjs-agent:tool-registry");
73
+ var AGENT_REGISTRY = Symbol.for("@dudousxd/nestjs-agent:agent-registry");
74
+ var AGENT_ACTOR_RESOLVER = Symbol.for("@dudousxd/nestjs-agent:actor-resolver");
75
+ var AGENT_GOVERNANCE_QUERIES = Symbol.for("@dudousxd/nestjs-agent:governance-queries");
76
+ var AGENT_PRICING_STORE = Symbol.for("@dudousxd/nestjs-agent:pricing-store");
77
+ var AGENT_RETRIEVER = Symbol.for("@dudousxd/nestjs-agent:retriever");
78
+ var AGENT_EMBEDDING_PROVIDER = Symbol.for("@dudousxd/nestjs-agent:embedding-provider");
79
+ var AGENT_DEPS_FACTORY = Symbol.for("@dudousxd/nestjs-agent:deps-factory");
80
+
81
+ // src/spi/token-stream-sink.ts
82
+ var AgentStreamError = class extends Error {
83
+ static {
84
+ __name(this, "AgentStreamError");
85
+ }
86
+ code;
87
+ constructor(error) {
88
+ super(error.message);
89
+ this.name = "AgentStreamError";
90
+ this.code = error.code;
91
+ }
92
+ };
93
+
94
+ // src/spi/pricing-store.ts
95
+ async function seedModelPrices(store, prices) {
96
+ for (const price of prices) {
97
+ await store.upsertModelPrice(price);
98
+ }
99
+ }
100
+ __name(seedModelPrices, "seedModelPrices");
101
+
102
+ // src/personas.ts
103
+ async function filterToolsByRole(tools, actor, policy) {
104
+ const checked = await Promise.all(tools.map(async (tool) => ({
105
+ tool,
106
+ allowed: await policy.can(actor, tool)
107
+ })));
108
+ return checked.filter((entry) => entry.allowed).map((entry) => entry.tool);
109
+ }
110
+ __name(filterToolsByRole, "filterToolsByRole");
111
+ function personaFilterTools(tools, allowedTools) {
112
+ if (allowedTools === void 0) {
113
+ return tools;
114
+ }
115
+ const allowed = new Set(allowedTools);
116
+ return tools.filter((tool) => allowed.has(tool.name));
117
+ }
118
+ __name(personaFilterTools, "personaFilterTools");
119
+
120
+ // src/agent-registry.ts
121
+ var AgentRegistry = class {
122
+ static {
123
+ __name(this, "AgentRegistry");
124
+ }
125
+ definitions = /* @__PURE__ */ new Map();
126
+ register(definition) {
127
+ this.definitions.set(definition.name, definition);
128
+ }
129
+ get(name) {
130
+ return this.definitions.get(name);
131
+ }
132
+ has(name) {
133
+ return this.definitions.has(name);
134
+ }
135
+ list() {
136
+ return [
137
+ ...this.definitions.values()
138
+ ];
139
+ }
140
+ };
141
+
142
+ // src/tool-registry.ts
143
+ var ToolForbiddenError = class extends Error {
144
+ static {
145
+ __name(this, "ToolForbiddenError");
146
+ }
147
+ toolName;
148
+ constructor(toolName) {
149
+ super(`Tool "${toolName}" is not allowed for this role`), this.toolName = toolName;
150
+ this.name = "ToolForbiddenError";
151
+ }
152
+ };
153
+ var ToolNotFoundError = class extends Error {
154
+ static {
155
+ __name(this, "ToolNotFoundError");
156
+ }
157
+ toolName;
158
+ constructor(toolName) {
159
+ super(`Tool "${toolName}" is not registered`), this.toolName = toolName;
160
+ this.name = "ToolNotFoundError";
161
+ }
162
+ };
163
+ var ToolInputInvalidError = class extends Error {
164
+ static {
165
+ __name(this, "ToolInputInvalidError");
166
+ }
167
+ toolName;
168
+ issues;
169
+ constructor(toolName, issues) {
170
+ super(`Invalid input for tool "${toolName}": ${issues.map((issue) => issue.message).join("; ")}`), this.toolName = toolName, this.issues = issues;
171
+ this.name = "ToolInputInvalidError";
172
+ }
173
+ };
174
+ var ToolRegistry = class {
175
+ static {
176
+ __name(this, "ToolRegistry");
177
+ }
178
+ entries = /* @__PURE__ */ new Map();
179
+ register(spec, handler) {
180
+ this.entries.set(spec.name, {
181
+ spec,
182
+ handler
183
+ });
184
+ }
185
+ has(name) {
186
+ return this.entries.has(name);
187
+ }
188
+ spec(name) {
189
+ return this.entries.get(name)?.spec;
190
+ }
191
+ allSpecs() {
192
+ return [
193
+ ...this.entries.values()
194
+ ].map((entry) => entry.spec);
195
+ }
196
+ /** The tools to offer the model for this actor+persona, after the two filter layers. */
197
+ async definitionsFor(actor, policy, allowedTools) {
198
+ const roleScoped = await filterToolsByRole(this.allSpecs(), actor, policy);
199
+ const personaScoped = personaFilterTools(roleScoped, allowedTools);
200
+ return personaScoped.map((spec) => ({
201
+ name: spec.name,
202
+ kind: spec.kind,
203
+ description: spec.description,
204
+ inputSchema: spec.inputSchema
205
+ }));
206
+ }
207
+ /** Run a tool. Re-checks the role (defense-in-depth) and re-parses the input via Zod. */
208
+ async invoke(name, input, ctx, policy) {
209
+ const entry = this.entries.get(name);
210
+ if (entry === void 0) {
211
+ throw new ToolNotFoundError(name);
212
+ }
213
+ if (!await policy.can(ctx.actor, entry.spec)) {
214
+ throw new ToolForbiddenError(name);
215
+ }
216
+ const validation = await entry.spec.inputSchema["~standard"].validate(input);
217
+ if (validation.issues !== void 0) {
218
+ throw new ToolInputInvalidError(name, validation.issues);
219
+ }
220
+ return entry.handler.execute(validation.value, ctx);
221
+ }
222
+ };
223
+ var DefaultRolesPolicy = class {
224
+ static {
225
+ __name(this, "DefaultRolesPolicy");
226
+ }
227
+ defaultRoles;
228
+ constructor(defaultRoles = [
229
+ "ADMIN"
230
+ ]) {
231
+ this.defaultRoles = defaultRoles;
232
+ }
233
+ can(actor, tool) {
234
+ const allowed = tool.roles ?? this.defaultRoles;
235
+ return (actor.roles ?? []).some((role) => allowed.includes(role));
236
+ }
237
+ };
238
+
239
+ // src/diagnostics.ts
240
+ var import_nestjs_diagnostics = require("@dudousxd/nestjs-diagnostics");
241
+ function publishAgentRunStarted(payload) {
242
+ (0, import_nestjs_diagnostics.emit)("agent", "run.started", payload);
243
+ }
244
+ __name(publishAgentRunStarted, "publishAgentRunStarted");
245
+ function publishAgentMessage(payload) {
246
+ (0, import_nestjs_diagnostics.emit)("agent", "message", payload);
247
+ }
248
+ __name(publishAgentMessage, "publishAgentMessage");
249
+ function publishAgentToolCall(payload) {
250
+ (0, import_nestjs_diagnostics.emit)("agent", "tool-call", payload);
251
+ }
252
+ __name(publishAgentToolCall, "publishAgentToolCall");
253
+ function publishAgentQuotaExceeded(payload) {
254
+ (0, import_nestjs_diagnostics.emit)("agent", "quota.exceeded", payload);
255
+ }
256
+ __name(publishAgentQuotaExceeded, "publishAgentQuotaExceeded");
257
+ function publishAgentRunFinished(payload) {
258
+ (0, import_nestjs_diagnostics.emit)("agent", "run.finished", payload);
259
+ }
260
+ __name(publishAgentRunFinished, "publishAgentRunFinished");
261
+ function publishAgentRunFailed(payload) {
262
+ (0, import_nestjs_diagnostics.emit)("agent", "run.failed", payload);
263
+ }
264
+ __name(publishAgentRunFailed, "publishAgentRunFailed");
265
+ function publishAgentDelegated(payload) {
266
+ (0, import_nestjs_diagnostics.emit)("agent", "delegated", payload);
267
+ }
268
+ __name(publishAgentDelegated, "publishAgentDelegated");
269
+ function publishAgentRetrieved(payload) {
270
+ (0, import_nestjs_diagnostics.emit)("agent", "retrieved", payload);
271
+ }
272
+ __name(publishAgentRetrieved, "publishAgentRetrieved");
273
+
274
+ // src/agent-loop.ts
275
+ function buildContextBlock(passages) {
276
+ const items = passages.map((passage, index) => {
277
+ const label = passage.source !== void 0 ? ` (${passage.source})` : "";
278
+ return `[${index + 1}]${label} ${passage.text}`;
279
+ }).join("\n\n");
280
+ return `<retrieved_context>
281
+ ${items}
282
+ </retrieved_context>
283
+ Use the retrieved context above to answer when relevant, and cite sources by their bracket number.`;
284
+ }
285
+ __name(buildContextBlock, "buildContextBlock");
286
+ function intersectAllow(a, b) {
287
+ if (a === void 0) {
288
+ return b;
289
+ }
290
+ if (b === void 0) {
291
+ return a;
292
+ }
293
+ const second = new Set(b);
294
+ return a.filter((name) => second.has(name));
295
+ }
296
+ __name(intersectAllow, "intersectAllow");
297
+ var QuotaExceededError = class extends Error {
298
+ static {
299
+ __name(this, "QuotaExceededError");
300
+ }
301
+ constructor() {
302
+ super("Daily token quota exceeded");
303
+ this.name = "QuotaExceededError";
304
+ }
305
+ };
306
+ var MAX_DELEGATION_DEPTH = 5;
307
+ async function resolvePrompt(prompt, ctx) {
308
+ return typeof prompt === "function" ? prompt(ctx) : prompt;
309
+ }
310
+ __name(resolvePrompt, "resolvePrompt");
311
+ async function resolveSystemPrompt(deps, input) {
312
+ const base = {
313
+ actor: input.actor,
314
+ ...input.persona !== void 0 ? {
315
+ persona: input.persona
316
+ } : {},
317
+ ...input.pageContext !== void 0 ? {
318
+ pageContext: input.pageContext
319
+ } : {}
320
+ };
321
+ const basePrompt = await resolvePrompt(deps.systemPrompt, {
322
+ ...base,
323
+ basePrompt: ""
324
+ });
325
+ if (input.persona === void 0) {
326
+ return basePrompt;
327
+ }
328
+ return resolvePrompt(input.persona.systemPrompt, {
329
+ ...base,
330
+ basePrompt
331
+ });
332
+ }
333
+ __name(resolveSystemPrompt, "resolveSystemPrompt");
334
+ function extractTask(input) {
335
+ if (typeof input === "object" && input !== null && "task" in input) {
336
+ const task = input.task;
337
+ if (typeof task === "string") {
338
+ return task;
339
+ }
340
+ }
341
+ return JSON.stringify(input);
342
+ }
343
+ __name(extractTask, "extractTask");
344
+ function deriveTitle(userText) {
345
+ const trimmed = userText.trim().replace(/\s+/g, " ");
346
+ return trimmed.length > 60 ? `${trimmed.slice(0, 57)}...` : trimmed || "New chat";
347
+ }
348
+ __name(deriveTitle, "deriveTitle");
349
+ var ToolTimeoutError = class ToolTimeoutError2 extends Error {
350
+ static {
351
+ __name(this, "ToolTimeoutError");
352
+ }
353
+ constructor(toolName, ms) {
354
+ super(`Tool "${toolName}" exceeded its ${ms}ms timeout`);
355
+ this.name = "ToolTimeoutError";
356
+ }
357
+ };
358
+ function withTimeout(work, ms, toolName) {
359
+ return new Promise((resolve, reject) => {
360
+ const timer = setTimeout(() => reject(new ToolTimeoutError(toolName, ms)), ms);
361
+ work.then((value) => {
362
+ clearTimeout(timer);
363
+ resolve(value);
364
+ }, (error) => {
365
+ clearTimeout(timer);
366
+ reject(error);
367
+ });
368
+ });
369
+ }
370
+ __name(withTimeout, "withTimeout");
371
+ function parseFollowUps(text, count) {
372
+ const source = text.match(/\[[\s\S]*\]/)?.[0] ?? text;
373
+ try {
374
+ const parsed = JSON.parse(source);
375
+ if (Array.isArray(parsed)) {
376
+ return parsed.filter((item) => typeof item === "string").slice(0, count);
377
+ }
378
+ } catch {
379
+ }
380
+ return [];
381
+ }
382
+ __name(parseFollowUps, "parseFollowUps");
383
+ async function generateFollowUps(model, messages, count) {
384
+ const discard = {
385
+ write: /* @__PURE__ */ __name(() => {
386
+ }, "write"),
387
+ end: /* @__PURE__ */ __name(() => {
388
+ }, "end"),
389
+ fail: /* @__PURE__ */ __name(() => {
390
+ }, "fail")
391
+ };
392
+ const turn = await model.runTurn({
393
+ system: `Based on the conversation so far, propose up to ${count} short, distinct follow-up questions the user is likely to ask next. Respond with ONLY a JSON array of strings \u2014 no prose, no code fences.`,
394
+ messages,
395
+ tools: [],
396
+ sink: discard
397
+ });
398
+ return {
399
+ followUps: parseFollowUps(turn.text, count),
400
+ usage: turn.usage,
401
+ ...turn.modelId !== void 0 ? {
402
+ modelId: turn.modelId
403
+ } : {}
404
+ };
405
+ }
406
+ __name(generateFollowUps, "generateFollowUps");
407
+ async function runAgentLoop(deps, input, hooks) {
408
+ const maxSteps = deps.maxSteps ?? 8;
409
+ const persona = input.persona;
410
+ let system = await resolveSystemPrompt(deps, input);
411
+ if (deps.quota !== void 0) {
412
+ const quota = deps.quota;
413
+ const state = await hooks.step("quota:check", () => quota.check(input.actor.id, deps.day));
414
+ if (!state.withinLimit) {
415
+ publishAgentQuotaExceeded({
416
+ actorId: input.actor.id,
417
+ usedTokens: state.usedTokens,
418
+ limitTokens: state.limitTokens
419
+ });
420
+ throw new QuotaExceededError();
421
+ }
422
+ }
423
+ if (input.regenerate === true) {
424
+ await hooks.step("regenerate:truncate", async () => {
425
+ const existing = await deps.store.getThread(input.threadId);
426
+ const messages = existing?.messages ?? [];
427
+ let lastUserIndex = -1;
428
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
429
+ if (messages[index]?.role === "user") {
430
+ lastUserIndex = index;
431
+ break;
432
+ }
433
+ }
434
+ const firstDropped = messages[lastUserIndex + 1];
435
+ if (firstDropped !== void 0) {
436
+ await deps.store.truncateFrom(input.threadId, firstDropped.id);
437
+ }
438
+ });
439
+ } else {
440
+ await hooks.step("persist:user", () => deps.store.appendMessage({
441
+ threadId: input.threadId,
442
+ role: "user",
443
+ content: input.userText,
444
+ ...persona !== void 0 ? {
445
+ persona: persona.id
446
+ } : {}
447
+ }));
448
+ }
449
+ const thread = await hooks.step("load:thread", () => deps.store.getThread(input.threadId));
450
+ const modelMessages = (thread?.messages ?? []).map((message) => ({
451
+ role: message.role,
452
+ content: message.content,
453
+ ...message.toolCalls !== void 0 ? {
454
+ toolCalls: message.toolCalls
455
+ } : {},
456
+ ...message.toolResults !== void 0 ? {
457
+ toolResults: message.toolResults
458
+ } : {}
459
+ }));
460
+ const writer = await hooks.openSink();
461
+ let lastText = "";
462
+ let steps = 0;
463
+ let totalInput = 0;
464
+ let totalOutput = 0;
465
+ publishAgentRunStarted({
466
+ runId: hooks.runId,
467
+ threadId: input.threadId,
468
+ actorId: input.actor.id,
469
+ ...persona !== void 0 ? {
470
+ persona: persona.id
471
+ } : {}
472
+ });
473
+ let injectedPassages;
474
+ if (deps.retriever !== void 0) {
475
+ const retriever = deps.retriever;
476
+ const passages = await hooks.step("retrieve", () => retriever.retrieve(input.userText, {
477
+ topK: deps.retrievalTopK ?? 5
478
+ }));
479
+ if (passages.length > 0) {
480
+ injectedPassages = passages;
481
+ system = `${system}
482
+
483
+ ${buildContextBlock(passages)}`;
484
+ }
485
+ publishAgentRetrieved({
486
+ runId: hooks.runId,
487
+ query: input.userText,
488
+ count: passages.length
489
+ });
490
+ }
491
+ for (let i = 0; i < maxSteps; i += 1) {
492
+ const tools = await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, intersectAllow(persona?.allowedTools, deps.toolAllowList));
493
+ const turn = await hooks.step(`llm:${i}`, () => deps.model.runTurn({
494
+ system,
495
+ messages: modelMessages,
496
+ tools,
497
+ sink: writer
498
+ }));
499
+ await hooks.step(`persist:usage:${i}`, () => deps.store.recordUsage({
500
+ threadId: input.threadId,
501
+ actorRef: input.actor.id,
502
+ // provider-reported model wins over the configured fallback, so cost can't misattribute
503
+ modelId: turn.modelId ?? deps.modelId ?? "unknown",
504
+ purpose: "chat",
505
+ usage: turn.usage,
506
+ // persist the provider's actual cost when reported; the read-model prefers it over pricing
507
+ ...turn.costUsd !== void 0 ? {
508
+ costUsd: turn.costUsd
509
+ } : {}
510
+ }));
511
+ if (deps.quota !== void 0) {
512
+ const quota = deps.quota;
513
+ await hooks.step(`quota:bump:${i}`, () => quota.bump(input.actor.id, deps.day, turn.usage.inputTokens + turn.usage.outputTokens));
514
+ }
515
+ steps += 1;
516
+ totalInput += turn.usage.inputTokens;
517
+ totalOutput += turn.usage.outputTokens;
518
+ lastText = turn.text;
519
+ publishAgentMessage({
520
+ runId: hooks.runId,
521
+ threadId: input.threadId,
522
+ role: "assistant",
523
+ textLength: turn.text.length
524
+ });
525
+ const isFinalTurn = turn.toolCalls.length === 0;
526
+ let followUps;
527
+ if (isFinalTurn && deps.followUpsCount !== void 0 && deps.followUpsCount > 0) {
528
+ const count = deps.followUpsCount;
529
+ const generated = await hooks.step(`followups:${i}`, () => generateFollowUps(deps.model, [
530
+ ...modelMessages,
531
+ {
532
+ role: "assistant",
533
+ content: turn.text
534
+ }
535
+ ], count));
536
+ if (generated.followUps.length > 0) {
537
+ followUps = generated.followUps;
538
+ }
539
+ await hooks.step(`persist:usage:followups:${i}`, () => deps.store.recordUsage({
540
+ threadId: input.threadId,
541
+ actorRef: input.actor.id,
542
+ modelId: generated.modelId ?? deps.modelId ?? "unknown",
543
+ purpose: "follow_ups",
544
+ usage: generated.usage
545
+ }));
546
+ }
547
+ const assistant = await hooks.step(`persist:assistant:${i}`, () => deps.store.appendMessage({
548
+ threadId: input.threadId,
549
+ role: "assistant",
550
+ content: turn.text,
551
+ usage: turn.usage,
552
+ ...persona !== void 0 ? {
553
+ persona: persona.id
554
+ } : {},
555
+ ...turn.toolCalls.length > 0 ? {
556
+ toolCalls: turn.toolCalls
557
+ } : {},
558
+ ...followUps !== void 0 ? {
559
+ followUps
560
+ } : {}
561
+ }));
562
+ modelMessages.push({
563
+ role: "assistant",
564
+ content: turn.text,
565
+ ...turn.toolCalls.length > 0 ? {
566
+ toolCalls: turn.toolCalls
567
+ } : {}
568
+ });
569
+ if (i === 0 && injectedPassages !== void 0) {
570
+ const passages = injectedPassages;
571
+ const toolCallId = `retrieve-${assistant.id}`;
572
+ await hooks.step(`persist:retrieval:${assistant.id}`, async () => {
573
+ await deps.store.recordToolCall({
574
+ toolCallId,
575
+ messageId: assistant.id,
576
+ toolName: "retrieve",
577
+ toolType: "read",
578
+ input: {
579
+ query: input.userText
580
+ },
581
+ status: "auto_executed"
582
+ });
583
+ await deps.store.updateToolCall({
584
+ toolCallId,
585
+ status: "executed",
586
+ output: {
587
+ passages
588
+ }
589
+ });
590
+ });
591
+ }
592
+ if (isFinalTurn) {
593
+ break;
594
+ }
595
+ const results = [];
596
+ for (const call of turn.toolCalls) {
597
+ const spec = deps.registry.spec(call.name);
598
+ const toolType = spec?.kind ?? "read";
599
+ const ctx = {
600
+ actor: input.actor,
601
+ threadId: input.threadId,
602
+ runId: hooks.runId,
603
+ requestId: hooks.runId,
604
+ ...persona !== void 0 ? {
605
+ persona
606
+ } : {},
607
+ ...input.pageContext !== void 0 ? {
608
+ pageContext: input.pageContext
609
+ } : {},
610
+ ...deps.host !== void 0 ? {
611
+ host: deps.host
612
+ } : {}
613
+ };
614
+ if (toolType === "agent") {
615
+ const targetAgent = spec?.targetAgent ?? call.name;
616
+ const task = extractTask(call.input);
617
+ await hooks.step(`persist:toolcall:${call.id}`, () => deps.store.recordToolCall({
618
+ toolCallId: call.id,
619
+ messageId: assistant.id,
620
+ toolName: call.name,
621
+ toolType: "read",
622
+ input: call.input,
623
+ status: "auto_executed"
624
+ }));
625
+ const overDepth = (input.delegationDepth ?? 0) >= MAX_DELEGATION_DEPTH;
626
+ publishAgentDelegated({
627
+ runId: hooks.runId,
628
+ toAgent: targetAgent,
629
+ ...input.agentName !== void 0 ? {
630
+ fromAgent: input.agentName
631
+ } : {}
632
+ });
633
+ let sub;
634
+ if (overDepth) {
635
+ sub = {
636
+ text: `(delegation depth limit of ${MAX_DELEGATION_DEPTH} reached)`
637
+ };
638
+ } else if (hooks.runAgent) {
639
+ sub = await hooks.runAgent(targetAgent, task);
640
+ } else {
641
+ sub = {
642
+ text: `(no multi-agent support wired; cannot reach "${targetAgent}")`
643
+ };
644
+ }
645
+ await hooks.step(`persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
646
+ toolCallId: call.id,
647
+ status: "executed",
648
+ output: sub
649
+ }));
650
+ results.push({
651
+ id: call.id,
652
+ name: call.name,
653
+ output: sub
654
+ });
655
+ continue;
656
+ }
657
+ if (toolType === "action") {
658
+ await hooks.step(`persist:toolcall:${call.id}`, () => deps.store.recordToolCall({
659
+ toolCallId: call.id,
660
+ messageId: assistant.id,
661
+ toolName: call.name,
662
+ toolType: "action",
663
+ input: call.input,
664
+ status: "pending_approval"
665
+ }));
666
+ const decision = await hooks.awaitApproval(call, ctx);
667
+ if (!decision.approved) {
668
+ await hooks.step(`persist:toolreject:${call.id}`, () => deps.store.updateToolCall({
669
+ toolCallId: call.id,
670
+ status: "rejected",
671
+ ...decision.reason !== void 0 ? {
672
+ error: decision.reason
673
+ } : {}
674
+ }));
675
+ results.push({
676
+ id: call.id,
677
+ name: call.name,
678
+ output: {
679
+ rejected: true,
680
+ reason: decision.reason ?? "rejected by user"
681
+ },
682
+ error: "rejected"
683
+ });
684
+ publishAgentToolCall({
685
+ runId: hooks.runId,
686
+ toolName: call.name,
687
+ toolType,
688
+ status: "rejected"
689
+ });
690
+ continue;
691
+ }
692
+ } else {
693
+ await hooks.step(`persist:toolcall:${call.id}`, () => deps.store.recordToolCall({
694
+ toolCallId: call.id,
695
+ messageId: assistant.id,
696
+ toolName: call.name,
697
+ toolType: "read",
698
+ input: call.input,
699
+ status: "auto_executed"
700
+ }));
701
+ }
702
+ const startedAt = Date.now();
703
+ try {
704
+ const invocation = hooks.step(`tool:${call.id}`, () => deps.registry.invoke(call.name, call.input, ctx, deps.rolesPolicy));
705
+ const output = deps.toolTimeoutMs !== void 0 ? await withTimeout(invocation, deps.toolTimeoutMs, call.name) : await invocation;
706
+ const executionMs = Date.now() - startedAt;
707
+ await hooks.step(`persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
708
+ toolCallId: call.id,
709
+ status: "executed",
710
+ output,
711
+ executionMs,
712
+ ...toolType === "action" ? {
713
+ executedByRef: input.actor.id
714
+ } : {}
715
+ }));
716
+ results.push({
717
+ id: call.id,
718
+ name: call.name,
719
+ output
720
+ });
721
+ publishAgentToolCall({
722
+ runId: hooks.runId,
723
+ toolName: call.name,
724
+ toolType,
725
+ status: "executed",
726
+ durationMs: executionMs
727
+ });
728
+ } catch (error) {
729
+ const executionMs = Date.now() - startedAt;
730
+ const message = error instanceof Error ? error.message : String(error);
731
+ await hooks.step(`persist:toolfail:${call.id}`, () => deps.store.updateToolCall({
732
+ toolCallId: call.id,
733
+ status: "failed",
734
+ error: message,
735
+ executionMs
736
+ }));
737
+ results.push({
738
+ id: call.id,
739
+ name: call.name,
740
+ output: null,
741
+ error: message
742
+ });
743
+ publishAgentToolCall({
744
+ runId: hooks.runId,
745
+ toolName: call.name,
746
+ toolType,
747
+ status: "failed",
748
+ durationMs: executionMs
749
+ });
750
+ }
751
+ }
752
+ modelMessages.push({
753
+ role: "user",
754
+ content: "",
755
+ toolResults: results
756
+ });
757
+ }
758
+ if (thread !== null && (thread.title === "" || thread.title === "New chat")) {
759
+ await hooks.step("persist:title", () => deps.store.setTitle(input.threadId, deriveTitle(input.userText)));
760
+ }
761
+ await writer.end();
762
+ publishAgentRunFinished({
763
+ runId: hooks.runId,
764
+ threadId: input.threadId,
765
+ steps,
766
+ inputTokens: totalInput,
767
+ outputTokens: totalOutput
768
+ });
769
+ return {
770
+ text: lastText
771
+ };
772
+ }
773
+ __name(runAgentLoop, "runAgentLoop");
774
+ // Annotate the CommonJS export names for ESM import in node:
775
+ 0 && (module.exports = {
776
+ AGENT_ACTOR_RESOLVER,
777
+ AGENT_DEPS_FACTORY,
778
+ AGENT_DURABLE_RUNNER,
779
+ AGENT_EMBEDDING_PROVIDER,
780
+ AGENT_GOVERNANCE_QUERIES,
781
+ AGENT_MODEL,
782
+ AGENT_OPTIONS,
783
+ AGENT_PRICING_STORE,
784
+ AGENT_QUOTA_STORE,
785
+ AGENT_REGISTRY,
786
+ AGENT_RETRIEVER,
787
+ AGENT_ROLES_POLICY,
788
+ AGENT_RUNNER,
789
+ AGENT_SINK,
790
+ AGENT_STORE,
791
+ AGENT_TOOL_REGISTRY,
792
+ AgentRegistry,
793
+ AgentStreamError,
794
+ DefaultRolesPolicy,
795
+ QuotaExceededError,
796
+ ToolForbiddenError,
797
+ ToolInputInvalidError,
798
+ ToolNotFoundError,
799
+ ToolRegistry,
800
+ filterToolsByRole,
801
+ personaFilterTools,
802
+ publishAgentDelegated,
803
+ publishAgentMessage,
804
+ publishAgentQuotaExceeded,
805
+ publishAgentRetrieved,
806
+ publishAgentRunFailed,
807
+ publishAgentRunFinished,
808
+ publishAgentRunStarted,
809
+ publishAgentToolCall,
810
+ runAgentLoop,
811
+ seedModelPrices
812
+ });
813
+ //# sourceMappingURL=index.cjs.map