@agent-native/core 0.84.1 → 0.84.3

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 (100) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +15 -0
  3. package/corpus/core/docs/design/durable-agent-runs.md +34 -35
  4. package/corpus/core/package.json +1 -1
  5. package/corpus/core/src/agent/engine/builder-engine.ts +81 -9
  6. package/corpus/core/src/agent/production-agent.ts +9 -2
  7. package/corpus/core/src/agent/run-loop-with-resume.ts +38 -8
  8. package/corpus/core/src/agent/run-manager.ts +8 -7
  9. package/corpus/core/src/agent/thread-data-builder.ts +6 -0
  10. package/corpus/core/src/cli/design-connect.ts +175 -0
  11. package/corpus/core/src/cli/skills.ts +5 -7
  12. package/corpus/core/src/client/AgentPanel.tsx +8 -0
  13. package/corpus/core/src/client/MultiTabAssistantChat.tsx +4 -1
  14. package/corpus/core/src/client/agent-chat-adapter.ts +253 -24
  15. package/corpus/core/src/client/sharing/ShareButton.tsx +70 -21
  16. package/corpus/core/src/client/sse-event-processor.ts +46 -4
  17. package/corpus/core/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +15 -17
  18. package/corpus/templates/analytics/AGENTS.md +1 -1
  19. package/corpus/templates/design/actions/apply-motion-edit.ts +50 -18
  20. package/corpus/templates/design/actions/get-motion-timeline.ts +4 -14
  21. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +156 -12
  22. package/corpus/templates/design/app/components/design/MotionDock.tsx +2 -3
  23. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +217 -75
  24. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +39 -39
  25. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +558 -37
  26. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +75 -3
  27. package/corpus/templates/design/app/components/design/types.ts +13 -0
  28. package/corpus/templates/design/app/components/layout/Layout.tsx +1 -0
  29. package/corpus/templates/design/app/i18n/zh-TW.ts +17 -0
  30. package/corpus/templates/design/app/i18n-data.ts +245 -0
  31. package/corpus/templates/design/app/pages/DesignEditor.tsx +890 -252
  32. package/corpus/templates/design/changelog/2026-06-30-agent-chat-now-reports-saved-design-generations-clearly-when.md +6 -0
  33. package/corpus/templates/design/changelog/2026-06-30-apply-styles-now-appears-only-for-localhost-visual-edit-scre.md +6 -0
  34. package/corpus/templates/design/changelog/2026-06-30-copying-or-dragging-screen-elements-onto-the-infinite-canvas.md +6 -0
  35. package/corpus/templates/design/changelog/2026-06-30-design-questions-now-use-tighter-editor-typography-and-contr.md +6 -0
  36. package/corpus/templates/design/changelog/2026-06-30-element-drags-can-be-cancelled-with-escape-before-they-commi.md +6 -0
  37. package/corpus/templates/design/changelog/2026-06-30-pending-visual-style-edits-now-warn-before-you-leave-the-edi.md +6 -0
  38. package/corpus/templates/design/changelog/2026-06-30-share-options-now-make-export-and-agent-handoff-easier-to-no.md +6 -0
  39. package/corpus/templates/design/changelog/2026-06-30-visual-style-drags-stay-live-while-pending-edits-can-be-appl.md +6 -0
  40. package/corpus/templates/design/changelog/2026-07-01-design-chat-no-longer-shows-a-redundant-context-tab-above-the-composer.md +6 -0
  41. package/corpus/templates/design/changelog/2026-07-01-motion-track-creation-no-longer-fails-in-local-editors.md +6 -0
  42. package/corpus/templates/design/changelog/2026-07-01-share-general-access-menu-stays-open-when-choosing-organization.md +6 -0
  43. package/corpus/templates/design/server/plugins/db.ts +10 -0
  44. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  45. package/dist/agent/engine/builder-engine.js +61 -10
  46. package/dist/agent/engine/builder-engine.js.map +1 -1
  47. package/dist/agent/production-agent.d.ts.map +1 -1
  48. package/dist/agent/production-agent.js +9 -2
  49. package/dist/agent/production-agent.js.map +1 -1
  50. package/dist/agent/run-loop-with-resume.d.ts +2 -2
  51. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  52. package/dist/agent/run-loop-with-resume.js +31 -8
  53. package/dist/agent/run-loop-with-resume.js.map +1 -1
  54. package/dist/agent/run-manager.d.ts +8 -7
  55. package/dist/agent/run-manager.d.ts.map +1 -1
  56. package/dist/agent/run-manager.js +8 -7
  57. package/dist/agent/run-manager.js.map +1 -1
  58. package/dist/agent/thread-data-builder.d.ts +2 -0
  59. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  60. package/dist/agent/thread-data-builder.js +5 -0
  61. package/dist/agent/thread-data-builder.js.map +1 -1
  62. package/dist/cli/design-connect.d.ts +2 -0
  63. package/dist/cli/design-connect.d.ts.map +1 -1
  64. package/dist/cli/design-connect.js +140 -0
  65. package/dist/cli/design-connect.js.map +1 -1
  66. package/dist/cli/skills.d.ts.map +1 -1
  67. package/dist/cli/skills.js +5 -7
  68. package/dist/cli/skills.js.map +1 -1
  69. package/dist/client/AgentPanel.d.ts +5 -1
  70. package/dist/client/AgentPanel.d.ts.map +1 -1
  71. package/dist/client/AgentPanel.js +4 -4
  72. package/dist/client/AgentPanel.js.map +1 -1
  73. package/dist/client/MultiTabAssistantChat.d.ts +3 -1
  74. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  75. package/dist/client/MultiTabAssistantChat.js +2 -2
  76. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  77. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  78. package/dist/client/agent-chat-adapter.js +216 -22
  79. package/dist/client/agent-chat-adapter.js.map +1 -1
  80. package/dist/client/sharing/ShareButton.d.ts +6 -0
  81. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  82. package/dist/client/sharing/ShareButton.js +25 -11
  83. package/dist/client/sharing/ShareButton.js.map +1 -1
  84. package/dist/client/sse-event-processor.d.ts +18 -1
  85. package/dist/client/sse-event-processor.d.ts.map +1 -1
  86. package/dist/client/sse-event-processor.js +34 -5
  87. package/dist/client/sse-event-processor.js.map +1 -1
  88. package/dist/collab/awareness.d.ts +2 -2
  89. package/dist/collab/awareness.d.ts.map +1 -1
  90. package/dist/collab/routes.d.ts +2 -2
  91. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  92. package/dist/notifications/routes.d.ts +3 -3
  93. package/dist/observability/routes.d.ts +7 -7
  94. package/dist/resources/handlers.d.ts +2 -2
  95. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  96. package/dist/server/transcribe-voice.d.ts +1 -1
  97. package/dist/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +15 -17
  98. package/docs/design/durable-agent-runs.md +34 -35
  99. package/package.json +1 -1
  100. package/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +15 -17
@@ -1 +1 @@
1
- {"version":3,"file":"agent-chat-adapter.d.ts","sourceRoot":"","sources":["../../src/client/agent-chat-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,qBAAqB,CAAC;AAMhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAiBrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,oBAAoB;AAC9B;;;;GAIG;AACD,KAAK;AACP,0EAA0E;GACxE,WAAW,CAAC;AAmmChB;;;;GAIG;AACH;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC5C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,SAAS,CAAA;KAAE,CAAC;IACrD,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAED,wBAAgB,sBAAsB,CACpC,OAAO,CAAC,EAAE,6BAA6B,GACtC,gBAAgB,CAyuClB"}
1
+ {"version":3,"file":"agent-chat-adapter.d.ts","sourceRoot":"","sources":["../../src/client/agent-chat-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,qBAAqB,CAAC;AAMhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAkBrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,oBAAoB;AAC9B;;;;GAIG;AACD,KAAK;AACP,0EAA0E;GACxE,WAAW,CAAC;AA0oChB;;;;GAIG;AACH;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC5C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,SAAS,CAAA;KAAE,CAAC;IACrD,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAED,wBAAgB,sBAAsB,CACpC,OAAO,CAAC,EAAE,6BAA6B,GACtC,gBAAgB,CAs6ClB"}
@@ -529,6 +529,34 @@ function hasContinuationProgress(content) {
529
529
  ? part.text.trim().length > 0
530
530
  : part.result !== undefined);
531
531
  }
532
+ function lastCompletedSideEffectTool(content) {
533
+ for (let i = content.length - 1; i >= 0; i--) {
534
+ const part = content[i];
535
+ if (part.type === "tool-call" &&
536
+ part.activity !== true &&
537
+ part.result !== undefined &&
538
+ part.isError !== true &&
539
+ part.completedSideEffect === true) {
540
+ return part;
541
+ }
542
+ }
543
+ return undefined;
544
+ }
545
+ function humanizeActionName(toolName) {
546
+ return toolName
547
+ .replace(/^agent:/, "")
548
+ .replace(/[-_]+/g, " ")
549
+ .trim();
550
+ }
551
+ function completedToolTimeoutMessage(toolName) {
552
+ if (toolName === "generate-design" || toolName === "update-design") {
553
+ return "The design was saved, but the assistant timed out before sending its final note. You can open the generated design from the completed tool card above.";
554
+ }
555
+ if (toolName === "present-design-variants") {
556
+ return "The design variants were saved, but the assistant timed out before sending its final note. You can review the completed variants from the tool card above.";
557
+ }
558
+ return `The ${humanizeActionName(toolName)} action completed, but the assistant timed out before sending its final response. The saved result is in the completed tool card above.`;
559
+ }
532
560
  /**
533
561
  * Signature of the *unique* sentence-like segments in a continuation's newly
534
562
  * streamed text, used to detect a degenerate repetition loop. A stuck model
@@ -603,6 +631,8 @@ function toolContinuationKey(part) {
603
631
  stableJson(part.args),
604
632
  part.result === undefined ? "pending" : "done",
605
633
  part.result ?? "",
634
+ part.isError === true ? "error" : "",
635
+ part.completedSideEffect === true ? "side-effect" : "",
606
636
  part.activity === true ? "activity" : "tool",
607
637
  part.mcpApp ? "mcp-app" : "",
608
638
  ].join("\u0000");
@@ -657,6 +687,8 @@ function incrementalActionGuidance(tool) {
657
687
  case "generate-design":
658
688
  case "update-design":
659
689
  return "persist a minimal first version (fewer files) with `generate-design`, then refine individual files with `edit-design` search/replace instead of resending everything";
690
+ case "present-design-variants":
691
+ return "save compact but complete variant screens with `present-design-variants` first, keeping each HTML direction focused enough to finish, then refine the chosen direction with `generate-design` or `edit-design`";
660
692
  case "create-visual-plan":
661
693
  case "create-ui-plan":
662
694
  case "create-plan-design":
@@ -1039,11 +1071,13 @@ export function createAgentChatAdapter(options) {
1039
1071
  // its own budget rather than inheriting the prior payload's.
1040
1072
  let lastInFlightToolSignature;
1041
1073
  let repeatedInFlightToolCount = 0;
1074
+ let recoveryGaveUpOnInFlightTool = false;
1042
1075
  const MAX_REPEATED_INFLIGHT_TOOL_STALLS = 3;
1043
1076
  const continuationHistoryFragments = [];
1044
1077
  const structuredContinuationFragments = [];
1045
1078
  let visibleContinuationPrefix = [];
1046
1079
  let lastAutoContinueReason = null;
1080
+ let lastRecoverableRunError = null;
1047
1081
  const attemptedRunIds = [];
1048
1082
  let authRecoveryAttempted = false;
1049
1083
  let continuationToolCallCounter = 0;
@@ -1053,6 +1087,12 @@ export function createAgentChatAdapter(options) {
1053
1087
  lastAutoContinueReason
1054
1088
  ? `last_auto_continue_reason: ${lastAutoContinueReason}`
1055
1089
  : "",
1090
+ lastRecoverableRunError?.errorCode
1091
+ ? `last_recoverable_error_code: ${lastRecoverableRunError.errorCode}`
1092
+ : "",
1093
+ lastRecoverableRunError?.message
1094
+ ? `last_recoverable_error: ${lastRecoverableRunError.message}`
1095
+ : "",
1056
1096
  `stale_run_continuations: ${staleRunContinuationAttempts}`,
1057
1097
  `stalled_transient_continuations: ${stalledTransientContinuationAttempts}`,
1058
1098
  `empty_transient_continuations: ${emptyTransientContinuationAttempts}`,
@@ -1070,6 +1110,9 @@ export function createAgentChatAdapter(options) {
1070
1110
  .join("\n");
1071
1111
  };
1072
1112
  const exhaustedRecoveryMessage = (reason) => {
1113
+ if (recoveryGaveUpOnInFlightTool) {
1114
+ return "The agent got stuck waiting for the same tool to finish, so I stopped the automatic retries. The tool did not report a completed result.";
1115
+ }
1073
1116
  if (recoveryGaveUpOnRepetition) {
1074
1117
  return "The agent got stuck repeating the same response without finishing, so I stopped the automatic retries. This often happens when it tries to re-type a large pasted file into one action — starting a new chat, or asking for a smaller first step, usually gets it unstuck.";
1075
1118
  }
@@ -1302,11 +1345,81 @@ export function createAgentChatAdapter(options) {
1302
1345
  }
1303
1346
  return false;
1304
1347
  };
1348
+ const reconnectBackgroundContinuationForRunTimeout = async function* () {
1349
+ if (!threadId || !runId)
1350
+ return false;
1351
+ const interruptedRunId = runId;
1352
+ const interruptedLastSeq = lastSeq;
1353
+ let lastActiveRunError = null;
1354
+ for (let attempt = 0; attempt < 3; attempt++) {
1355
+ if (attempt > 0) {
1356
+ await delay(500, abortSignal);
1357
+ }
1358
+ if (abortSignal.aborted)
1359
+ return true;
1360
+ try {
1361
+ const activeRes = await fetch(`${apiUrl}/runs/active?threadId=${encodeURIComponent(threadId)}`, { signal: abortSignal });
1362
+ if (!activeRes.ok) {
1363
+ if (activeRes.status === 404)
1364
+ return false;
1365
+ lastActiveRunError = new Error(`Active run lookup failed: ${activeRes.status}`);
1366
+ continue;
1367
+ }
1368
+ const active = await activeRes.json();
1369
+ if (!active?.active || !active.runId)
1370
+ return false;
1371
+ const activeRunId = String(active.runId);
1372
+ const dispatchMode = typeof active.dispatchMode === "string"
1373
+ ? active.dispatchMode
1374
+ : "";
1375
+ if (activeRunId === interruptedRunId) {
1376
+ if (dispatchMode.startsWith("background"))
1377
+ continue;
1378
+ return false;
1379
+ }
1380
+ const activeTurnId = typeof active.turnId === "string" ? active.turnId : "";
1381
+ const activeStatus = typeof active.status === "string" ? active.status : "";
1382
+ if (!dispatchMode.startsWith("background"))
1383
+ return false;
1384
+ if (activeTurnId && activeTurnId !== turnId)
1385
+ return false;
1386
+ if (activeStatus !== "running" && activeStatus !== "starting") {
1387
+ return false;
1388
+ }
1389
+ runId = activeRunId;
1390
+ if (!attemptedRunIds.includes(activeRunId)) {
1391
+ attemptedRunIds.push(activeRunId);
1392
+ }
1393
+ lastSeq = -1;
1394
+ setActiveRun({ threadId, runId: activeRunId, lastSeq: -1 });
1395
+ const reconnected = yield* reconnectCurrentRun();
1396
+ if (reconnected)
1397
+ return true;
1398
+ }
1399
+ catch (activeErr) {
1400
+ if (activeErr instanceof Error &&
1401
+ activeErr.name === "AbortError") {
1402
+ clearActiveRun();
1403
+ return true;
1404
+ }
1405
+ lastActiveRunError = activeErr;
1406
+ }
1407
+ }
1408
+ if (lastActiveRunError) {
1409
+ captureChatClientError(lastActiveRunError, "reconnect-background-continuation-failed");
1410
+ }
1411
+ runId = interruptedRunId;
1412
+ lastSeq = interruptedLastSeq;
1413
+ return false;
1414
+ };
1305
1415
  const visibleContentForContinuation = () => {
1306
1416
  return contentAfterContinuationPrefix(content, visibleContinuationPrefix);
1307
1417
  };
1308
1418
  const prepareAutoContinuation = (signal) => {
1309
1419
  lastAutoContinueReason = signal.reason;
1420
+ if (signal.errorInfo) {
1421
+ lastRecoverableRunError = signal.errorInfo;
1422
+ }
1310
1423
  const isTransient = signal.reason !== "loop_limit";
1311
1424
  const visibleContent = visibleContentForContinuation();
1312
1425
  const currentPartialHistory = contentToContinuationHistory(visibleContent);
@@ -1315,13 +1428,13 @@ export function createAgentChatAdapter(options) {
1315
1428
  // whitespace-only output cannot keep the run alive indefinitely.
1316
1429
  const madeContentProgress = hasContinuationProgress(visibleContent);
1317
1430
  // An action was streamed but has not returned yet (a tool_start with
1318
- // no tool_done), or the activity trail shows the server was working
1319
- // on a tool. A run_timeout that fires in this window means the agent
1320
- // was actively making progress the server's foldAssistantTurn
1321
- // persisted the in-flight call — so it must NOT count against the
1322
- // stalled/empty continuation budgets.
1323
- const hasInFlightTool = hasInFlightToolCall(visibleContent) ||
1324
- Boolean(lastActivityTool(signal.activityTrail));
1431
+ // no tool_done). This is durable enough to survive continuation: the
1432
+ // server already emitted a real tool call. A tool-scoped activity
1433
+ // card ("Preparing generate-design") is useful UI, but it happens
1434
+ // before tool_start; treating it as progress caused silent retry
1435
+ // loops when the LLM timed out while assembling a large tool input.
1436
+ const hasInFlightTool = hasInFlightToolCall(visibleContent);
1437
+ const completedSideEffectTool = lastCompletedSideEffectTool(content);
1325
1438
  // Either real output or an actively-running tool counts as progress
1326
1439
  // for the stalled/empty caps.
1327
1440
  const madeProgress = madeContentProgress || hasInFlightTool;
@@ -1334,11 +1447,11 @@ export function createAgentChatAdapter(options) {
1334
1447
  // MAX_REPEATED_INFLIGHT_TOOL_STALLS consecutive stream_ended events,
1335
1448
  // bail with a clear message.
1336
1449
  //
1337
- // Only count stream_ended (connection drop / reconnect failed), NOT
1338
- // run_timeout (server legitimately still executing a slow tool). A
1339
- // run_timeout with an in-flight tool means the server is actively
1340
- // working and reconnection may still recover the result; a repeated
1341
- // stream_ended means the connection keeps breaking under that payload.
1450
+ // Count broken streams (connection drop / reconnect failed) and
1451
+ // no_progress stalls (the client aborts a stream that stayed open but
1452
+ // stopped producing events). Do NOT count run_timeout: with an
1453
+ // in-flight tool that means the server is still actively executing a
1454
+ // slow action and reconnection may recover the result.
1342
1455
  const currentInFlightToolPart = visibleContent.find((p) => p.type === "tool-call" &&
1343
1456
  p.result === undefined &&
1344
1457
  p.activity !== true);
@@ -1346,8 +1459,8 @@ export function createAgentChatAdapter(options) {
1346
1459
  const currentInFlightToolSignature = currentInFlightToolPart
1347
1460
  ? inFlightToolInputSignature(currentInFlightToolPart)
1348
1461
  : undefined;
1349
- const isConnectionDrop = signal.reason === "stream_ended";
1350
- if (currentInFlightToolName && isConnectionDrop) {
1462
+ const isBrokenInFlightTool = signal.reason === "stream_ended" || signal.reason === "no_progress";
1463
+ if (currentInFlightToolName && isBrokenInFlightTool) {
1351
1464
  if (currentInFlightToolName === lastInFlightToolName &&
1352
1465
  currentInFlightToolSignature === lastInFlightToolSignature) {
1353
1466
  repeatedInFlightToolCount += 1;
@@ -1384,11 +1497,27 @@ export function createAgentChatAdapter(options) {
1384
1497
  }
1385
1498
  else {
1386
1499
  totalTransientContinuationAttempts += 1;
1500
+ // If a mutating action already completed, do not turn a missing
1501
+ // closing sentence into a scary connection failure. Give the model
1502
+ // one continuation opportunity (the completed tool itself counts
1503
+ // as progress on the first timeout); if the follow-up produces no
1504
+ // new content, stop locally with a clear "saved, final note timed
1505
+ // out" message.
1506
+ if (completedSideEffectTool &&
1507
+ !hasInFlightToolCall(content) &&
1508
+ !madeContentProgress &&
1509
+ !hasInFlightTool) {
1510
+ return {
1511
+ ok: false,
1512
+ resetVisibleContent: false,
1513
+ completedToolName: completedSideEffectTool.toolName,
1514
+ };
1515
+ }
1387
1516
  // Bail when the same write tool is stuck in-flight across too many
1388
1517
  // consecutive continuations. Checked before the text-repeat guard
1389
1518
  // because hasInFlightTool=true would mask the repeat as progress.
1390
1519
  if (repeatedInFlightToolCount >= MAX_REPEATED_INFLIGHT_TOOL_STALLS) {
1391
- recoveryGaveUpOnRepetition = true;
1520
+ recoveryGaveUpOnInFlightTool = true;
1392
1521
  return { ok: false, resetVisibleContent: false };
1393
1522
  }
1394
1523
  // Bail fast on a non-advancing repetition loop, well before the
@@ -1751,6 +1880,11 @@ export function createAgentChatAdapter(options) {
1751
1880
  if (err.reason === "no_progress") {
1752
1881
  await abortCurrentRun();
1753
1882
  }
1883
+ if (err.reason === "run_timeout" && !err.errorInfo) {
1884
+ const reconnected = yield* reconnectBackgroundContinuationForRunTimeout();
1885
+ if (reconnected)
1886
+ return;
1887
+ }
1754
1888
  if (err.reason === "stream_ended") {
1755
1889
  const reconnected = yield* reconnectCurrentRun();
1756
1890
  if (reconnected)
@@ -1761,15 +1895,48 @@ export function createAgentChatAdapter(options) {
1761
1895
  }
1762
1896
  const continuation = prepareAutoContinuation(err);
1763
1897
  if (!continuation.ok) {
1764
- const message = exhaustedRecoveryMessage(err.reason);
1898
+ if (continuation.completedToolName) {
1899
+ const message = completedToolTimeoutMessage(continuation.completedToolName);
1900
+ content.push({ type: "text", text: message });
1901
+ yield {
1902
+ content: [...content],
1903
+ status: {
1904
+ type: "complete",
1905
+ reason: "stop",
1906
+ },
1907
+ metadata: {
1908
+ custom: {
1909
+ ...(runId ? { runId } : {}),
1910
+ runWarning: {
1911
+ message,
1912
+ errorCode: "final_response_timeout_after_tool",
1913
+ recoverable: true,
1914
+ },
1915
+ },
1916
+ },
1917
+ };
1918
+ clearActiveRun();
1919
+ return;
1920
+ }
1921
+ const preservedError = err.errorInfo ?? lastRecoverableRunError ?? null;
1922
+ const message = preservedError?.message ??
1923
+ exhaustedRecoveryMessage(err.reason);
1924
+ const details = [
1925
+ preservedError?.details,
1926
+ connectionRecoveryDetails(),
1927
+ ]
1928
+ .filter(Boolean)
1929
+ .join("\n\n");
1930
+ const errorCode = preservedError?.errorCode ?? "connection_error";
1765
1931
  captureChatClientError(err, "auto-continuation-exhausted", {
1766
1932
  autoContinueReason: err.reason,
1933
+ ...(errorCode ? { errorCode } : {}),
1767
1934
  });
1768
1935
  const runError = {
1769
1936
  message,
1770
- details: connectionRecoveryDetails(),
1771
- errorCode: "connection_error",
1772
- recoverable: true,
1937
+ ...(details ? { details } : {}),
1938
+ errorCode,
1939
+ recoverable: preservedError?.recoverable ?? true,
1773
1940
  ...(runId ? { runId } : {}),
1774
1941
  };
1775
1942
  if (typeof window !== "undefined") {
@@ -1777,10 +1944,12 @@ export function createAgentChatAdapter(options) {
1777
1944
  detail: { ...runError, tabId },
1778
1945
  }));
1779
1946
  }
1780
- settleInterruptedToolCalls(content);
1947
+ settleInterruptedToolCalls(content, undefined, {
1948
+ includeActivity: true,
1949
+ });
1781
1950
  content.push({
1782
1951
  type: "text",
1783
- text: `Something went wrong: ${message}`,
1952
+ text: formatChatErrorText(message, preservedError?.upgradeUrl, errorCode),
1784
1953
  });
1785
1954
  yield {
1786
1955
  content: [...content],
@@ -1899,6 +2068,29 @@ export function createAgentChatAdapter(options) {
1899
2068
  if (content.length > 0) {
1900
2069
  const continuation = prepareAutoContinuation(new AgentAutoContinueSignal({ reason: "stream_ended" }));
1901
2070
  if (!continuation.ok) {
2071
+ if (continuation.completedToolName) {
2072
+ const message = completedToolTimeoutMessage(continuation.completedToolName);
2073
+ content.push({ type: "text", text: message });
2074
+ yield {
2075
+ content: [...content],
2076
+ status: {
2077
+ type: "complete",
2078
+ reason: "stop",
2079
+ },
2080
+ metadata: {
2081
+ custom: {
2082
+ ...(runId ? { runId } : {}),
2083
+ runWarning: {
2084
+ message,
2085
+ errorCode: "final_response_timeout_after_tool",
2086
+ recoverable: true,
2087
+ },
2088
+ },
2089
+ },
2090
+ };
2091
+ clearActiveRun();
2092
+ return;
2093
+ }
1902
2094
  const message = exhaustedRecoveryMessage("stream_ended");
1903
2095
  captureChatClientError(err, "recovery-exhausted");
1904
2096
  const runError = {
@@ -1913,7 +2105,9 @@ export function createAgentChatAdapter(options) {
1913
2105
  detail: { ...runError, tabId },
1914
2106
  }));
1915
2107
  }
1916
- settleInterruptedToolCalls(content);
2108
+ settleInterruptedToolCalls(content, undefined, {
2109
+ includeActivity: true,
2110
+ });
1917
2111
  content.push({
1918
2112
  type: "text",
1919
2113
  text: `Something went wrong: ${message}`,