@dudousxd/nestjs-agent-core 0.3.3 → 0.5.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 CHANGED
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/index.ts
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
+ AGENT_ACTOR_DIRECTORY: () => AGENT_ACTOR_DIRECTORY,
24
25
  AGENT_ACTOR_RESOLVER: () => AGENT_ACTOR_RESOLVER,
26
+ AGENT_ATTACHMENT_STAGING: () => AGENT_ATTACHMENT_STAGING,
25
27
  AGENT_DEPS_FACTORY: () => AGENT_DEPS_FACTORY,
26
28
  AGENT_DURABLE_RUNNER: () => AGENT_DURABLE_RUNNER,
27
29
  AGENT_EMBEDDING_PROVIDER: () => AGENT_EMBEDDING_PROVIDER,
@@ -64,7 +66,8 @@ __export(index_exports, {
64
66
  publishAgentRunStarted: () => publishAgentRunStarted,
65
67
  publishAgentToolCall: () => publishAgentToolCall,
66
68
  runAgentLoop: () => runAgentLoop,
67
- seedModelPrices: () => seedModelPrices
69
+ seedModelPrices: () => seedModelPrices,
70
+ withToolTimeout: () => withToolTimeout
68
71
  });
69
72
  module.exports = __toCommonJS(index_exports);
70
73
 
@@ -86,6 +89,8 @@ var AGENT_RETRIEVER = Symbol.for("@dudousxd/nestjs-agent:retriever");
86
89
  var AGENT_EMBEDDING_PROVIDER = Symbol.for("@dudousxd/nestjs-agent:embedding-provider");
87
90
  var AGENT_DEPS_FACTORY = Symbol.for("@dudousxd/nestjs-agent:deps-factory");
88
91
  var AGENT_PROMPT_CONTRIBUTORS = Symbol.for("@dudousxd/nestjs-agent:prompt-contributors");
92
+ var AGENT_ACTOR_DIRECTORY = Symbol.for("@dudousxd/nestjs-agent:actor-directory");
93
+ var AGENT_ATTACHMENT_STAGING = Symbol.for("@dudousxd/nestjs-agent:attachment-staging");
89
94
 
90
95
  // src/spi/token-stream-sink.ts
91
96
  var AgentStreamError = class extends Error {
@@ -425,6 +430,13 @@ function publishAgentRetrieved(payload) {
425
430
  __name(publishAgentRetrieved, "publishAgentRetrieved");
426
431
 
427
432
  // src/agent-loop.ts
433
+ function resolveCostUsd(usage, reportedCostUsd, price) {
434
+ if (reportedCostUsd !== void 0) {
435
+ return reportedCostUsd;
436
+ }
437
+ return price === void 0 ? null : estimateCost(usage, price);
438
+ }
439
+ __name(resolveCostUsd, "resolveCostUsd");
428
440
  function buildContextBlock(passages) {
429
441
  const items = passages.map((passage, index) => {
430
442
  const label = passage.source !== void 0 ? ` (${passage.source})` : "";
@@ -494,7 +506,7 @@ var ToolTimeoutError = class ToolTimeoutError2 extends Error {
494
506
  this.name = "ToolTimeoutError";
495
507
  }
496
508
  };
497
- function withTimeout(work, ms, toolName) {
509
+ function withToolTimeout(work, ms, toolName) {
498
510
  return new Promise((resolve, reject) => {
499
511
  const timer = setTimeout(() => reject(new ToolTimeoutError(toolName, ms)), ms);
500
512
  work.then((value) => {
@@ -506,7 +518,7 @@ function withTimeout(work, ms, toolName) {
506
518
  });
507
519
  });
508
520
  }
509
- __name(withTimeout, "withTimeout");
521
+ __name(withToolTimeout, "withToolTimeout");
510
522
  function parseFollowUps(text, count) {
511
523
  const source = text.match(/\[[\s\S]*\]/)?.[0] ?? text;
512
524
  try {
@@ -611,6 +623,17 @@ async function runAgentLoop(deps, input, hooks) {
611
623
  agentName: input.agentName
612
624
  } : {}
613
625
  });
626
+ const startedAt = await hooks.step("run:started-at", () => Promise.resolve(Date.now()));
627
+ await hooks.step("persist:run:start", async () => {
628
+ await deps.store.recordRunStart?.({
629
+ runId: hooks.runId,
630
+ threadId: input.threadId,
631
+ actorRef: input.actor.id,
632
+ ...input.agentName !== void 0 ? {
633
+ agentName: input.agentName
634
+ } : {}
635
+ });
636
+ });
614
637
  let injectedPassages;
615
638
  if (deps.retriever !== void 0) {
616
639
  const retriever = deps.retriever;
@@ -629,24 +652,50 @@ ${buildContextBlock(passages)}`;
629
652
  count: passages.length
630
653
  });
631
654
  }
655
+ let prices = [];
656
+ if (deps.pricingStore !== void 0) {
657
+ const pricingStore = deps.pricingStore;
658
+ prices = await hooks.step("pricing:list", () => pricingStore.listCurrentPrices());
659
+ }
660
+ const priceByModel = new Map(prices.map((price) => [
661
+ price.modelId,
662
+ price
663
+ ]));
632
664
  for (let i = 0; i < maxSteps; i += 1) {
633
665
  await hooks.step(`stream:step-start:${i}`, async () => {
634
666
  await writer.write(encodeStreamEvent({
635
667
  kind: "step-start"
636
668
  }));
637
669
  });
638
- const tools = await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, deps.toolAllowList);
639
- const turn = await hooks.step(`llm:${i}`, () => deps.model.runTurn({
640
- system,
641
- messages: modelMessages,
642
- tools,
643
- sink: writer
670
+ let turn;
671
+ if (hooks.dispatchLlm) {
672
+ turn = await hooks.dispatchLlm(i, {
673
+ ...input.agentName !== void 0 ? {
674
+ agentName: input.agentName
675
+ } : {},
676
+ system,
677
+ messages: modelMessages,
678
+ actor: input.actor
679
+ });
680
+ } else {
681
+ const tools = await deps.registry.definitionsFor(input.actor, deps.rolesPolicy, deps.toolAllowList);
682
+ turn = await hooks.step(`llm:${i}`, () => deps.model.runTurn({
683
+ system,
684
+ messages: modelMessages,
685
+ tools,
686
+ sink: writer
687
+ }));
688
+ }
689
+ const resolvedModelId = turn.modelId ?? deps.modelId ?? "unknown";
690
+ const costUsd = resolveCostUsd(turn.usage, turn.costUsd, priceByModel.get(resolvedModelId));
691
+ const toolCallsWithKind = turn.toolCalls.map((call) => ({
692
+ ...call,
693
+ kind: deps.registry.spec(call.name)?.kind ?? "read"
644
694
  }));
645
695
  await hooks.step(`persist:usage:${i}`, () => deps.store.recordUsage({
646
696
  threadId: input.threadId,
647
697
  actorRef: input.actor.id,
648
- // provider-reported model wins over the configured fallback, so cost can't misattribute
649
- modelId: turn.modelId ?? deps.modelId ?? "unknown",
698
+ modelId: resolvedModelId,
650
699
  purpose: "chat",
651
700
  usage: turn.usage,
652
701
  // persist the provider's actual cost when reported; the read-model prefers it over pricing
@@ -694,12 +743,15 @@ ${buildContextBlock(passages)}`;
694
743
  threadId: input.threadId,
695
744
  role: "assistant",
696
745
  content: turn.text,
697
- usage: turn.usage,
746
+ usage: {
747
+ ...turn.usage,
748
+ costUsd
749
+ },
698
750
  ...input.agentName !== void 0 ? {
699
751
  agentName: input.agentName
700
752
  } : {},
701
- ...turn.toolCalls.length > 0 ? {
702
- toolCalls: turn.toolCalls
753
+ ...toolCallsWithKind.length > 0 ? {
754
+ toolCalls: toolCallsWithKind
703
755
  } : {},
704
756
  ...followUps !== void 0 ? {
705
757
  followUps
@@ -708,8 +760,8 @@ ${buildContextBlock(passages)}`;
708
760
  const assistantMessage = {
709
761
  role: "assistant",
710
762
  content: turn.text,
711
- ...turn.toolCalls.length > 0 ? {
712
- toolCalls: turn.toolCalls
763
+ ...toolCallsWithKind.length > 0 ? {
764
+ toolCalls: toolCallsWithKind
713
765
  } : {}
714
766
  };
715
767
  modelMessages.push(assistantMessage);
@@ -739,15 +791,17 @@ ${buildContextBlock(passages)}`;
739
791
  if (isFinalTurn) {
740
792
  await hooks.step(`stream:step-finish:${i}`, async () => {
741
793
  await writer.write(encodeStreamEvent({
742
- kind: "step-finish"
794
+ kind: "step-finish",
795
+ usage: turn.usage,
796
+ costUsd
743
797
  }));
744
798
  });
745
799
  break;
746
800
  }
747
801
  const results = [];
748
- for (const call of turn.toolCalls) {
802
+ for (const call of toolCallsWithKind) {
749
803
  const spec = deps.registry.spec(call.name);
750
- const toolType = spec?.kind ?? "read";
804
+ const toolType = call.kind ?? "read";
751
805
  const ctx = {
752
806
  actor: input.actor,
753
807
  threadId: input.threadId,
@@ -851,11 +905,36 @@ ${buildContextBlock(passages)}`;
851
905
  status: "auto_executed"
852
906
  }));
853
907
  }
854
- const startedAt = Date.now();
908
+ const startedAt2 = Date.now();
855
909
  try {
856
- const invocation = hooks.step(`tool:${call.id}`, () => deps.registry.invoke(call.name, call.input, ctx, deps.rolesPolicy));
857
- const output = deps.toolTimeoutMs !== void 0 ? await withTimeout(invocation, deps.toolTimeoutMs, call.name) : await invocation;
858
- const executionMs = Date.now() - startedAt;
910
+ let output;
911
+ if (hooks.dispatchTool) {
912
+ const stepCtx = {
913
+ actor: input.actor,
914
+ threadId: input.threadId,
915
+ runId: hooks.runId,
916
+ requestId: hooks.runId,
917
+ ...input.agentName !== void 0 ? {
918
+ agentName: input.agentName
919
+ } : {},
920
+ ...input.pageContext !== void 0 ? {
921
+ pageContext: input.pageContext
922
+ } : {}
923
+ };
924
+ const envelope = {
925
+ toolName: call.name,
926
+ input: call.input,
927
+ ctx: stepCtx,
928
+ ...deps.toolTimeoutMs !== void 0 ? {
929
+ timeoutMs: deps.toolTimeoutMs
930
+ } : {}
931
+ };
932
+ output = await hooks.dispatchTool(call, envelope);
933
+ } else {
934
+ const invocation = hooks.step(`tool:${call.id}`, () => deps.registry.invoke(call.name, call.input, ctx, deps.rolesPolicy));
935
+ output = deps.toolTimeoutMs !== void 0 ? await withToolTimeout(invocation, deps.toolTimeoutMs, call.name) : await invocation;
936
+ }
937
+ const executionMs = Date.now() - startedAt2;
859
938
  await hooks.step(`persist:toolexec:${call.id}`, () => deps.store.updateToolCall({
860
939
  toolCallId: call.id,
861
940
  status: "executed",
@@ -878,7 +957,10 @@ ${buildContextBlock(passages)}`;
878
957
  durationMs: executionMs
879
958
  });
880
959
  } catch (error) {
881
- const executionMs = Date.now() - startedAt;
960
+ if (hooks.isControlFlowError?.(error) === true) {
961
+ throw error;
962
+ }
963
+ const executionMs = Date.now() - startedAt2;
882
964
  const message = error instanceof Error ? error.message : String(error);
883
965
  await hooks.step(`persist:toolfail:${call.id}`, () => deps.store.updateToolCall({
884
966
  toolCallId: call.id,
@@ -917,13 +999,22 @@ ${buildContextBlock(passages)}`;
917
999
  });
918
1000
  await hooks.step(`stream:step-finish:${i}`, async () => {
919
1001
  await writer.write(encodeStreamEvent({
920
- kind: "step-finish"
1002
+ kind: "step-finish",
1003
+ usage: turn.usage,
1004
+ costUsd
921
1005
  }));
922
1006
  });
923
1007
  }
924
1008
  if (thread !== null && (thread.title === "" || thread.title === "New chat")) {
925
1009
  await hooks.step("persist:title", () => deps.store.setTitle(input.threadId, deriveTitle(input.userText)));
926
1010
  }
1011
+ await hooks.step("persist:run:end", async () => {
1012
+ await deps.store.recordRunEnd?.({
1013
+ runId: hooks.runId,
1014
+ status: "completed",
1015
+ durationMs: Date.now() - startedAt
1016
+ });
1017
+ });
927
1018
  await writer.end();
928
1019
  publishAgentRunFinished({
929
1020
  runId: hooks.runId,
@@ -939,7 +1030,9 @@ ${buildContextBlock(passages)}`;
939
1030
  __name(runAgentLoop, "runAgentLoop");
940
1031
  // Annotate the CommonJS export names for ESM import in node:
941
1032
  0 && (module.exports = {
1033
+ AGENT_ACTOR_DIRECTORY,
942
1034
  AGENT_ACTOR_RESOLVER,
1035
+ AGENT_ATTACHMENT_STAGING,
943
1036
  AGENT_DEPS_FACTORY,
944
1037
  AGENT_DURABLE_RUNNER,
945
1038
  AGENT_EMBEDDING_PROVIDER,
@@ -982,6 +1075,7 @@ __name(runAgentLoop, "runAgentLoop");
982
1075
  publishAgentRunStarted,
983
1076
  publishAgentToolCall,
984
1077
  runAgentLoop,
985
- seedModelPrices
1078
+ seedModelPrices,
1079
+ withToolTimeout
986
1080
  });
987
1081
  //# sourceMappingURL=index.cjs.map