@agent-native/core 0.214.0 → 0.216.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.
Files changed (161) hide show
  1. package/dist/action.js +54 -1
  2. package/dist/agent/actions/disconnect-chatgpt-subscription.js +5 -0
  3. package/dist/agent/actions/get-agentkit-capabilities.js +3 -3
  4. package/dist/agent/actions/manage-builder-connection.js +5 -0
  5. package/dist/agent/actions/manage-provider-key-policy.js +5 -0
  6. package/dist/agent/actions/manage-provider-models.js +5 -0
  7. package/dist/agent/actions/manage-service-providers.js +5 -0
  8. package/dist/agent/actions/select-chatgpt-subscription-account.js +5 -0
  9. package/dist/agent/context-xray/actions/context-evict.js +5 -0
  10. package/dist/agent/context-xray/actions/context-pin.js +5 -0
  11. package/dist/agent/context-xray/actions/context-report.js +5 -0
  12. package/dist/agent/context-xray/actions/context-restore.js +5 -0
  13. package/dist/agent/execution-fence.d.ts +1 -0
  14. package/dist/agent/execution-fence.js +5 -0
  15. package/dist/agent/production-agent.d.ts +35 -2
  16. package/dist/agent/production-agent.js +629 -32
  17. package/dist/agent/run-manager.d.ts +2 -0
  18. package/dist/agent/run-manager.js +397 -115
  19. package/dist/agent/run-slot-lock.d.ts +2 -0
  20. package/dist/agent/run-slot-lock.js +6 -0
  21. package/dist/agent/run-store.d.ts +5 -0
  22. package/dist/agent/run-store.js +423 -156
  23. package/dist/agent/thread-data-builder.js +27 -5
  24. package/dist/agent/types.d.ts +6 -12
  25. package/dist/agentkit/capabilities.d.ts +1 -1
  26. package/dist/application-state/store.d.ts +5 -0
  27. package/dist/application-state/store.js +26 -0
  28. package/dist/chat-threads/schema-migrations.js +17 -0
  29. package/dist/chat-threads/store.d.ts +21 -1
  30. package/dist/chat-threads/store.js +223 -41
  31. package/dist/cli/skills-content/turn-into-app-skill.d.ts +1 -1
  32. package/dist/cli/skills-content/turn-into-app-skill.js +20 -18
  33. package/dist/cli/templates-meta.js +14 -0
  34. package/dist/client/analytics.js +167 -17
  35. package/dist/client/chat/agentkit-agent-native.js +79 -3
  36. package/dist/client/chat/agentkit-protocol.js +80 -42
  37. package/dist/client/chat/runtime.d.ts +5 -1
  38. package/dist/client/chat/runtime.js +46 -3
  39. package/dist/client/consent.d.ts +36 -0
  40. package/dist/client/consent.js +283 -0
  41. package/dist/client/review/use-review.d.ts +2 -0
  42. package/dist/client/review/use-review.js +28 -1
  43. package/dist/client/sharing/useResourceAccessGate.d.ts +3 -2
  44. package/dist/client/sharing/useResourceAccessGate.js +9 -1
  45. package/dist/client/use-chat-thread-list.d.ts +2 -0
  46. package/dist/client/use-chat-thread-list.js +12 -2
  47. package/dist/client/use-chat-threads.d.ts +1 -0
  48. package/dist/client/use-chat-threads.js +2 -0
  49. package/dist/collab/awareness.d.ts +2 -2
  50. package/dist/collab/routes.d.ts +2 -2
  51. package/dist/collab/struct-routes.d.ts +1 -1
  52. package/dist/connections/catalog.d.ts +5 -5
  53. package/dist/connections/catalog.js +22 -3
  54. package/dist/data-widgets/index.d.ts +36 -2
  55. package/dist/data-widgets/index.js +94 -6
  56. package/dist/db/migrations.js +4 -1
  57. package/dist/db/schema-snapshot.generated.js +8 -1
  58. package/dist/feature-flags/actions/set-feature-flag.js +5 -0
  59. package/dist/feature-flags/index.d.ts +1 -1
  60. package/dist/feature-flags/index.js +1 -1
  61. package/dist/feature-flags/store.d.ts +12 -0
  62. package/dist/feature-flags/store.js +79 -7
  63. package/dist/file-upload/actions/manage-file-storage.js +5 -0
  64. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  65. package/dist/file-upload/actions/upload-image.js +5 -0
  66. package/dist/history/actions/create-resource-version.js +5 -0
  67. package/dist/history/actions/restore-resource-version.js +5 -0
  68. package/dist/ingestion/index.d.ts +1 -1
  69. package/dist/ingestion/index.js +1 -1
  70. package/dist/ingestion/pptx.d.ts +11 -0
  71. package/dist/ingestion/pptx.js +81 -1
  72. package/dist/integrations/actions/manage-messaging-channel.js +5 -0
  73. package/dist/jobs/scheduler.js +17 -1
  74. package/dist/labs/actions/set-lab.js +5 -0
  75. package/dist/localization/actions/set-localization-preference.js +5 -0
  76. package/dist/observability/actions/save-observability-instruction-update.js +5 -0
  77. package/dist/observability/actions/save-observability-review-summary.js +5 -0
  78. package/dist/progress/routes.d.ts +1 -1
  79. package/dist/provider-api/actions/custom-provider-registration.d.ts +18 -18
  80. package/dist/provider-api/actions/provider-api.d.ts +14 -14
  81. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  82. package/dist/provider-api/index.js +9 -2
  83. package/dist/review/actions/consume-review-feedback.js +5 -0
  84. package/dist/review/actions/create-review-comment.js +5 -0
  85. package/dist/review/actions/delete-review-comment.js +5 -0
  86. package/dist/review/actions/react-to-review-comment.js +5 -0
  87. package/dist/review/actions/reply-review-comment.js +5 -0
  88. package/dist/review/actions/resolve-review-thread.js +5 -0
  89. package/dist/review/actions/send-review-thread-to-agent.js +5 -0
  90. package/dist/review/actions/set-review-status.js +5 -0
  91. package/dist/review/actions/set-review-thread-muted.js +5 -0
  92. package/dist/review/actions/set-review-thread-unread.js +5 -0
  93. package/dist/review/actions/set-review-threads-unread.js +5 -0
  94. package/dist/review/actions/update-review-comment-anchor.js +5 -0
  95. package/dist/review/actions/update-review-comment.js +5 -0
  96. package/dist/review/suggestions/actions.js +60 -21
  97. package/dist/review/suggestions/store.d.ts +2 -0
  98. package/dist/review/suggestions/store.js +28 -0
  99. package/dist/secrets/actions/delete-api-key.js +5 -0
  100. package/dist/secrets/routes.d.ts +6 -6
  101. package/dist/server/agent-chat/context-tools.js +63 -2
  102. package/dist/server/agent-chat-plugin.d.ts +2 -0
  103. package/dist/server/agent-chat-plugin.js +242 -54
  104. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  105. package/dist/server/agent-engine-default-model-route.d.ts +2 -2
  106. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  107. package/dist/server/application-state-maintenance.d.ts +2 -0
  108. package/dist/server/application-state-maintenance.js +2 -0
  109. package/dist/server/attribution.d.ts +14 -0
  110. package/dist/server/attribution.js +62 -2
  111. package/dist/server/auth.js +7 -0
  112. package/dist/server/consent-route.d.ts +41 -0
  113. package/dist/server/consent-route.js +102 -0
  114. package/dist/server/first-touch-route.d.ts +39 -0
  115. package/dist/server/first-touch-route.js +115 -0
  116. package/dist/server/index.d.ts +1 -0
  117. package/dist/server/index.js +1 -0
  118. package/dist/server/queued-submission-inbox-drain.d.ts +9 -0
  119. package/dist/server/queued-submission-inbox-drain.js +12 -0
  120. package/dist/server/request-origin.d.ts +6 -0
  121. package/dist/server/request-origin.js +12 -0
  122. package/dist/settings/org-settings.d.ts +2 -1
  123. package/dist/settings/org-settings.js +4 -1
  124. package/dist/settings/store.d.ts +8 -0
  125. package/dist/settings/store.js +14 -5
  126. package/dist/shared/consent.d.ts +31 -0
  127. package/dist/shared/consent.js +145 -0
  128. package/dist/shared/first-touch-persistence.d.ts +31 -0
  129. package/dist/shared/first-touch-persistence.js +53 -0
  130. package/dist/sharing/actions/set-resource-visibility.js +5 -0
  131. package/dist/sharing/actions/share-resource.js +5 -0
  132. package/dist/sharing/actions/unshare-resource.js +5 -0
  133. package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +1 -1
  134. package/dist/templates/default/.agents/skills/actions/references/action-fields.md +1 -1
  135. package/dist/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +36 -80
  136. package/dist/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +54 -16
  137. package/dist/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +50 -73
  138. package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +1 -1
  139. package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -1
  140. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +20 -18
  141. package/dist/triggers/actions/manage-automation.d.ts +1 -1
  142. package/dist/usage/actions/get-usage-metrics.js +27 -4
  143. package/dist/usage/actions/manage-usage-alert.js +5 -0
  144. package/dist/vite/client.js +8 -6
  145. package/dist/workspace-connections/actions/bulk-update-workspace-user-groups.js +5 -0
  146. package/dist/workspace-connections/actions/delete-workspace-user-group.js +5 -0
  147. package/dist/workspace-connections/actions/upsert-workspace-user-group.js +5 -0
  148. package/docs/content/actions-access-control.mdx +8 -8
  149. package/docs/content/actions-defining.mdx +9 -8
  150. package/docs/content/template-design-developers.mdx +9 -9
  151. package/docs/content/template-design-features.mdx +1 -3
  152. package/docs/content/turn-into-app.mdx +4 -0
  153. package/package.json +4 -3
  154. package/src/templates/chat/.agents/skills/actions/references/action-fields.md +1 -1
  155. package/src/templates/default/.agents/skills/actions/references/action-fields.md +1 -1
  156. package/src/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +36 -80
  157. package/src/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +54 -16
  158. package/src/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +50 -73
  159. package/src/templates/headless/.agents/skills/actions/references/action-fields.md +1 -1
  160. package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +1 -1
  161. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +20 -18
@@ -12,7 +12,7 @@ import { getAgentEngineEntry } from "./engine/registry.js";
12
12
  import { EngineError } from "./engine/types.js";
13
13
  import { currentExecutionFence, MissingExecutionFenceError, StaleExecutionFenceError, withExecutionFence, withExecutionLeaseRequested, } from "./execution-fence.js";
14
14
  import { AgentRunEventNotPersistedError, insertRun, insertRunEvent, insertRunEvents, updateRunStatusIfRunning, markRunAborted, markRunAbortedAtSafeBoundary, getRunAbortState, getRunStatus, getRunEventsSince, getCurrentTurnRunEventsForThread, getRunById, getRunByThread, getRunTurnRef, listRunsForThread, markTurnAborted, cleanupOldRuns, updateRunHeartbeat, bumpRunProgress, setRunInFlightMarker, reapIfStale, reapUnclaimedBackgroundRun, shouldRedispatchUnclaimedBackgroundRun, reconcileTerminalRunFromEvents, ensureTerminalRunEvent, getLastTerminalRunEvent, resolveErroredRunTerminalEvent, setRunError, setRunTerminalReason, persistRunCheckpointEvent, recordRunDiagnostic, RUN_DIAG_STAGE, claimTurnExecutionLease, renewTurnExecutionLease, releaseTurnExecutionLease, TurnExecutionLeaseBusyError, terminalEventForAbortReason, RUN_RECORD_MISSING_ERROR_EVENT, RUN_RECORD_MISSING_GRACE_MS, RUN_TERMINAL_LOOKUP_FAILED_ERROR_EVENT, UNKNOWN_RUN_STATUS_ERROR_EVENT, } from "./run-store.js";
15
- import { isContinuationTerminalReason } from "./types.js";
15
+ import { CONTINUATION_REASONS, isContinuationTerminalReason } from "./types.js";
16
16
  const activeRuns = new Map();
17
17
  const threadToRun = new Map();
18
18
  function peekRunEventSeq(run) {
@@ -107,6 +107,13 @@ const RECONNECTABLE_TERMINAL_RUN_STATUSES = new Set([
107
107
  "errored",
108
108
  "aborted",
109
109
  ]);
110
+ function isKnownRunStatus(status) {
111
+ return (status === "running" ||
112
+ status === "completed" ||
113
+ status === "truncated" ||
114
+ status === "errored" ||
115
+ status === "aborted");
116
+ }
110
117
  function isDuplicateSubmissionMarker(run) {
111
118
  return (run.status === "aborted" &&
112
119
  run.terminalReason === "aborted:duplicate_submission");
@@ -371,6 +378,8 @@ function terminalReasonForRun(finalStatus, terminalEvent, abortReason, completio
371
378
  if (terminalEvent?.type === "error") {
372
379
  return `error:${terminalEvent.errorCode || "unknown"}`;
373
380
  }
381
+ if (finalStatus === "truncated")
382
+ return "stream_ended";
374
383
  if (finalStatus === "aborted")
375
384
  return `aborted:${abortReason ?? "user"}`;
376
385
  if (completionError)
@@ -584,6 +593,7 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
584
593
  },
585
594
  };
586
595
  let softTimedOut = false;
596
+ let checkpointBoundaryStarted = false;
587
597
  let resolveFinalized = () => { };
588
598
  let rejectFinalized = () => { };
589
599
  const finalized = new Promise((resolve, reject) => {
@@ -652,12 +662,16 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
652
662
  : undefined;
653
663
  const servicePrincipalRunStart = Boolean(options?.turnInitiator &&
654
664
  parseServiceIdentityEmail(options.turnInitiator.email));
665
+ let servicePrincipalStartRefused = false;
655
666
  const insertRunPromise = (options?.runRowAlreadyInserted && !servicePrincipalRunStart
656
667
  ? Promise.resolve()
657
668
  : insertOptions
658
669
  ? insertRun(runId, threadId, options?.turnId, insertOptions)
659
670
  : insertRun(runId, threadId, options?.turnId)).catch((error) => {
660
671
  captureRunPersistenceError(error, "insert-run");
672
+ if (error instanceof ServicePrincipalRefusedError) {
673
+ servicePrincipalStartRefused = true;
674
+ }
661
675
  if (options?.executionLease ||
662
676
  error instanceof ServicePrincipalRefusedError)
663
677
  throw error;
@@ -983,24 +997,140 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
983
997
  mirrorInFlightMarker(false);
984
998
  }
985
999
  };
1000
+ let checkpointRunBoundaryTask = Promise.resolve();
986
1001
  let checkpointAbortInFlight = false;
987
- const checkpointRunBoundary = async (event, terminalReason) => {
1002
+ let checkpointPersistenceFailed = false;
1003
+ let checkpointStatusReadFailed = false;
1004
+ let checkpointUnknownStatus = null;
1005
+ let checkpointObservedTerminalStatus = null;
1006
+ const observeCheckpointRunStatus = (persistedRun, context) => {
1007
+ if (!persistedRun) {
1008
+ checkpointStatusReadFailed = true;
1009
+ console.error(`[run-manager] run row missing ${context}`, runId);
1010
+ return "missing";
1011
+ }
1012
+ if (!isKnownRunStatus(persistedRun.status)) {
1013
+ checkpointUnknownStatus = persistedRun.status;
1014
+ return "unknown";
1015
+ }
1016
+ if (persistedRun.status === "running")
1017
+ return "running";
1018
+ checkpointObservedTerminalStatus = persistedRun.status;
1019
+ run.status = persistedRun.status;
1020
+ if (persistedRun.status === "aborted") {
1021
+ run.abortReason = persistedRun.terminalReason?.startsWith("aborted:")
1022
+ ? persistedRun.terminalReason.slice("aborted:".length)
1023
+ : undefined;
1024
+ }
1025
+ return "terminal";
1026
+ };
1027
+ let runDriverSettled = false;
1028
+ let pendingTerminalEvent = null;
1029
+ let persistedCheckpointTerminalEvent = null;
1030
+ const rememberPersistedCheckpointTerminalEvent = (persistedRun, checkpointEvent) => {
1031
+ if (persistedRun?.status !== "truncated")
1032
+ return;
1033
+ const persistedReason = persistedRun.terminalReason;
1034
+ const persistedContinuationReason = CONTINUATION_REASONS.find((reason) => reason === persistedReason);
1035
+ const reason = persistedContinuationReason ??
1036
+ (checkpointEvent.type === "auto_continue"
1037
+ ? checkpointEvent.reason
1038
+ : "stream_ended");
1039
+ persistedCheckpointTerminalEvent = {
1040
+ seq: peekRunEventSeq(run),
1041
+ event: { type: "auto_continue", reason },
1042
+ };
1043
+ };
1044
+ const checkpointRunBoundary = (event, terminalReason, diagnostic) => {
988
1045
  if (checkpointAbortInFlight ||
989
1046
  run.status !== "running" ||
990
1047
  abort.signal.aborted)
991
- return;
1048
+ return checkpointRunBoundaryTask;
992
1049
  checkpointAbortInFlight = true;
993
- try {
994
- await withWorkerFence(() => persistRunCheckpointEvent(runId, event, terminalReason));
995
- }
996
- catch {
997
- // The abort still has to happen if the checkpoint write is rejected; the
998
- // caller has already reached a server-owned chunk boundary.
999
- }
1000
- finally {
1001
- abort.abort(terminalReason);
1002
- checkpointAbortInFlight = false;
1003
- }
1050
+ checkpointBoundaryStarted = true;
1051
+ const task = (async () => {
1052
+ let checkpointPersisted = false;
1053
+ try {
1054
+ await withWorkerFence(() => persistRunCheckpointEvent(runId, event, terminalReason));
1055
+ checkpointPersisted = true;
1056
+ let persistedRun;
1057
+ try {
1058
+ persistedRun = await getRunById(runId);
1059
+ }
1060
+ catch (error) {
1061
+ checkpointStatusReadFailed = true;
1062
+ console.error("[run-manager] could not confirm run status after checkpoint persistence", runId, error instanceof Error ? error.message : error);
1063
+ return;
1064
+ }
1065
+ const checkpointStatus = observeCheckpointRunStatus(persistedRun, "after checkpoint persistence");
1066
+ if (checkpointStatus !== "running") {
1067
+ rememberPersistedCheckpointTerminalEvent(persistedRun, event);
1068
+ return;
1069
+ }
1070
+ softTimedOut = true;
1071
+ recordRunBoundaryDiagnostic(terminalReason, diagnostic, "terminal");
1072
+ emitRunBoundaryTrackingEvent({
1073
+ runId,
1074
+ threadId,
1075
+ reason: terminalReason,
1076
+ recovered: false,
1077
+ boundaryIndex: recoveredChunkBoundaries + 1,
1078
+ dispatchMode: options?.dispatchMode,
1079
+ model: options?.model,
1080
+ engineName: options?.engineName,
1081
+ userId: options?.userId,
1082
+ });
1083
+ if (!pendingTerminalEvent && run.status === "running") {
1084
+ send(event);
1085
+ persistedCheckpointTerminalEvent = pendingTerminalEvent;
1086
+ }
1087
+ }
1088
+ catch (error) {
1089
+ let persistedCheckpointStatus = "running";
1090
+ if (error instanceof AgentRunEventNotPersistedError &&
1091
+ error.terminalRun) {
1092
+ try {
1093
+ const persistedRun = await getRunById(runId);
1094
+ persistedCheckpointStatus = observeCheckpointRunStatus(persistedRun, "after checkpoint rejection");
1095
+ rememberPersistedCheckpointTerminalEvent(persistedRun, event);
1096
+ }
1097
+ catch (statusError) {
1098
+ checkpointStatusReadFailed = true;
1099
+ console.error("[run-manager] could not confirm run status after checkpoint rejection", runId, statusError instanceof Error ? statusError.message : statusError);
1100
+ }
1101
+ }
1102
+ if (!checkpointStatusReadFailed &&
1103
+ (persistedCheckpointStatus === "terminal" ||
1104
+ (pendingTerminalEvent && !checkpointPersisted))) {
1105
+ softTimedOut = true;
1106
+ }
1107
+ else if (!checkpointStatusReadFailed &&
1108
+ persistedCheckpointStatus !== "unknown") {
1109
+ checkpointPersistenceFailed = true;
1110
+ }
1111
+ if (persistedCheckpointStatus !== "terminal" &&
1112
+ persistedCheckpointStatus !== "unknown" &&
1113
+ !checkpointStatusReadFailed) {
1114
+ console.error("[run-manager] could not persist run boundary checkpoint", runId, error instanceof Error ? error.message : error);
1115
+ }
1116
+ if (!pendingTerminalEvent &&
1117
+ persistedCheckpointStatus !== "terminal" &&
1118
+ persistedCheckpointStatus !== "unknown" &&
1119
+ !checkpointStatusReadFailed) {
1120
+ send({
1121
+ type: "error",
1122
+ error: "The agent could not save its continuation checkpoint.",
1123
+ errorCode: "run_checkpoint_persistence_failed",
1124
+ });
1125
+ }
1126
+ }
1127
+ finally {
1128
+ abort.abort(run.abortReason ?? terminalReason);
1129
+ checkpointAbortInFlight = false;
1130
+ }
1131
+ })();
1132
+ checkpointRunBoundaryTask = task;
1133
+ return task;
1004
1134
  };
1005
1135
  const recordRunBoundaryDiagnostic = (reason, diagnostic, disposition) => {
1006
1136
  if (getCurrentRequestDbPoolScope()?.closing)
@@ -1016,7 +1146,7 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1016
1146
  })).catch(() => { }))).catch(() => { });
1017
1147
  };
1018
1148
  const reachRunBoundary = (reason, diagnostic = {}) => {
1019
- if (run.status !== "running" || abort.signal.aborted)
1149
+ if (runDriverSettled || run.status !== "running" || abort.signal.aborted)
1020
1150
  return;
1021
1151
  const activeChunkAbort = chunkAbort;
1022
1152
  const canRecoverChunk = activeChunkAbort !== null &&
@@ -1034,22 +1164,8 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1034
1164
  }
1035
1165
  if (chunkAbort)
1036
1166
  chunkBoundaryReason = reason;
1037
- softTimedOut = true;
1038
- recordRunBoundaryDiagnostic(reason, diagnostic, "terminal");
1039
- emitRunBoundaryTrackingEvent({
1040
- runId,
1041
- threadId,
1042
- reason,
1043
- recovered: false,
1044
- boundaryIndex: recoveredChunkBoundaries + 1,
1045
- dispatchMode: options?.dispatchMode,
1046
- model: options?.model,
1047
- engineName: options?.engineName,
1048
- userId: options?.userId,
1049
- });
1050
1167
  const event = { type: "auto_continue", reason };
1051
- send(event);
1052
- void checkpointRunBoundary(event, reason);
1168
+ void checkpointRunBoundary(event, reason, diagnostic);
1053
1169
  };
1054
1170
  const checkNoProgressBackstop = () => {
1055
1171
  if (noProgressTimeoutMs <= 0)
@@ -1204,7 +1320,6 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1204
1320
  };
1205
1321
  armChunkSoftTimeout();
1206
1322
  }
1207
- let pendingTerminalEvent = null;
1208
1323
  const captureRunError = (error, phase) => {
1209
1324
  const errorCode = getRunErrorCode(error);
1210
1325
  const engineError = error instanceof EngineError ? error : null;
@@ -1249,18 +1364,22 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1249
1364
  };
1250
1365
  const persistEventHook = options?.persistEvent;
1251
1366
  const persistRunEvent = async (runEvent, eventData, eventAt) => {
1252
- if (runEvent.event.type === "tool_start")
1367
+ const terminal = isTerminalRunEvent(runEvent.event);
1368
+ const requireInserted = runEvent.event.type === "tool_start" ||
1369
+ (terminal && !servicePrincipalStartRefused);
1370
+ if (requireInserted)
1253
1371
  await insertRunPromise;
1254
- const write = () => runEvent.event.type === "tool_start" ||
1255
- runEvent.event.type === "tool_done"
1256
- ? insertRunEvent(runId, runEvent.seq, eventData, {
1257
- eventAt,
1258
- toolInputSource: "execution",
1259
- ...(runEvent.event.type === "tool_start"
1260
- ? { requireInserted: true }
1261
- : {}),
1262
- })
1263
- : insertRunEvent(runId, runEvent.seq, eventData, { eventAt });
1372
+ const write = () => insertRunEvent(runId, runEvent.seq, eventData, {
1373
+ eventAt,
1374
+ ...(runEvent.event.type === "tool_start" ||
1375
+ runEvent.event.type === "tool_done"
1376
+ ? { toolInputSource: "execution" }
1377
+ : {}),
1378
+ ...(requireInserted ? { requireInserted: true } : {}),
1379
+ ...(runEvent.event.type === "auto_continue"
1380
+ ? { allowContinuationTerminal: true }
1381
+ : {}),
1382
+ });
1264
1383
  return withWorkerFence(() => options?.persistEvent
1265
1384
  ? options.persistEvent(write, {
1266
1385
  terminal: isTerminalRunEvent(runEvent.event),
@@ -1381,6 +1500,14 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1381
1500
  event: normalizedEvent,
1382
1501
  };
1383
1502
  if (isTerminalRunEvent(event)) {
1503
+ if (persistedCheckpointTerminalEvent)
1504
+ return Promise.resolve();
1505
+ if (checkpointPersistenceFailed &&
1506
+ pendingTerminalEvent?.event.type === "error" &&
1507
+ pendingTerminalEvent.event.errorCode ===
1508
+ "run_checkpoint_persistence_failed") {
1509
+ return Promise.resolve();
1510
+ }
1384
1511
  pendingTerminalEvent = runEvent;
1385
1512
  return Promise.resolve();
1386
1513
  }
@@ -1406,18 +1533,40 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1406
1533
  await runFn(send, runControl.chunkSignal, runControl);
1407
1534
  };
1408
1535
  const runPromise = startDriver()
1409
- .then(() => {
1536
+ .then(async () => {
1537
+ runDriverSettled = true;
1538
+ if (checkpointAbortInFlight)
1539
+ await checkpointRunBoundaryTask;
1410
1540
  settleBoundary(false);
1411
1541
  if (abort.signal.aborted) {
1412
- run.status = softTimedOut ? "completed" : "aborted";
1542
+ if (run.status !== "aborted") {
1543
+ run.status =
1544
+ checkpointObservedTerminalStatus ??
1545
+ (checkpointPersistenceFailed
1546
+ ? "errored"
1547
+ : softTimedOut
1548
+ ? "completed"
1549
+ : "aborted");
1550
+ }
1413
1551
  return;
1414
1552
  }
1415
1553
  run.status = "completed";
1416
1554
  })
1417
- .catch((err) => {
1555
+ .catch(async (err) => {
1556
+ runDriverSettled = true;
1557
+ if (checkpointAbortInFlight)
1558
+ await checkpointRunBoundaryTask;
1418
1559
  settleBoundary(false);
1419
1560
  if (abort.signal.aborted) {
1420
- run.status = softTimedOut ? "completed" : "aborted";
1561
+ if (run.status !== "aborted") {
1562
+ run.status =
1563
+ checkpointObservedTerminalStatus ??
1564
+ (checkpointPersistenceFailed
1565
+ ? "errored"
1566
+ : softTimedOut
1567
+ ? "completed"
1568
+ : "aborted");
1569
+ }
1421
1570
  return;
1422
1571
  }
1423
1572
  run.status = "errored";
@@ -1450,8 +1599,11 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1450
1599
  let eventPersistenceError = null;
1451
1600
  let runTerminalErrorCode;
1452
1601
  let terminalPersistenceEstablished = false;
1602
+ let continuationStatusPersistedBeforeEvent = false;
1603
+ let suppressContinuationEvent = false;
1453
1604
  try {
1454
1605
  await persistenceChain;
1606
+ await run.abortPersistencePromise;
1455
1607
  }
1456
1608
  catch (error) {
1457
1609
  eventPersistenceError = error;
@@ -1467,14 +1619,43 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1467
1619
  };
1468
1620
  }
1469
1621
  }
1622
+ if (checkpointStatusReadFailed || checkpointUnknownStatus !== null) {
1623
+ const event = checkpointStatusReadFailed
1624
+ ? RUN_TERMINAL_LOOKUP_FAILED_ERROR_EVENT
1625
+ : UNKNOWN_RUN_STATUS_ERROR_EVENT;
1626
+ const abortAlreadyWon = run.status === "aborted" && run.abortReason !== undefined;
1627
+ if (!abortAlreadyWon) {
1628
+ run.status = "errored";
1629
+ if (!eventPersistenceError) {
1630
+ pendingTerminalEvent = {
1631
+ seq: peekRunEventSeq(run),
1632
+ event: { ...event },
1633
+ };
1634
+ }
1635
+ }
1636
+ if (checkpointUnknownStatus !== null) {
1637
+ console.error("[run-manager] checkpoint observed an unknown persisted run status", runId, checkpointUnknownStatus);
1638
+ }
1639
+ }
1470
1640
  const resolveTerminalEventForCompletion = () => {
1471
- if (run.status === "aborted")
1641
+ if (run.status === "aborted" && run.abortReason !== undefined)
1472
1642
  return null;
1473
1643
  if (eventPersistenceError) {
1474
1644
  return pendingTerminalEvent
1475
1645
  ? { ...pendingTerminalEvent, seq: run.events.length }
1476
1646
  : null;
1477
1647
  }
1648
+ if (checkpointStatusReadFailed || checkpointUnknownStatus !== null) {
1649
+ return pendingTerminalEvent
1650
+ ? { ...pendingTerminalEvent, seq: peekRunEventSeq(run) }
1651
+ : null;
1652
+ }
1653
+ if (persistedCheckpointTerminalEvent) {
1654
+ return {
1655
+ ...persistedCheckpointTerminalEvent,
1656
+ seq: peekRunEventSeq(run),
1657
+ };
1658
+ }
1478
1659
  const continuationTerminalEvent = run.continuationTerminalEvent
1479
1660
  ? {
1480
1661
  seq: peekRunEventSeq(run),
@@ -1489,7 +1670,9 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1489
1670
  };
1490
1671
  let terminalEventForCompletion = resolveTerminalEventForCompletion();
1491
1672
  let terminalEvent = terminalEventForCompletion?.event ?? null;
1492
- if (onComplete) {
1673
+ if (onComplete &&
1674
+ !checkpointStatusReadFailed &&
1675
+ checkpointUnknownStatus === null) {
1493
1676
  try {
1494
1677
  const completionStatus = run.status !== "aborted" &&
1495
1678
  terminalEventForcesErroredStatus(terminalEvent)
@@ -1524,9 +1707,13 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1524
1707
  completionError ||
1525
1708
  terminalEventForcesErroredStatus(terminalEvent)
1526
1709
  ? "errored"
1527
- : "completed";
1710
+ : run.status === "truncated"
1711
+ ? "truncated"
1712
+ : "completed";
1528
1713
  run.status = finalStatus;
1529
1714
  const shouldAutoContinueAfterUnfinishedTurn = finalStatus === "completed" &&
1715
+ !checkpointStatusReadFailed &&
1716
+ checkpointUnknownStatus === null &&
1530
1717
  (endsAfterToolResultWithoutAssistantFinal(run) ||
1531
1718
  endsDuringActionPreparation(run)) &&
1532
1719
  (!terminalEventForCompletion ||
@@ -1549,16 +1736,36 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1549
1736
  ? "truncated"
1550
1737
  : finalStatus;
1551
1738
  let volatileTerminalEvent = null;
1552
- const persistTerminalStatus = () => completionError
1553
- ? updateRunStatusIfRunning(runId, persistedStatus, {
1554
- threadProjectionPending: true,
1555
- })
1556
- : updateRunStatusIfRunning(runId, persistedStatus);
1557
- const reconcileTerminalRun = () => completionError
1739
+ const threadProjectionPending = Boolean(completionError ||
1740
+ (onComplete &&
1741
+ (checkpointStatusReadFailed || checkpointUnknownStatus !== null)));
1742
+ const terminalStatusOptions = {
1743
+ ...(threadProjectionPending ? { threadProjectionPending: true } : {}),
1744
+ ...(checkpointBoundaryStarted || persistedStatus === "truncated"
1745
+ ? { terminalReason }
1746
+ : {}),
1747
+ };
1748
+ const persistTerminalStatus = () => Object.keys(terminalStatusOptions).length === 0
1749
+ ? updateRunStatusIfRunning(runId, persistedStatus)
1750
+ : updateRunStatusIfRunning(runId, persistedStatus, terminalStatusOptions);
1751
+ const reconcileTerminalRun = () => threadProjectionPending
1558
1752
  ? reconcileTerminalRunFromEvents(runId, {
1559
1753
  threadProjectionPending: true,
1560
1754
  })
1561
1755
  : reconcileTerminalRunFromEvents(runId);
1756
+ const reportTerminalStatusPersistenceError = (error) => {
1757
+ captureError(error, {
1758
+ route: "/_agent-native/agent-chat",
1759
+ aiTraceId: runId,
1760
+ tags: {
1761
+ source: "agent-run-manager",
1762
+ phase: "terminal-status-persistence",
1763
+ errorCode: "run_terminal_status_persistence_failed",
1764
+ },
1765
+ extra: { runId, threadId, turnId: run.turnId },
1766
+ });
1767
+ console.error("[run-manager] terminal status persistence error:", error instanceof Error ? error.message : error);
1768
+ };
1562
1769
  if (finalStatus === "completed" || finalStatus === "errored") {
1563
1770
  const terminalEventToEmit = eventPersistenceError
1564
1771
  ? {
@@ -1581,8 +1788,37 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1581
1788
  ? "Agent response could not be saved."
1582
1789
  : "Agent run ended unexpectedly",
1583
1790
  };
1791
+ if (terminalEventToEmit.type === "auto_continue") {
1792
+ if (persistedStatus !== "truncated") {
1793
+ suppressContinuationEvent = true;
1794
+ }
1795
+ else {
1796
+ try {
1797
+ await insertRunPromise;
1798
+ continuationStatusPersistedBeforeEvent = await withWorkerFence(persistTerminalStatus);
1799
+ if (!continuationStatusPersistedBeforeEvent) {
1800
+ await withWorkerFence(reconcileTerminalRun);
1801
+ const persistedRun = await getRunById(runId);
1802
+ continuationStatusPersistedBeforeEvent =
1803
+ persistedRun?.status === "truncated" &&
1804
+ persistedRun.terminalReason === terminalReason;
1805
+ }
1806
+ if (continuationStatusPersistedBeforeEvent) {
1807
+ terminalPersistenceEstablished = true;
1808
+ }
1809
+ else {
1810
+ suppressContinuationEvent = true;
1811
+ }
1812
+ }
1813
+ catch (error) {
1814
+ suppressContinuationEvent = true;
1815
+ reportTerminalStatusPersistenceError(error);
1816
+ }
1817
+ }
1818
+ }
1584
1819
  const last = run.events[run.events.length - 1];
1585
- if (!last || !isTerminalRunEvent(last.event)) {
1820
+ if ((!last || !isTerminalRunEvent(last.event)) &&
1821
+ !suppressContinuationEvent) {
1586
1822
  const terminal = {
1587
1823
  seq: terminalEventForCompletion?.seq ?? allocateRunEventSeq(run),
1588
1824
  event: terminalEventToEmit,
@@ -1621,27 +1857,40 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1621
1857
  if (!executionLeaseClaimFailed)
1622
1858
  mirrorInFlightMarker(false);
1623
1859
  }
1624
- const reportTerminalStatusPersistenceError = (error) => {
1625
- captureError(error, {
1626
- route: "/_agent-native/agent-chat",
1627
- aiTraceId: runId,
1628
- tags: {
1629
- source: "agent-run-manager",
1630
- phase: "terminal-status-persistence",
1631
- errorCode: "run_terminal_status_persistence_failed",
1632
- },
1633
- extra: { runId, threadId, turnId: run.turnId },
1634
- });
1635
- console.error("[run-manager] terminal status persistence error:", error instanceof Error ? error.message : error);
1636
- };
1637
- if (executionLeaseClaimFailed) {
1638
- await insertRunPromise;
1639
- await markRunAborted(runId, run.abortReason ?? "execution-lease-claim-failed");
1860
+ if (executionLeaseClaimFailed || finalStatus === "aborted") {
1861
+ if (!executionLeaseClaimFailed && run.abortPersistenceManaged) {
1862
+ terminalPersistenceEstablished = true;
1863
+ }
1864
+ else if (!executionLeaseClaimFailed &&
1865
+ (run.abortReason === "superseded" ||
1866
+ run.abortReason === "execution_lease_lost")) {
1867
+ terminalPersistenceEstablished = false;
1868
+ }
1869
+ else {
1870
+ try {
1871
+ await insertRunPromise;
1872
+ const persistAtomicAbort = () => markRunAborted(runId, run.abortReason ?? "execution-lease-claim-failed");
1873
+ const aborted = executionLeaseClaimFailed
1874
+ ? await persistAtomicAbort()
1875
+ : await withWorkerFence(persistAtomicAbort);
1876
+ terminalPersistenceEstablished = aborted
1877
+ ? true
1878
+ : await withWorkerFence(reconcileTerminalRun);
1879
+ }
1880
+ catch (error) {
1881
+ terminalPersistenceEstablished = false;
1882
+ reportTerminalStatusPersistenceError(error);
1883
+ throw error;
1884
+ }
1885
+ }
1640
1886
  }
1641
1887
  else {
1642
1888
  try {
1643
- await insertRunPromise;
1644
- if (!terminalPersistenceError || eventPersistenceError) {
1889
+ if (!continuationStatusPersistedBeforeEvent) {
1890
+ await insertRunPromise;
1891
+ }
1892
+ if (!continuationStatusPersistedBeforeEvent &&
1893
+ (!terminalPersistenceError || eventPersistenceError)) {
1645
1894
  let statusUpdated = false;
1646
1895
  try {
1647
1896
  statusUpdated = await withWorkerFence(persistTerminalStatus);
@@ -1650,8 +1899,10 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
1650
1899
  reportTerminalStatusPersistenceError(error);
1651
1900
  }
1652
1901
  if (statusUpdated) {
1902
+ if (!checkpointBoundaryStarted) {
1903
+ await withWorkerFence(() => setRunTerminalReason(runId, terminalReason));
1904
+ }
1653
1905
  terminalPersistenceEstablished = !terminalPersistenceError;
1654
- await withWorkerFence(() => setRunTerminalReason(runId, terminalReason));
1655
1906
  }
1656
1907
  else {
1657
1908
  terminalPersistenceEstablished =
@@ -2102,14 +2353,15 @@ function subscribeFromSQL(runId, fromSeq) {
2102
2353
  return;
2103
2354
  if (run?.status === "aborted") {
2104
2355
  const existing = await getLastTerminalRunEvent(runId).catch(() => null);
2356
+ const persistedTerminal = existing?.event.type === "auto_continue" ? null : existing;
2105
2357
  const abortReason = run.terminalReason?.startsWith("aborted:")
2106
2358
  ? run.terminalReason.slice("aborted:".length)
2107
2359
  : undefined;
2108
- const terminalEvent = existing
2109
- ? existing.event
2360
+ const terminalEvent = persistedTerminal
2361
+ ? persistedTerminal.event
2110
2362
  : terminalEventForAbortReason(abortReason);
2111
2363
  try {
2112
- controller.enqueue(encoder.encode(`data: ${JSON.stringify(streamEventWithIdentity(runId, terminalEvent, existing?.seq ?? lastSeq))}\n\n`));
2364
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(streamEventWithIdentity(runId, terminalEvent, persistedTerminal?.seq ?? lastSeq))}\n\n`));
2113
2365
  }
2114
2366
  catch {
2115
2367
  cancelled = true;
@@ -2497,9 +2749,15 @@ export function abortRun(runId, reason = "user") {
2497
2749
  const run = activeRuns.get(runId);
2498
2750
  const abortedInMemory = abortRunInMemory(runId, reason);
2499
2751
  const markAborted = () => markRunAborted(runId, reason);
2500
- void flushRunEventsBeforeAbort(run)
2752
+ const abortPersistence = flushRunEventsBeforeAbort(run)
2501
2753
  .then(markAborted, markAborted)
2502
- .catch((error) => {
2754
+ .then((persisted) => {
2755
+ if (run)
2756
+ run.abortPersistenceManaged = persisted;
2757
+ return persisted;
2758
+ }, (error) => {
2759
+ if (run)
2760
+ run.abortPersistenceManaged = false;
2503
2761
  captureError(error, {
2504
2762
  route: "/_agent-native/agent-chat/runs/:id/abort",
2505
2763
  aiTraceId: runId,
@@ -2507,7 +2765,10 @@ export function abortRun(runId, reason = "user") {
2507
2765
  extra: { runId, threadId: run?.threadId, reason },
2508
2766
  });
2509
2767
  console.error("[run-manager] durable abort status write failed:", error instanceof Error ? error.message : error);
2768
+ return false;
2510
2769
  });
2770
+ if (run)
2771
+ run.abortPersistencePromise = abortPersistence;
2511
2772
  return abortedInMemory;
2512
2773
  }
2513
2774
  /**
@@ -2519,44 +2780,65 @@ export function abortRun(runId, reason = "user") {
2519
2780
  export async function abortRunDurably(runId, reason = "user", options) {
2520
2781
  const run = activeRuns.get(runId);
2521
2782
  const abortedInMemory = abortRunInMemory(runId, reason);
2522
- try {
2523
- const eventFlushFailure = await flushRunEventsBeforeAbort(run);
2524
- const abortPersisted = await markRunAborted(runId, reason);
2525
- if (!abortPersisted || eventFlushFailure) {
2526
- const persistedStatus = await getRunStatus(runId);
2527
- if (persistedStatus === null) {
2528
- // Agent Teams persists Stop intent before run registration.
2529
- // Workers recheck it before dispatch.
2530
- if (options?.allowNotStarted && !run && !eventFlushFailure)
2531
- return false;
2532
- throw new Error(`Cannot confirm durable abort for missing run ${runId}`);
2533
- }
2534
- if (!abortPersisted && persistedStatus === "running") {
2535
- throw new Error(`Durable abort did not transition run ${runId}`);
2783
+ const persistence = (async () => {
2784
+ let eventFlushFailure;
2785
+ try {
2786
+ eventFlushFailure = await flushRunEventsBeforeAbort(run);
2787
+ }
2788
+ catch (error) {
2789
+ if (run)
2790
+ run.abortPersistenceManaged = false;
2791
+ throw error;
2792
+ }
2793
+ let abortPersistedDurably = false;
2794
+ try {
2795
+ const abortPersisted = await markRunAborted(runId, reason);
2796
+ abortPersistedDurably = abortPersisted;
2797
+ if (!abortPersisted || eventFlushFailure) {
2798
+ const persistedStatus = await getRunStatus(runId);
2799
+ abortPersistedDurably ||= persistedStatus === "aborted";
2800
+ if (persistedStatus === null) {
2801
+ // Agent Teams persists Stop intent before run registration.
2802
+ // Workers recheck it before dispatch.
2803
+ if (options?.allowNotStarted && !run && !eventFlushFailure)
2804
+ return false;
2805
+ throw new Error(`Cannot confirm durable abort for missing run ${runId}`);
2806
+ }
2807
+ if (!abortPersisted && persistedStatus === "running") {
2808
+ throw new Error(`Durable abort did not transition run ${runId}`);
2809
+ }
2536
2810
  }
2811
+ if (run)
2812
+ run.abortPersistenceManaged = abortPersistedDurably;
2813
+ if (eventFlushFailure)
2814
+ throw eventFlushFailure;
2537
2815
  }
2538
- if (eventFlushFailure)
2539
- throw eventFlushFailure;
2540
- }
2541
- catch (error) {
2542
- captureError(error, {
2543
- route: "/_agent-native/agent-chat/runs/:id/abort",
2544
- aiTraceId: runId,
2545
- tags: {
2546
- source: "agent-run-manager",
2547
- phase: "abort-run",
2548
- },
2549
- extra: {
2550
- runId,
2551
- threadId: activeRuns.get(runId)?.threadId,
2552
- reason,
2553
- abortedInMemory,
2554
- },
2555
- });
2556
- console.error("[run-manager] durable abort persistence failed:", error instanceof Error ? error.message : error);
2557
- throw error;
2816
+ catch (error) {
2817
+ if (run)
2818
+ run.abortPersistenceManaged = abortPersistedDurably;
2819
+ captureError(error, {
2820
+ route: "/_agent-native/agent-chat/runs/:id/abort",
2821
+ aiTraceId: runId,
2822
+ tags: {
2823
+ source: "agent-run-manager",
2824
+ phase: "abort-run",
2825
+ },
2826
+ extra: {
2827
+ runId,
2828
+ threadId: activeRuns.get(runId)?.threadId,
2829
+ reason,
2830
+ abortedInMemory,
2831
+ },
2832
+ });
2833
+ console.error("[run-manager] durable abort persistence failed:", error instanceof Error ? error.message : error);
2834
+ throw error;
2835
+ }
2836
+ return abortedInMemory;
2837
+ })();
2838
+ if (run) {
2839
+ run.abortPersistencePromise = persistence.then(() => run.abortPersistenceManaged ?? false, () => run.abortPersistenceManaged ?? false);
2558
2840
  }
2559
- return abortedInMemory;
2841
+ return persistence;
2560
2842
  }
2561
2843
  async function abortRunAtSafeBoundaryDurablyResult(runId, reason = "user") {
2562
2844
  const run = activeRuns.get(runId);