@agent-native/core 0.210.1-nightly-20261020124228 → 0.210.2

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 (34) hide show
  1. package/dist/a2a/server.js +2 -1
  2. package/dist/agent/contain-principal.js +2 -3
  3. package/dist/agent/engine/ai-sdk-engine.js +29 -20
  4. package/dist/agent/engine/anthropic-engine.js +10 -1
  5. package/dist/agent/engine/builder-engine.js +28 -14
  6. package/dist/agent/engine/types.d.ts +11 -0
  7. package/dist/agent/engine/types.js +8 -0
  8. package/dist/agent/execution-fence.d.ts +5 -0
  9. package/dist/agent/execution-fence.js +15 -0
  10. package/dist/agent/harness/background.js +28 -7
  11. package/dist/agent/production-agent.d.ts +4 -3
  12. package/dist/agent/production-agent.js +20 -3
  13. package/dist/agent/run-manager.d.ts +8 -1
  14. package/dist/agent/run-manager.js +273 -34
  15. package/dist/agent/run-store.d.ts +3 -2
  16. package/dist/agent/run-store.fencing.worker.js +19 -1
  17. package/dist/agent/run-store.js +216 -67
  18. package/dist/client/use-agent-engine-configured.d.ts +2 -2
  19. package/dist/client/use-agent-engine-configured.js +3 -9
  20. package/dist/collab/awareness.d.ts +2 -2
  21. package/dist/collab/routes.d.ts +1 -1
  22. package/dist/collab/struct-routes.d.ts +1 -1
  23. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  24. package/dist/notifications/routes.d.ts +1 -1
  25. package/dist/observability/routes.d.ts +5 -5
  26. package/dist/progress/routes.d.ts +1 -1
  27. package/dist/secrets/routes.d.ts +4 -4
  28. package/dist/server/agent-chat-plugin.js +27 -11
  29. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  30. package/dist/server/agent-engine-default-model-route.d.ts +1 -1
  31. package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
  32. package/dist/server/agent-teams.d.ts +3 -0
  33. package/dist/server/agent-teams.js +192 -30
  34. package/package.json +4 -4
@@ -27,39 +27,39 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
27
27
  thumbsUpRate: number;
28
28
  avgEvalScore: number;
29
29
  } | {
30
- ok?: undefined;
31
30
  error: string;
32
31
  message: string;
33
32
  summary?: undefined;
34
33
  spans?: undefined;
34
+ ok?: undefined;
35
35
  } | {
36
36
  error?: undefined;
37
- ok?: undefined;
38
37
  message?: undefined;
39
38
  summary: import("./types.js").TraceSummary;
40
39
  spans: import("./types.js").TraceSpan[];
40
+ ok?: undefined;
41
41
  } | {
42
42
  error?: undefined;
43
- ok?: undefined;
44
43
  message?: undefined;
45
44
  summary?: undefined;
46
45
  spans?: undefined;
47
46
  traceMissing: true;
48
47
  id: string;
48
+ ok?: undefined;
49
49
  } | {
50
50
  error?: undefined;
51
- ok?: undefined;
52
51
  message?: undefined;
53
52
  summary?: undefined;
54
53
  spans?: undefined;
55
54
  traceMissing?: undefined;
56
55
  id: string;
57
- } | {
58
56
  ok?: undefined;
57
+ } | {
59
58
  message?: undefined;
60
59
  summary?: undefined;
61
60
  spans?: undefined;
62
61
  error: any;
62
+ ok?: undefined;
63
63
  } | {
64
64
  error?: undefined;
65
65
  message?: undefined;
@@ -2,6 +2,6 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
2
2
  error: string;
3
3
  ok?: undefined;
4
4
  } | {
5
- ok: boolean;
6
5
  error?: undefined;
6
+ ok: boolean;
7
7
  }>>;
@@ -57,31 +57,31 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
57
57
  } | {
58
58
  error: string;
59
59
  errorCode?: undefined;
60
- ok?: undefined;
61
60
  status?: undefined;
61
+ ok?: undefined;
62
62
  } | {
63
63
  error: string;
64
64
  errorCode: string;
65
- ok?: undefined;
66
65
  status?: undefined;
66
+ ok?: undefined;
67
67
  } | {
68
68
  error?: undefined;
69
69
  errorCode?: undefined;
70
70
  ok: boolean;
71
71
  status: string;
72
72
  } | {
73
- ok?: undefined;
74
73
  error: string;
75
74
  removed?: undefined;
75
+ ok?: undefined;
76
76
  } | {
77
77
  error?: undefined;
78
78
  ok: boolean;
79
79
  removed: boolean;
80
80
  }>>;
81
81
  export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
82
- ok?: undefined;
83
82
  error: string;
84
83
  note?: undefined;
84
+ ok?: undefined;
85
85
  } | {
86
86
  error?: undefined;
87
87
  ok: boolean;
@@ -20,7 +20,7 @@ import { SYSTEM_PROMPT_CACHE_SPLIT } from "../agent/engine/prompt-cache.js";
20
20
  import { PROVIDER_TO_ENV } from "../agent/engine/provider-env-vars.js";
21
21
  import { currentExecutionFence, withExecutionFence, } from "../agent/execution-fence.js";
22
22
  import { hostedHarnessSystemPrompt } from "../agent/harness/hosted.js";
23
- import { createProductionAgentHandler, endsAtContinuationBoundary, actionsToEngineTools, executeAgentToolCall, filterActionsByAllowedNames, normalizeAgentActionSurfaceResolution, getActiveRunForThreadAsync, abortRunDurably, abortRunAtSafeBoundaryDurably, abortTurnByRefDurably, abortTurnDurably, subscribeToRun, } from "../agent/production-agent.js";
23
+ import { createProductionAgentHandler, endsAtContinuationBoundary, actionsToEngineTools, executeAgentToolCall, filterActionsByAllowedNames, normalizeAgentActionSurfaceResolution, getActiveRunForThreadAsync, abortRunDurably, abortRunAndTurnDurably, abortRunAtSafeBoundaryDurably, abortTurnByRefDurably, abortTurnDurably, subscribeToRun, } from "../agent/production-agent.js";
24
24
  import { applyProviderModelSelection, providerForEngineName, resolveProviderModelSelectionAtScope, } from "../agent/provider-model-selection.js";
25
25
  import { startRun } from "../agent/run-manager.js";
26
26
  import { callerHasRunAccess, callerHasThreadAccess, } from "../agent/run-ownership.js";
@@ -420,7 +420,7 @@ export async function runA2AAgentLoopWithRunManager(runOptions, pluginOptions, t
420
420
  model: runOptions.model,
421
421
  engineName: runOptions.engine.name,
422
422
  });
423
- let cancellation = Promise.resolve(false);
423
+ let cancellation = Promise.resolve({ status: "fulfilled", aborted: false });
424
424
  const abortFromTask = () => {
425
425
  const reason = taskSignal?.reason;
426
426
  const reasonCode = reason && typeof reason === "object" && "code" in reason
@@ -431,7 +431,7 @@ export async function runA2AAgentLoopWithRunManager(runOptions, pluginOptions, t
431
431
  run.abort.abort("execution_lease_lost");
432
432
  return;
433
433
  }
434
- cancellation = abortRunDurably(runId, clientAbortReason(reason));
434
+ cancellation = abortRunDurably(runId, clientAbortReason(reason)).then((aborted) => ({ status: "fulfilled", aborted }), (error) => ({ status: "rejected", error }));
435
435
  };
436
436
  if (taskSignal?.aborted) {
437
437
  abortFromTask();
@@ -440,8 +440,17 @@ export async function runA2AAgentLoopWithRunManager(runOptions, pluginOptions, t
440
440
  taskSignal?.addEventListener("abort", abortFromTask, { once: true });
441
441
  }
442
442
  try {
443
- await run.finalized;
444
- await cancellation;
443
+ const finalization = await run.finalized.then(() => ({ status: "fulfilled" }), (error) => ({ status: "rejected", error }));
444
+ const cancellationResult = await cancellation;
445
+ if (finalization.status === "rejected" &&
446
+ cancellationResult.status === "rejected") {
447
+ throw new AggregateError([finalization.error, cancellationResult.error], "A2A run finalization and durable cancellation both failed");
448
+ }
449
+ if (finalization.status === "rejected")
450
+ throw finalization.error;
451
+ if (cancellationResult.status === "rejected") {
452
+ throw cancellationResult.error;
453
+ }
445
454
  }
446
455
  finally {
447
456
  taskSignal?.removeEventListener("abort", abortFromTask);
@@ -5262,10 +5271,15 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
5262
5271
  // Empty/invalid body — keep the default user abort reason.
5263
5272
  }
5264
5273
  if (safeBoundary) {
5265
- const aborted = await abortRunAtSafeBoundaryDurably(runId, reason);
5266
- if (USER_STOP_ABORT_REASONS.has(reason)) {
5267
- const latest = aborted ? null : await getRunById(runId);
5268
- if (aborted || latest?.status === "aborted") {
5274
+ const isUserStop = USER_STOP_ABORT_REASONS.has(reason);
5275
+ const aborted = isUserStop
5276
+ ? await abortRunAndTurnDurably(runId, reason, {
5277
+ safeBoundary: true,
5278
+ })
5279
+ : await abortRunAtSafeBoundaryDurably(runId, reason);
5280
+ if (isUserStop && !aborted) {
5281
+ const latest = await getRunById(runId);
5282
+ if (latest?.status === "aborted") {
5269
5283
  await abortTurnDurably(runId, reason);
5270
5284
  }
5271
5285
  }
@@ -5274,9 +5288,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
5274
5288
  // Recovery starts as soon as this response resolves, so wait for
5275
5289
  // the cross-isolate abort + terminal event to be durable. Returning
5276
5290
  // early lets Retry collide with the still-running row.
5277
- await abortRunDurably(runId, reason);
5278
5291
  if (USER_STOP_ABORT_REASONS.has(reason)) {
5279
- await abortTurnDurably(runId, reason);
5292
+ await abortRunAndTurnDurably(runId, reason);
5293
+ }
5294
+ else {
5295
+ await abortRunDurably(runId, reason);
5280
5296
  }
5281
5297
  return { ok: true };
5282
5298
  }
@@ -66,6 +66,7 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
66
66
  error: any;
67
67
  code: any;
68
68
  } | {
69
+ error?: undefined;
69
70
  errorCode?: undefined;
70
71
  code?: undefined;
71
72
  ok: boolean;
@@ -73,6 +74,5 @@ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHand
73
74
  baseUrlKey?: string;
74
75
  scope: AgentEngineApiKeyScope;
75
76
  defaultModel?: import("./agent-engine-default-model-route.js").SavedKeyDefaultModelOutcome;
76
- error?: undefined;
77
77
  }>>;
78
78
  export {};
@@ -8,8 +8,8 @@ export declare const AGENT_ENGINE_API_KEY_ACTION_NAME = "agent-engine-api-key";
8
8
  * resolveEngine's credential detection.
9
9
  */
10
10
  export declare function createAgentEngineDisconnectHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
11
- ok?: undefined;
12
11
  error: string;
12
+ ok?: undefined;
13
13
  scope?: undefined;
14
14
  } | {
15
15
  ok: boolean;
@@ -1,9 +1,9 @@
1
1
  export declare function createAgentEngineOllamaModelsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
2
2
  error: string;
3
- ok?: undefined;
4
3
  models?: undefined;
4
+ ok?: undefined;
5
5
  } | {
6
- error?: undefined;
7
6
  ok: boolean;
8
7
  models: string[];
8
+ error?: undefined;
9
9
  }>>;
@@ -44,6 +44,9 @@ export interface AgentTask {
44
44
  terminalEffectsVersion?: 1;
45
45
  terminalEffectsReconciled?: boolean;
46
46
  terminalProgressStatus?: TerminalProgressStatus;
47
+ stopRequestedAt?: number;
48
+ stopRequestedRunId?: string;
49
+ stopRequestedReason?: string;
47
50
  }
48
51
  export interface AgentTeamOwnerScope {
49
52
  ownerEmail: string | null;
@@ -5,7 +5,7 @@ import { resolveMainChatMaxOutputTokens } from "../agent/engine/output-tokens.js
5
5
  import { currentExecutionFence, withExecutionFence, withFencedExecutionWrite, } from "../agent/execution-fence.js";
6
6
  import { actionsToEngineTools, filterActionsByAllowedNames, filterInitialEngineTools, readPersistedAllowedActionNames, resolveAgentRequestReasoningEffort, } from "../agent/production-agent.js";
7
7
  import { runAgentLoop, appendAgentLoopContinuation, } from "../agent/production-agent.js";
8
- import { abortRun, getActiveRunForThreadAsync, getRun, startRun, subscribeToRun, } from "../agent/run-manager.js";
8
+ import { abortRun, abortRunDurably, getActiveRunForThreadAsync, getRun, startRun, subscribeToRun, } from "../agent/run-manager.js";
9
9
  import { ensureRunTables, getRunEventsSince, lockTaskExecutionLease, revokeTaskExecutionLease, } from "../agent/run-store.js";
10
10
  import { buildAssistantMessageFromRunJournal } from "../agent/run-thread-projector.js";
11
11
  import { resolveMaxSubagentDelegationDepth } from "../agent/runtime-context.js";
@@ -466,6 +466,8 @@ async function failReconciledTask(task, ownerEmail, message, progressStatus = "f
466
466
  ? undefined
467
467
  : structuredClone(currentTask);
468
468
  Object.assign(task, currentTask);
469
+ if (task.stopRequestedAt !== undefined)
470
+ return false;
469
471
  cancelled ||= isCancelledAgentTask(task);
470
472
  terminalProgressStatus = cancelled ? "cancelled" : progressStatus;
471
473
  task.status = "errored";
@@ -1407,6 +1409,9 @@ async function stopAgentTeamRunAfterActionPersistenceError(task, claimedAttempts
1407
1409
  }
1408
1410
  async function finalizeAgentTeamRun(task, run, ownerEmail, fullText, options) {
1409
1411
  const result = await withCurrentAgentTeamRunAttempt(task.taskId, options.claimedAttempts, async () => {
1412
+ const currentTask = await loadTask(task.taskId);
1413
+ if (currentTask && currentTask.stopRequestedAt !== undefined)
1414
+ return null;
1410
1415
  if (options.hitContinuationLimit) {
1411
1416
  await bumpAgentTeamContinuation(task.taskId, options.claimedAttempts, {
1412
1417
  requeue: false,
@@ -1573,6 +1578,13 @@ export async function processAgentTeamRun(opts) {
1573
1578
  await completeAgentTeamRun(opts.taskId, task.status === "completed" ? "done" : "failed", claimed.attempts);
1574
1579
  return { ok: true, skipped: "task-terminal" };
1575
1580
  }
1581
+ if (task.stopRequestedAt !== undefined) {
1582
+ const stopped = await stopAgentTeamBackgroundRun(runId, task.stopRequestedReason ?? "user");
1583
+ if (!stopped.ok) {
1584
+ throw new Error("The persisted agent task stop could not be resumed.");
1585
+ }
1586
+ return { ok: true, skipped: "stop-reconciled" };
1587
+ }
1576
1588
  const ownerEmail = claimed.ownerEmail ?? getRequestUserEmail() ?? "";
1577
1589
  const orgId = claimed.orgId;
1578
1590
  const turnId = payload.turnId || taskRunId(opts.taskId);
@@ -1591,6 +1603,11 @@ export async function processAgentTeamRun(opts) {
1591
1603
  ? `Failed to prepare sub-agent: ${err.message}`
1592
1604
  : "Failed to prepare sub-agent.";
1593
1605
  await failReconciledTask(task, ownerEmail || null, message, "failed", claimed, false);
1606
+ const latestTask = await loadTask(opts.taskId);
1607
+ if (latestTask?.stopRequestedAt !== undefined ||
1608
+ latestTask?.terminalProgressStatus === "cancelled") {
1609
+ return { ok: true, skipped: "stop-requested" };
1610
+ }
1594
1611
  return { ok: false, skipped: "config-failed" };
1595
1612
  }
1596
1613
  const mode = opts.mode ?? (claimed.continuationCount > 0 ? "continue" : "start");
@@ -1630,19 +1647,54 @@ export async function processAgentTeamRun(opts) {
1630
1647
  if (leaseLost)
1631
1648
  return { ok: true, skipped: "superseded" };
1632
1649
  const started = await withCurrentAgentTeamRunAttempt(opts.taskId, claimedAttempts, async () => {
1633
- task.currentStep =
1634
- mode === "continue"
1635
- ? "Continuing sub-agent"
1636
- : "Working on response";
1637
- task.startedAt = task.startedAt ?? Date.now();
1638
- await saveTask(task);
1639
- if (ownerEmail)
1640
- await updateTaskProgressRun(task, ownerEmail);
1650
+ for (let attempt = 0; attempt < 3; attempt += 1) {
1651
+ const currentTask = await loadTask(opts.taskId);
1652
+ if (!currentTask || currentTask.status !== "running") {
1653
+ return { saved: false, task: currentTask };
1654
+ }
1655
+ if (currentTask.stopRequestedAt !== undefined) {
1656
+ return { saved: false, task: currentTask };
1657
+ }
1658
+ const expectedTask = structuredClone(currentTask);
1659
+ const transcriptRunIds = [
1660
+ ...new Set([
1661
+ ...(currentTask.transcriptRunIds ?? []),
1662
+ ...(task.transcriptRunIds ?? []),
1663
+ ]),
1664
+ ];
1665
+ if (transcriptRunIds.length > 0) {
1666
+ currentTask.transcriptRunIds = transcriptRunIds;
1667
+ }
1668
+ currentTask.currentStep =
1669
+ mode === "continue"
1670
+ ? "Continuing sub-agent"
1671
+ : "Working on response";
1672
+ currentTask.startedAt = currentTask.startedAt ?? Date.now();
1673
+ if (await saveTaskIfCurrent(currentTask, expectedTask)) {
1674
+ Object.assign(task, currentTask);
1675
+ if (ownerEmail) {
1676
+ await updateTaskProgressRun(currentTask, ownerEmail);
1677
+ }
1678
+ return { saved: true, task: currentTask };
1679
+ }
1680
+ }
1681
+ const currentTask = await loadTask(opts.taskId);
1682
+ return { saved: false, task: currentTask };
1641
1683
  });
1642
1684
  if (!started.current) {
1643
1685
  markLeaseLost();
1644
1686
  return { ok: true, skipped: "superseded" };
1645
1687
  }
1688
+ if (!started.value?.saved) {
1689
+ const currentTask = started.value?.task;
1690
+ if (currentTask?.stopRequestedAt !== undefined) {
1691
+ return { ok: true, skipped: "stop-requested" };
1692
+ }
1693
+ if (currentTask?.status !== "running") {
1694
+ return { ok: true, skipped: "task-terminal" };
1695
+ }
1696
+ throw new Error("The agent task changed repeatedly before the worker could start.");
1697
+ }
1646
1698
  let accumulatedText = "";
1647
1699
  let lastProgressSent = 0;
1648
1700
  const PROGRESS_INTERVAL_MS = 2000;
@@ -1651,21 +1703,28 @@ export async function processAgentTeamRun(opts) {
1651
1703
  let progressWriteChain = Promise.resolve();
1652
1704
  let continuationDispatch;
1653
1705
  const scheduleProgressWrite = () => {
1654
- const snapshot = {
1655
- ...task,
1656
- ...(task.transcriptRunIds
1657
- ? { transcriptRunIds: [...task.transcriptRunIds] }
1658
- : {}),
1706
+ const progress = {
1707
+ preview: task.preview,
1708
+ currentStep: task.currentStep,
1659
1709
  };
1660
1710
  progressWriteChain = progressWriteChain
1661
1711
  .then(async () => {
1662
1712
  if (leaseLost)
1663
1713
  return;
1664
1714
  const result = await withCurrentAgentTeamRunAttempt(opts.taskId, claimedAttempts, async () => {
1665
- await saveTask(snapshot);
1666
- if (ownerEmail) {
1667
- await updateTaskProgressRun(snapshot, ownerEmail);
1715
+ const currentTask = await loadTask(opts.taskId);
1716
+ if (!currentTask || currentTask.status !== "running") {
1717
+ return false;
1668
1718
  }
1719
+ const expectedTask = structuredClone(currentTask);
1720
+ currentTask.preview = progress.preview;
1721
+ currentTask.currentStep = progress.currentStep;
1722
+ if (!(await saveTaskIfCurrent(currentTask, expectedTask))) {
1723
+ return false;
1724
+ }
1725
+ if (ownerEmail)
1726
+ await updateTaskProgressRun(currentTask, ownerEmail);
1727
+ return true;
1669
1728
  });
1670
1729
  if (!result.current)
1671
1730
  markLeaseLost();
@@ -1677,6 +1736,25 @@ export async function processAgentTeamRun(opts) {
1677
1736
  await new Promise((resolve) => {
1678
1737
  const startedRun = startRun(runId, task.threadId, async (send, signal, control) => {
1679
1738
  workerExecutionFence = currentExecutionFence();
1739
+ const dispatchReady = await withCurrentAgentTeamRunAttempt(opts.taskId, claimedAttempts, async () => {
1740
+ const currentTask = await loadTask(opts.taskId);
1741
+ return {
1742
+ allowed: currentTask?.status === "running" &&
1743
+ currentTask.stopRequestedAt === undefined,
1744
+ stopRequested: currentTask?.stopRequestedAt !== undefined,
1745
+ stopReason: currentTask?.stopRequestedReason,
1746
+ };
1747
+ });
1748
+ if (!dispatchReady.current || !dispatchReady.value.allowed) {
1749
+ const currentTask = await loadTask(opts.taskId);
1750
+ const reason = dispatchReady.current && dispatchReady.value.stopRequested
1751
+ ? (dispatchReady.value.stopReason ?? "user")
1752
+ : currentTask && isCancelledAgentTask(currentTask)
1753
+ ? (currentTask?.stopRequestedReason ?? "user")
1754
+ : "superseded";
1755
+ markLeaseLost(reason);
1756
+ return;
1757
+ }
1680
1758
  const wrappedSend = (event) => {
1681
1759
  if (leaseLost)
1682
1760
  return Promise.resolve();
@@ -1730,6 +1808,34 @@ export async function processAgentTeamRun(opts) {
1730
1808
  actions: messageAwareActions,
1731
1809
  send: wrappedSend,
1732
1810
  beforeWrite: control.flushEvents,
1811
+ beforeModelDispatch: async (dispatch) => {
1812
+ const current = await withCurrentAgentTeamRunAttempt(opts.taskId, claimedAttempts, async () => {
1813
+ const currentTask = await loadTask(opts.taskId);
1814
+ if (currentTask?.status === "running" &&
1815
+ currentTask.stopRequestedAt === undefined) {
1816
+ return {
1817
+ allowed: true,
1818
+ result: {
1819
+ started: true,
1820
+ value: dispatch(),
1821
+ },
1822
+ };
1823
+ }
1824
+ return { allowed: false, task: currentTask };
1825
+ });
1826
+ if (current.current && current.value.allowed)
1827
+ return current.value.result;
1828
+ const currentTask = current.current
1829
+ ? current.value.task
1830
+ : await loadTask(opts.taskId);
1831
+ const reason = currentTask &&
1832
+ (currentTask.stopRequestedAt !== undefined ||
1833
+ isCancelledAgentTask(currentTask))
1834
+ ? (currentTask.stopRequestedReason ?? "user")
1835
+ : "superseded";
1836
+ markLeaseLost(reason);
1837
+ return { started: false };
1838
+ },
1733
1839
  signal,
1734
1840
  finalResponseGuard: createTaskMessageFinalGuard(opts.taskId),
1735
1841
  };
@@ -2146,10 +2252,13 @@ async function controlAgentTeamBackgroundAgentRun(input) {
2146
2252
  export async function stopAgentTeamBackgroundRun(runId, reason = "user", scope) {
2147
2253
  const taskId = await resolveTaskIdFromBackgroundRunId(runId);
2148
2254
  const ownerScope = resolveOwnerScope(scope);
2149
- const markStopped = (currentTask) => {
2255
+ const markStopped = (currentTask, stopReason) => {
2256
+ delete currentTask.stopRequestedAt;
2257
+ delete currentTask.stopRequestedRunId;
2258
+ delete currentTask.stopRequestedReason;
2150
2259
  currentTask.status = "errored";
2151
2260
  currentTask.summary =
2152
- reason === "user" ? "Task stopped." : `Task stopped: ${reason}`;
2261
+ stopReason === "user" ? "Task stopped." : `Task stopped: ${stopReason}`;
2153
2262
  currentTask.error = currentTask.summary;
2154
2263
  currentTask.currentStep = "";
2155
2264
  currentTask.completedAt = Date.now();
@@ -2159,7 +2268,6 @@ export async function stopAgentTeamBackgroundRun(runId, reason = "user", scope)
2159
2268
  currentTask.parentCompletionEnqueued = !currentTask.parentThreadId;
2160
2269
  };
2161
2270
  let stoppedTask = null;
2162
- let stoppedDispatch = null;
2163
2271
  for (let retry = 0; retry < 3 && !stoppedTask; retry += 1) {
2164
2272
  const task = await loadTask(taskId);
2165
2273
  if (!task || !taskMatchesOwnerScope(task, ownerScope)) {
@@ -2175,15 +2283,54 @@ export async function stopAgentTeamBackgroundRun(runId, reason = "user", scope)
2175
2283
  return { ok: false, error: "Task is not running" };
2176
2284
  }
2177
2285
  if (dispatch) {
2286
+ const runId = taskRunChunkId(taskId, dispatch.continuationCount, dispatch.attempts);
2287
+ let stopRequestedAt;
2288
+ let stopRequestedRunId;
2289
+ let stopRequestedReason;
2290
+ const current = await withCurrentAgentTeamRunAttempt(taskId, dispatch.attempts, async () => {
2291
+ const currentTask = await loadTask(taskId);
2292
+ if (currentTask &&
2293
+ taskMatchesOwnerScope(currentTask, ownerScope) &&
2294
+ currentTask.status === "running") {
2295
+ const expectedTask = structuredClone(currentTask);
2296
+ currentTask.stopRequestedAt ??= Date.now();
2297
+ stopRequestedAt = currentTask.stopRequestedAt;
2298
+ currentTask.stopRequestedRunId ??= runId;
2299
+ stopRequestedRunId = currentTask.stopRequestedRunId;
2300
+ currentTask.stopRequestedReason ??= reason;
2301
+ stopRequestedReason = currentTask.stopRequestedReason;
2302
+ return saveTaskIfCurrent(currentTask, expectedTask);
2303
+ }
2304
+ return false;
2305
+ }, { statuses: ["queued", "running"] });
2306
+ if (!current.current ||
2307
+ !current.value ||
2308
+ stopRequestedAt === undefined ||
2309
+ stopRequestedRunId === undefined ||
2310
+ stopRequestedReason === undefined)
2311
+ continue;
2312
+ const requestedAt = stopRequestedAt;
2313
+ const requestedRunId = stopRequestedRunId;
2314
+ const requestedReason = stopRequestedReason;
2315
+ // Keep retries pointed at the original run after the queue attempt is reclaimed.
2316
+ await abortRunDurably(requestedRunId, requestedReason, {
2317
+ allowNotStarted: true,
2318
+ });
2178
2319
  const result = await withCurrentAgentTeamRunAttempt(taskId, dispatch.attempts, async () => {
2179
2320
  const currentTask = await loadTask(taskId);
2180
2321
  if (!currentTask ||
2181
2322
  !taskMatchesOwnerScope(currentTask, ownerScope) ||
2182
- currentTask.status !== "running") {
2323
+ currentTask.status !== "running" ||
2324
+ currentTask.stopRequestedAt !== requestedAt ||
2325
+ currentTask.stopRequestedRunId !== requestedRunId ||
2326
+ currentTask.stopRequestedReason !== requestedReason) {
2327
+ return null;
2328
+ }
2329
+ const expectedTask = structuredClone(currentTask);
2330
+ markStopped(currentTask, requestedReason);
2331
+ if (!(await saveTaskIfCurrent(currentTask, expectedTask))) {
2183
2332
  return null;
2184
2333
  }
2185
- markStopped(currentTask);
2186
- await saveTask(currentTask);
2187
2334
  if (!(await completeAgentTeamRun(taskId, "failed", dispatch.attempts))) {
2188
2335
  throw new Error("The agent task run changed before it could stop.");
2189
2336
  }
@@ -2191,22 +2338,37 @@ export async function stopAgentTeamBackgroundRun(runId, reason = "user", scope)
2191
2338
  }, { statuses: ["queued", "running"] });
2192
2339
  if (result.current && result.value) {
2193
2340
  stoppedTask = result.value;
2194
- stoppedDispatch = dispatch;
2195
2341
  }
2196
2342
  continue;
2197
2343
  }
2198
2344
  const expectedTask = structuredClone(task);
2199
- markStopped(task);
2200
- if (await saveTaskIfCurrent(task, expectedTask)) {
2201
- stoppedTask = task;
2345
+ const stopRequestedTask = {
2346
+ ...task,
2347
+ stopRequestedAt: task.stopRequestedAt ?? Date.now(),
2348
+ stopRequestedRunId: task.stopRequestedRunId ?? runningInMemoryTaskRunId(taskId),
2349
+ stopRequestedReason: task.stopRequestedReason ?? reason,
2350
+ };
2351
+ if (!(await saveTaskIfCurrent(stopRequestedTask, expectedTask)))
2352
+ continue;
2353
+ await abortRunDurably(stopRequestedTask.stopRequestedRunId, stopRequestedTask.stopRequestedReason, { allowNotStarted: true });
2354
+ const currentTask = await loadTask(taskId);
2355
+ if (!currentTask ||
2356
+ !taskMatchesOwnerScope(currentTask, ownerScope) ||
2357
+ currentTask.status !== "running" ||
2358
+ currentTask.stopRequestedAt !== stopRequestedTask.stopRequestedAt ||
2359
+ currentTask.stopRequestedRunId !== stopRequestedTask.stopRequestedRunId ||
2360
+ currentTask.stopRequestedReason !== stopRequestedTask.stopRequestedReason) {
2361
+ return { ok: false, error: "Task is not running" };
2362
+ }
2363
+ const expectedStopRequestedTask = structuredClone(currentTask);
2364
+ markStopped(currentTask, stopRequestedTask.stopRequestedReason);
2365
+ if (await saveTaskIfCurrent(currentTask, expectedStopRequestedTask)) {
2366
+ stoppedTask = currentTask;
2202
2367
  }
2203
2368
  }
2204
2369
  if (!stoppedTask) {
2205
2370
  return { ok: false, error: "Task is not running" };
2206
2371
  }
2207
- abortRun(stoppedDispatch
2208
- ? taskRunChunkId(taskId, stoppedDispatch.continuationCount, stoppedDispatch.attempts)
2209
- : runningInMemoryTaskRunId(taskId), reason);
2210
2372
  const ownerEmail = getRequestUserEmail();
2211
2373
  if (ownerEmail) {
2212
2374
  await completeTaskProgressRun(stoppedTask, ownerEmail, "cancelled", stoppedTask.summary);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.210.1-nightly-20261020124228",
3
+ "version": "0.210.2",
4
4
  "description": "The agentic application framework for building autonomous agents with intuitive UIs",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -711,8 +711,8 @@
711
711
  "yaml": "^2.9.0",
712
712
  "yjs": "^13.6.33",
713
713
  "zod": "^4.3.6",
714
- "@agent-native/agentkit": "^0.210.1-nightly-20261020124228",
715
- "@agent-native/recap-cli": "^0.5.75"
714
+ "@agent-native/agentkit": "^0.210.2",
715
+ "@agent-native/recap-cli": "^0.5.77"
716
716
  },
717
717
  "devDependencies": {
718
718
  "@ai-sdk/anthropic": "^3.0.71",
@@ -782,7 +782,7 @@
782
782
  "xlsx": "^0.18.5"
783
783
  },
784
784
  "peerDependencies": {
785
- "@agent-native/core-corpus": "0.210.1-nightly-20261020124228",
785
+ "@agent-native/core-corpus": ">=0.196.0",
786
786
  "@ai-sdk/anthropic": ">=3",
787
787
  "@ai-sdk/cohere": ">=3",
788
788
  "@ai-sdk/google": ">=3",