@dudousxd/nestjs-agent-core 0.32.0 → 0.34.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.js CHANGED
@@ -2348,6 +2348,123 @@ function isControlFlowSignal(error) {
2348
2348
  }
2349
2349
  __name(isControlFlowSignal, "isControlFlowSignal");
2350
2350
 
2351
+ // src/dangling-tool-calls.ts
2352
+ var UNFINISHED_TOOL_CALL = "This tool call was never completed: the turn it belonged to ended unexpectedly before it was settled. Do not assume it ran, and do not assume it did not \u2014 check the current state with a read tool if one exists, tell the person plainly what is and is not confirmed, and do it again only if they still want it.";
2353
+ var RUN_ENDED_BEFORE_TOOL_CALL = "the run ended before this tool call was settled";
2354
+ var DECLINED = "The person was asked to approve this action and declined it. Nothing ran and nothing changed.";
2355
+ var EXPIRED = "This action needed approval, and the request expired before anyone decided. Nothing ran.";
2356
+ function danglingToolCallIds(messages) {
2357
+ const ids = [];
2358
+ for (const message of messages) {
2359
+ const calls = message.toolCalls ?? [];
2360
+ if (message.role !== "assistant" || calls.length === 0) {
2361
+ continue;
2362
+ }
2363
+ const settled = new Set((message.toolResults ?? []).map((result) => result.id));
2364
+ for (const call of calls) {
2365
+ if (!settled.has(call.id)) {
2366
+ ids.push(call.id);
2367
+ }
2368
+ }
2369
+ }
2370
+ return ids;
2371
+ }
2372
+ __name(danglingToolCallIds, "danglingToolCallIds");
2373
+ function resultFor(call, outcome) {
2374
+ const base = {
2375
+ id: call.id,
2376
+ name: call.name
2377
+ };
2378
+ switch (outcome?.status) {
2379
+ case "executed":
2380
+ case "auto_executed":
2381
+ return {
2382
+ ...base,
2383
+ output: outcome.output ?? null
2384
+ };
2385
+ case "failed":
2386
+ return {
2387
+ ...base,
2388
+ output: null,
2389
+ error: outcome.error ?? UNFINISHED_TOOL_CALL
2390
+ };
2391
+ case "rejected":
2392
+ return {
2393
+ ...base,
2394
+ output: {
2395
+ rejected: true
2396
+ },
2397
+ denied: true,
2398
+ error: DECLINED
2399
+ };
2400
+ case "expired":
2401
+ return {
2402
+ ...base,
2403
+ output: {
2404
+ rejected: true,
2405
+ expired: true
2406
+ },
2407
+ denied: true,
2408
+ expired: true,
2409
+ error: EXPIRED
2410
+ };
2411
+ default:
2412
+ return {
2413
+ ...base,
2414
+ output: null,
2415
+ error: UNFINISHED_TOOL_CALL
2416
+ };
2417
+ }
2418
+ }
2419
+ __name(resultFor, "resultFor");
2420
+ function settleDanglingToolCalls(messages, outcomes = []) {
2421
+ const known = new Map(outcomes.map((outcome) => [
2422
+ outcome.id,
2423
+ outcome
2424
+ ]));
2425
+ return messages.map((message) => {
2426
+ const calls = message.toolCalls ?? [];
2427
+ if (message.role !== "assistant" || calls.length === 0) {
2428
+ return message;
2429
+ }
2430
+ const results = message.toolResults ?? [];
2431
+ const settled = new Set(results.map((result) => result.id));
2432
+ const missing = calls.filter((call) => !settled.has(call.id));
2433
+ if (missing.length === 0) {
2434
+ return message;
2435
+ }
2436
+ return {
2437
+ ...message,
2438
+ toolResults: [
2439
+ ...results,
2440
+ ...missing.map((call) => resultFor(call, known.get(call.id)))
2441
+ ]
2442
+ };
2443
+ });
2444
+ }
2445
+ __name(settleDanglingToolCalls, "settleDanglingToolCalls");
2446
+
2447
+ // src/dead-run.ts
2448
+ var RUN_NOT_ACTIVE_CODE = "run_not_active";
2449
+ var RUN_NOT_ACTIVE_MESSAGE = "This request is no longer waiting for an answer: the turn it belonged to has ended. Send the message again.";
2450
+ var RUN_NO_LONGER_RUNNING = "the run is no longer running";
2451
+ async function settleDeadRun(store, input) {
2452
+ const failure = input.failure;
2453
+ if (failure !== void 0) {
2454
+ await Promise.resolve(store.recordRunEnd?.({
2455
+ runId: input.runId,
2456
+ status: "failed",
2457
+ errorCode: failure.code,
2458
+ errorMessage: failure.message
2459
+ })).catch(() => void 0);
2460
+ }
2461
+ await Promise.resolve(store.failUnsettledToolCalls?.(input.runId, RUN_ENDED_BEFORE_TOOL_CALL)).catch(() => 0);
2462
+ if (input.threadId !== void 0) {
2463
+ await releaseThreadRun(store, input.threadId, input.runId).catch(() => void 0);
2464
+ }
2465
+ }
2466
+ __name(settleDeadRun, "settleDeadRun");
2467
+
2351
2468
  // src/delegation.ts
2352
2469
  function normalizeDelegation(entry) {
2353
2470
  return typeof entry === "string" ? {
@@ -2812,9 +2929,46 @@ function agentFailureCode(error) {
2812
2929
  if (error instanceof StructuredOutputError) {
2813
2930
  return "structured_output_invalid";
2814
2931
  }
2815
- return "run_failed";
2932
+ if (isReplayIntegrityError(error)) {
2933
+ return "replay_diverged";
2934
+ }
2935
+ return isNoOutputError(error) ? "model_no_output" : "run_failed";
2816
2936
  }
2817
2937
  __name(agentFailureCode, "agentFailureCode");
2938
+ function isNoOutputError(error) {
2939
+ if (!(error instanceof Error)) {
2940
+ return false;
2941
+ }
2942
+ return error.name === "AI_NoOutputGeneratedError" || error.name === "NoOutputGeneratedError" || error.message.startsWith("No output generated");
2943
+ }
2944
+ __name(isNoOutputError, "isNoOutputError");
2945
+ var RUN_FAILED_MESSAGE = "The assistant could not finish this answer. Please try again.";
2946
+ var streamErrorDetails;
2947
+ function exposeStreamErrorDetails(expose) {
2948
+ streamErrorDetails = expose;
2949
+ }
2950
+ __name(exposeStreamErrorDetails, "exposeStreamErrorDetails");
2951
+ var WORDED_CODES = /* @__PURE__ */ new Set([
2952
+ "quota_exceeded",
2953
+ "output_rejected",
2954
+ "structured_output_invalid"
2955
+ ]);
2956
+ function streamFailure(error) {
2957
+ const detail = error instanceof Error ? error.message : String(error);
2958
+ const code = agentFailureCode(error);
2959
+ if (WORDED_CODES.has(code)) {
2960
+ return {
2961
+ code,
2962
+ message: detail
2963
+ };
2964
+ }
2965
+ const exposed = streamErrorDetails ?? process.env.NODE_ENV !== "production";
2966
+ return {
2967
+ code,
2968
+ message: exposed ? detail : RUN_FAILED_MESSAGE
2969
+ };
2970
+ }
2971
+ __name(streamFailure, "streamFailure");
2818
2972
  var MAX_DELEGATION_DEPTH = 5;
2819
2973
  var DEFAULT_MAX_AGENT_APPEARANCES = 1;
2820
2974
  function delegationRefusal(args) {
@@ -3006,9 +3160,9 @@ function splitHistory(policy, input, messages) {
3006
3160
  }
3007
3161
  __name(splitHistory, "splitHistory");
3008
3162
  async function loadSelectedHistory(deps, input, hooks) {
3009
- return hooks.step("load:thread", async () => {
3163
+ const recorded = await hooks.step("load:thread", async () => {
3010
3164
  const thread = await readThreadForTurn(deps, input.threadId);
3011
- const { keep, drop } = splitHistory(deps.historyPolicy, input, toModelMessages(thread.messages));
3165
+ const { keep, drop } = splitHistory(deps.historyPolicy, input, await settleHistory(deps, toModelMessages(thread.messages)));
3012
3166
  return {
3013
3167
  messages: keep,
3014
3168
  dropped: deps.historyPolicy?.summarize === void 0 ? [] : drop,
@@ -3016,8 +3170,21 @@ async function loadSelectedHistory(deps, input, hooks) {
3016
3170
  hasAssistantMessage: thread.hasAssistantMessage
3017
3171
  };
3018
3172
  });
3173
+ return {
3174
+ ...recorded,
3175
+ messages: settleDanglingToolCalls(recorded.messages)
3176
+ };
3019
3177
  }
3020
3178
  __name(loadSelectedHistory, "loadSelectedHistory");
3179
+ async function settleHistory(deps, messages) {
3180
+ const dangling = danglingToolCallIds(messages);
3181
+ if (dangling.length === 0) {
3182
+ return messages;
3183
+ }
3184
+ const outcomes = await Promise.resolve(deps.store.toolCallOutcomes?.(dangling) ?? []).catch(() => []);
3185
+ return settleDanglingToolCalls(messages, outcomes);
3186
+ }
3187
+ __name(settleHistory, "settleHistory");
3021
3188
  async function readThreadForTurn(deps, threadId) {
3022
3189
  const windowing = deps.store;
3023
3190
  if (typeof windowing.loadThreadForTurn === "function") {
@@ -3057,7 +3224,7 @@ __name(turnMessageLimit, "turnMessageLimit");
3057
3224
  async function loadWholeThread(deps, input, hooks) {
3058
3225
  const thread = await hooks.step("load:thread", () => deps.store.getThread(input.threadId));
3059
3226
  const stored = thread?.messages ?? [];
3060
- const { keep, drop } = splitHistory(deps.historyPolicy, input, toModelMessages(stored));
3227
+ const { keep, drop } = splitHistory(deps.historyPolicy, input, settleDanglingToolCalls(toModelMessages(stored)));
3061
3228
  return {
3062
3229
  messages: keep,
3063
3230
  dropped: drop,
@@ -3240,6 +3407,14 @@ async function awaitElicitation(hooks, request, ctx) {
3240
3407
  }, ctx));
3241
3408
  }
3242
3409
  __name(awaitElicitation, "awaitElicitation");
3410
+ function toolCallContext(ctx, toolCallId) {
3411
+ return {
3412
+ ...ctx,
3413
+ toolCallId,
3414
+ idempotencyKey: `${ctx.runId}:${toolCallId}`
3415
+ };
3416
+ }
3417
+ __name(toolCallContext, "toolCallContext");
3243
3418
  function toolContext(deps, input, hooks) {
3244
3419
  return {
3245
3420
  actor: input.actor,
@@ -3679,7 +3854,7 @@ async function invokeClaimedTool(turn, claimed) {
3679
3854
  }, () => invokeWithTransientRetry(() => {
3680
3855
  ui2.restart();
3681
3856
  return deps.registry.invoke(call.name, call.input, {
3682
- ...ctx,
3857
+ ...toolCallContext(ctx, call.id),
3683
3858
  emitUi: ui2.emit
3684
3859
  }, deps.rolesPolicy);
3685
3860
  }, deps.toolTransientRetry ?? {}, {
@@ -5074,6 +5249,13 @@ var InMemoryAgentStore = class {
5074
5249
  message.attachments = patch.attachments;
5075
5250
  }
5076
5251
  }
5252
+ if (patch.interrupt !== void 0) {
5253
+ if (patch.interrupt) {
5254
+ message.interrupt = true;
5255
+ } else {
5256
+ delete message.interrupt;
5257
+ }
5258
+ }
5077
5259
  message.updatedAt = this.now();
5078
5260
  return {
5079
5261
  ...message
@@ -5270,6 +5452,36 @@ var InMemoryAgentStore = class {
5270
5452
  } : {}
5271
5453
  });
5272
5454
  }
5455
+ async toolCallOutcomes(toolCallIds) {
5456
+ const outcomes = [];
5457
+ for (const id of toolCallIds) {
5458
+ const row = this.toolCalls.get(id);
5459
+ if (row !== void 0) {
5460
+ outcomes.push({
5461
+ id,
5462
+ status: row.status,
5463
+ ...row.output !== void 0 ? {
5464
+ output: row.output
5465
+ } : {},
5466
+ ...row.error !== void 0 ? {
5467
+ error: row.error
5468
+ } : {}
5469
+ });
5470
+ }
5471
+ }
5472
+ return outcomes;
5473
+ }
5474
+ async failUnsettledToolCalls(runId, error) {
5475
+ let settled = 0;
5476
+ for (const row of this.toolCalls.values()) {
5477
+ if (row.runId === runId && row.status === "pending_approval") {
5478
+ row.status = "failed";
5479
+ row.error = error;
5480
+ settled += 1;
5481
+ }
5482
+ }
5483
+ return settled;
5484
+ }
5273
5485
  async updateToolCall(input) {
5274
5486
  const row = this.toolCalls.get(input.toolCallId);
5275
5487
  if (row === void 0) {
@@ -5596,6 +5808,11 @@ export {
5596
5808
  REMEMBER_TOOL_DESCRIPTION,
5597
5809
  REMEMBER_TOOL_NAME,
5598
5810
  REQUESTER_APPROVER,
5811
+ RUN_ENDED_BEFORE_TOOL_CALL,
5812
+ RUN_FAILED_MESSAGE,
5813
+ RUN_NOT_ACTIVE_CODE,
5814
+ RUN_NOT_ACTIVE_MESSAGE,
5815
+ RUN_NO_LONGER_RUNNING,
5599
5816
  RunCancelledError,
5600
5817
  SKILL_TOOL_DESCRIPTION,
5601
5818
  SKILL_TOOL_NAME,
@@ -5606,6 +5823,7 @@ export {
5606
5823
  ToolInputInvalidError,
5607
5824
  ToolNotFoundError,
5608
5825
  ToolRegistry,
5826
+ UNFINISHED_TOOL_CALL,
5609
5827
  actorScope,
5610
5828
  agentDiagnosticKey,
5611
5829
  agentFailureCode,
@@ -5623,6 +5841,7 @@ export {
5623
5841
  createIncrementalGate,
5624
5842
  createNoopEmitUi,
5625
5843
  createUiCollector,
5844
+ danglingToolCallIds,
5626
5845
  dayBoundsUtc,
5627
5846
  decodeStreamEvent,
5628
5847
  defaultCanDecide,
@@ -5634,6 +5853,7 @@ export {
5634
5853
  estimateCost,
5635
5854
  estimateMessageTokens,
5636
5855
  exhaustedWindow,
5856
+ exposeStreamErrorDetails,
5637
5857
  extractJson,
5638
5858
  filterToolsByAllowList,
5639
5859
  filterToolsByCanUse,
@@ -5696,6 +5916,8 @@ export {
5696
5916
  runOutputProcessors,
5697
5917
  seedModelPrices,
5698
5918
  settleAll,
5919
+ settleDanglingToolCalls,
5920
+ settleDeadRun,
5699
5921
  settleElicitation,
5700
5922
  settleUnsettledDelegation,
5701
5923
  skillInputSchema,
@@ -5704,9 +5926,11 @@ export {
5704
5926
  stampToolKinds,
5705
5927
  staticModelCatalog,
5706
5928
  staticSkillProvider,
5929
+ streamFailure,
5707
5930
  summarizeWithModel,
5708
5931
  tenantScope,
5709
5932
  toolCallApprovalFromRow,
5933
+ toolCallContext,
5710
5934
  traceLlmTurn,
5711
5935
  traceToolExecution,
5712
5936
  truncateDetailContent,