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

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.
@@ -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.1",
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.1",
715
+ "@agent-native/recap-cli": "^0.5.76"
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",