@dudousxd/nestjs-agent-core 0.8.0 → 0.10.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
@@ -45,6 +45,8 @@ __export(index_exports, {
45
45
  AGENT_TOOL_REGISTRY: () => AGENT_TOOL_REGISTRY,
46
46
  AgentRegistry: () => AgentRegistry,
47
47
  AgentStreamError: () => AgentStreamError,
48
+ DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS: () => DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS,
49
+ DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS: () => DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
48
50
  DefaultRolesPolicy: () => DefaultRolesPolicy,
49
51
  QuotaExceededError: () => QuotaExceededError,
50
52
  ToolForbiddenError: () => ToolForbiddenError,
@@ -61,6 +63,8 @@ __export(index_exports, {
61
63
  estimateCost: () => estimateCost,
62
64
  filterToolsByAllowList: () => filterToolsByAllowList,
63
65
  filterToolsByRole: () => filterToolsByRole,
66
+ invokeWithTransientRetry: () => invokeWithTransientRetry,
67
+ isTransientToolError: () => isTransientToolError,
64
68
  publishAgentDelegated: () => publishAgentDelegated,
65
69
  publishAgentMessage: () => publishAgentMessage,
66
70
  publishAgentQuotaExceeded: () => publishAgentQuotaExceeded,
@@ -69,6 +73,8 @@ __export(index_exports, {
69
73
  publishAgentRunFinished: () => publishAgentRunFinished,
70
74
  publishAgentRunStarted: () => publishAgentRunStarted,
71
75
  publishAgentToolCall: () => publishAgentToolCall,
76
+ publishAgentToolRetry: () => publishAgentToolRetry,
77
+ resolveToolTransientRetryNumbers: () => resolveToolTransientRetryNumbers,
72
78
  runAgentLoop: () => runAgentLoop,
73
79
  seedModelPrices: () => seedModelPrices,
74
80
  traceLlmTurn: () => traceLlmTurn,
@@ -439,6 +445,10 @@ function publishAgentRetrieved(payload) {
439
445
  (0, import_nestjs_diagnostics.emit)("agent", "retrieved", payload);
440
446
  }
441
447
  __name(publishAgentRetrieved, "publishAgentRetrieved");
448
+ function publishAgentToolRetry(payload) {
449
+ (0, import_nestjs_diagnostics.emit)("agent", "tool.retry", payload);
450
+ }
451
+ __name(publishAgentToolRetry, "publishAgentToolRetry");
442
452
  var AGENT_SPAN_EVENTS = [
443
453
  "llm.turn",
444
454
  "tool.execution",
@@ -453,13 +463,96 @@ var AGENT_DIAGNOSTIC_EVENTS = [
453
463
  "run.finished",
454
464
  "run.failed",
455
465
  "delegated",
456
- "retrieved"
466
+ "retrieved",
467
+ "tool.retry"
457
468
  ];
458
469
  function agentDiagnosticKey(event) {
459
470
  return `agent:${event}`;
460
471
  }
461
472
  __name(agentDiagnosticKey, "agentDiagnosticKey");
462
473
 
474
+ // src/tool-retry.ts
475
+ function hasTransientShape(error) {
476
+ if (typeof error !== "object" || error === null) {
477
+ return false;
478
+ }
479
+ const code = "code" in error ? error.code : void 0;
480
+ const errno = "errno" in error ? error.errno : void 0;
481
+ const sqlState = "sqlState" in error ? error.sqlState : void 0;
482
+ if (code === 1213 || code === 1205 || errno === 1213 || errno === 1205) {
483
+ return true;
484
+ }
485
+ if (code === "ER_LOCK_DEADLOCK" || code === "ER_LOCK_WAIT_TIMEOUT") {
486
+ return true;
487
+ }
488
+ if (code === "40001" || code === "40P01" || sqlState === "40001" || sqlState === "40P01") {
489
+ return true;
490
+ }
491
+ if (code === "SQLITE_BUSY") {
492
+ return true;
493
+ }
494
+ const message = "message" in error ? error.message : void 0;
495
+ return typeof message === "string" && /deadlock|lock wait timeout|serialization failure/i.test(message);
496
+ }
497
+ __name(hasTransientShape, "hasTransientShape");
498
+ function isTransientToolError(error) {
499
+ if (hasTransientShape(error)) {
500
+ return true;
501
+ }
502
+ if (typeof error === "object" && error !== null && "cause" in error) {
503
+ const cause = error.cause;
504
+ if (cause !== void 0 && cause !== error && hasTransientShape(cause)) {
505
+ return true;
506
+ }
507
+ }
508
+ return false;
509
+ }
510
+ __name(isTransientToolError, "isTransientToolError");
511
+ var DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS = 2;
512
+ var DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS = 150;
513
+ function resolveToolTransientRetryNumbers(setting) {
514
+ if (setting === false) {
515
+ return false;
516
+ }
517
+ return {
518
+ attempts: setting?.attempts ?? DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS,
519
+ backoffMs: setting?.backoffMs ?? DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS
520
+ };
521
+ }
522
+ __name(resolveToolTransientRetryNumbers, "resolveToolTransientRetryNumbers");
523
+ function delay(ms) {
524
+ return new Promise((resolve) => {
525
+ setTimeout(resolve, ms);
526
+ });
527
+ }
528
+ __name(delay, "delay");
529
+ async function invokeWithTransientRetry(fn, setting, options) {
530
+ if (setting === false) {
531
+ return fn();
532
+ }
533
+ const attempts = setting.attempts ?? DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS;
534
+ const backoffMs = setting.backoffMs ?? DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS;
535
+ const classify = setting.classify ?? isTransientToolError;
536
+ let attempt = 1;
537
+ for (; ; ) {
538
+ try {
539
+ return await fn();
540
+ } catch (error) {
541
+ if (options?.isControlFlowError?.(error) === true) {
542
+ throw error;
543
+ }
544
+ const attemptsRemain = attempt < attempts;
545
+ if (!attemptsRemain || !classify(error)) {
546
+ throw error;
547
+ }
548
+ options?.onRetry?.(attempt, error);
549
+ await delay(backoffMs * attempt);
550
+ attempt += 1;
551
+ }
552
+ }
553
+ }
554
+ __name(invokeWithTransientRetry, "invokeWithTransientRetry");
555
+
463
556
  // src/agent-loop.ts
464
557
  function resolveCostUsd(usage, reportedCostUsd, price) {
465
558
  if (reportedCostUsd !== void 0) {
@@ -861,7 +954,8 @@ ${buildContextBlock(passages)}`;
861
954
  input: {
862
955
  query: input.userText
863
956
  },
864
- status: "auto_executed"
957
+ status: "auto_executed",
958
+ runId: hooks.runId
865
959
  });
866
960
  await deps.store.updateToolCall({
867
961
  toolCallId,
@@ -910,7 +1004,8 @@ ${buildContextBlock(passages)}`;
910
1004
  toolName: call.name,
911
1005
  toolType: "read",
912
1006
  input: call.input,
913
- status: "auto_executed"
1007
+ status: "auto_executed",
1008
+ runId: hooks.runId
914
1009
  }));
915
1010
  const overDepth = (input.delegationDepth ?? 0) >= MAX_DELEGATION_DEPTH;
916
1011
  publishAgentDelegated({
@@ -952,7 +1047,8 @@ ${buildContextBlock(passages)}`;
952
1047
  toolName: call.name,
953
1048
  toolType: "action",
954
1049
  input: call.input,
955
- status: "pending_approval"
1050
+ status: "pending_approval",
1051
+ runId: hooks.runId
956
1052
  }));
957
1053
  const decision = await hooks.awaitApproval(call, ctx);
958
1054
  deciderRef = decision.executedByRef ?? input.actor.id;
@@ -989,7 +1085,8 @@ ${buildContextBlock(passages)}`;
989
1085
  toolName: call.name,
990
1086
  toolType: "read",
991
1087
  input: call.input,
992
- status: "auto_executed"
1088
+ status: "auto_executed",
1089
+ runId: hooks.runId
993
1090
  }));
994
1091
  }
995
1092
  const startedAt2 = Date.now();
@@ -1014,7 +1111,10 @@ ${buildContextBlock(passages)}`;
1014
1111
  ctx: stepCtx,
1015
1112
  ...deps.toolTimeoutMs !== void 0 ? {
1016
1113
  timeoutMs: deps.toolTimeoutMs
1017
- } : {}
1114
+ } : {},
1115
+ // Numeric-only: the handler applies withToolTimeout AND its own local `classify` — see
1116
+ // ToolStepEnvelope.transientRetry.
1117
+ transientRetry: resolveToolTransientRetryNumbers(deps.toolTransientRetry)
1018
1118
  };
1019
1119
  output = await hooks.dispatchTool(call, envelope);
1020
1120
  } else {
@@ -1022,7 +1122,19 @@ ${buildContextBlock(passages)}`;
1022
1122
  toolCallId: call.id,
1023
1123
  toolName: call.name,
1024
1124
  toolType
1025
- }, () => deps.registry.invoke(call.name, call.input, ctx, deps.rolesPolicy)));
1125
+ }, () => invokeWithTransientRetry(() => deps.registry.invoke(call.name, call.input, ctx, deps.rolesPolicy), deps.toolTransientRetry ?? {}, {
1126
+ ...hooks.isControlFlowError !== void 0 ? {
1127
+ isControlFlowError: hooks.isControlFlowError
1128
+ } : {},
1129
+ onRetry: /* @__PURE__ */ __name((attempt, retryError) => {
1130
+ publishAgentToolRetry({
1131
+ toolName: call.name,
1132
+ toolCallId: call.id,
1133
+ attempt,
1134
+ message: retryError instanceof Error ? retryError.message : String(retryError)
1135
+ });
1136
+ }, "onRetry")
1137
+ })));
1026
1138
  output = deps.toolTimeoutMs !== void 0 ? await withToolTimeout(invocation, deps.toolTimeoutMs, call.name) : await invocation;
1027
1139
  }
1028
1140
  const executionMs = Date.now() - startedAt2;
@@ -1145,6 +1257,8 @@ __name(runAgentLoop, "runAgentLoop");
1145
1257
  AGENT_TOOL_REGISTRY,
1146
1258
  AgentRegistry,
1147
1259
  AgentStreamError,
1260
+ DEFAULT_TOOL_TRANSIENT_RETRY_ATTEMPTS,
1261
+ DEFAULT_TOOL_TRANSIENT_RETRY_BACKOFF_MS,
1148
1262
  DefaultRolesPolicy,
1149
1263
  QuotaExceededError,
1150
1264
  ToolForbiddenError,
@@ -1161,6 +1275,8 @@ __name(runAgentLoop, "runAgentLoop");
1161
1275
  estimateCost,
1162
1276
  filterToolsByAllowList,
1163
1277
  filterToolsByRole,
1278
+ invokeWithTransientRetry,
1279
+ isTransientToolError,
1164
1280
  publishAgentDelegated,
1165
1281
  publishAgentMessage,
1166
1282
  publishAgentQuotaExceeded,
@@ -1169,6 +1285,8 @@ __name(runAgentLoop, "runAgentLoop");
1169
1285
  publishAgentRunFinished,
1170
1286
  publishAgentRunStarted,
1171
1287
  publishAgentToolCall,
1288
+ publishAgentToolRetry,
1289
+ resolveToolTransientRetryNumbers,
1172
1290
  runAgentLoop,
1173
1291
  seedModelPrices,
1174
1292
  traceLlmTurn,