@agent-native/core 0.168.13 → 0.169.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.
Files changed (96) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/actions/bigquery-table-info.ts +3 -2
  3. package/corpus/templates/analytics/actions/search-bigquery-schema.ts +130 -4
  4. package/corpus/templates/analytics/agent-native.config.ts +1 -0
  5. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +36 -23
  6. package/corpus/templates/analytics/app/lib/dashboard-visibility.ts +41 -0
  7. package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +34 -9
  8. package/corpus/templates/analytics/changelog/2026-08-21-dashboard-filters-keep-other-users-dashboards-out-of-mine.md +6 -0
  9. package/corpus/templates/analytics/server/lib/real-data-actions.ts +14 -3
  10. package/corpus/templates/analytics/server/plugins/agent-chat.ts +33 -3
  11. package/corpus/templates/brain/server/lib/brain.ts +27 -2
  12. package/corpus/templates/clips/actions/stop-meeting-recording.ts +87 -6
  13. package/corpus/templates/clips/app/components/library/search-bar.tsx +1 -1
  14. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +3 -4
  15. package/corpus/templates/design/app/components/design/EditPanel.tsx +15 -47
  16. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +3 -3
  17. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +0 -7
  18. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +2 -4
  19. package/corpus/templates/design/app/i18n-data.ts +10 -10
  20. package/corpus/templates/design/app/pages/DesignEditor.tsx +22 -22
  21. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +39 -8
  22. package/corpus/templates/design/app/pages/DesignSystems.tsx +1 -1
  23. package/corpus/templates/design/app/pages/Index.tsx +1 -1
  24. package/corpus/templates/design/app/pages/design-editor/preview-navigation.ts +25 -0
  25. package/corpus/templates/mail/app/hooks/use-emails.ts +13 -3
  26. package/dist/agent/engine/credential-errors.d.ts +1 -1
  27. package/dist/agent/engine/credential-errors.js +2 -2
  28. package/dist/agent/engine/first-event-timeout.d.ts +8 -0
  29. package/dist/agent/engine/first-event-timeout.js +8 -0
  30. package/dist/agent/production-agent.d.ts +0 -30
  31. package/dist/agent/production-agent.js +17 -38
  32. package/dist/agent/run-loop-with-resume.d.ts +38 -25
  33. package/dist/agent/run-loop-with-resume.js +140 -55
  34. package/dist/agent/run-manager.d.ts +83 -68
  35. package/dist/agent/run-manager.js +280 -94
  36. package/dist/agent/run-store.d.ts +31 -0
  37. package/dist/agent/run-store.js +42 -12
  38. package/dist/app-config/agent.d.ts +2 -0
  39. package/dist/app-config/agent.js +33 -0
  40. package/dist/app-config/run-lifecycle-invariants.d.ts +248 -0
  41. package/dist/app-config/run-lifecycle-invariants.js +342 -0
  42. package/dist/app-config/schema.d.ts +2 -0
  43. package/dist/app-config/store.js +9 -1
  44. package/dist/client/AssistantChat.js +47 -15
  45. package/dist/client/ConnectBuilderCard.d.ts +3 -1
  46. package/dist/client/ConnectBuilderCard.js +6 -3
  47. package/dist/client/agent-chat-adapter.d.ts +0 -2
  48. package/dist/client/agent-chat-adapter.js +7 -23
  49. package/dist/client/chat/message-components.d.ts +7 -0
  50. package/dist/client/chat/message-components.js +1 -1
  51. package/dist/client/chat/run-recovery.js +5 -2
  52. package/dist/client/chat/tool-call-display.js +9 -3
  53. package/dist/client/error-format.js +12 -0
  54. package/dist/client/navigation/index.d.ts +1 -0
  55. package/dist/client/navigation/index.js +1 -0
  56. package/dist/client/route-state.js +6 -0
  57. package/dist/client/workspace-app-navigation.d.ts +10 -0
  58. package/dist/client/workspace-app-navigation.js +38 -0
  59. package/dist/collab/struct-routes.d.ts +1 -1
  60. package/dist/deploy/build.d.ts +13 -0
  61. package/dist/deploy/build.js +51 -0
  62. package/dist/jobs/background-automation-runner.d.ts +25 -0
  63. package/dist/jobs/background-automation-runner.js +104 -21
  64. package/dist/jobs/run-history.d.ts +7 -1
  65. package/dist/jobs/run-history.js +57 -14
  66. package/dist/localization/core-messages/ar-SA.js +1 -1
  67. package/dist/localization/core-messages/de-DE.js +1 -1
  68. package/dist/localization/core-messages/en-US.d.ts +1 -1
  69. package/dist/localization/core-messages/en-US.js +1 -1
  70. package/dist/localization/core-messages/es-ES.js +1 -1
  71. package/dist/localization/core-messages/fr-FR.js +1 -1
  72. package/dist/localization/core-messages/hi-IN.js +1 -1
  73. package/dist/localization/core-messages/ja-JP.js +1 -1
  74. package/dist/localization/core-messages/ko-KR.js +1 -1
  75. package/dist/localization/core-messages/pt-BR.js +1 -1
  76. package/dist/localization/core-messages/zh-CN.js +1 -1
  77. package/dist/localization/core-messages/zh-TW.js +1 -1
  78. package/dist/localization/core-messages.d.ts +1 -1
  79. package/dist/notifications/routes.d.ts +1 -1
  80. package/dist/observability/routes.d.ts +3 -3
  81. package/dist/observability/traces.d.ts +13 -0
  82. package/dist/observability/traces.js +369 -317
  83. package/dist/progress/routes.d.ts +1 -1
  84. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  85. package/dist/provider-api/actions/provider-api.d.ts +13 -13
  86. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  87. package/dist/resources/handlers.d.ts +1 -1
  88. package/dist/scripts/db/check-scoping.js +9 -6
  89. package/dist/secrets/routes.d.ts +6 -6
  90. package/dist/server/agent-chat-plugin.js +2 -4
  91. package/dist/server/builder-browser.d.ts +4 -0
  92. package/dist/server/builder-browser.js +23 -1
  93. package/dist/server/core-routes-plugin.js +13 -1
  94. package/dist/server/transcribe-voice.d.ts +1 -1
  95. package/package.json +3 -3
  96. /package/corpus/templates/dispatch/app/routes/{apps.$appId.tsx → apps.$appId.$.tsx} +0 -0
@@ -121,6 +121,8 @@ function getPollAbortMs(interval) {
121
121
  return Math.max(POLL_ABORT_MIN_MS, interval * 4);
122
122
  }
123
123
  const ACTIVE_RUN_CLEAR_TIMEOUT_MS = 5_000;
124
+ const ACTIVE_RUN_CLEAR_STABLE_POLLS = 2;
125
+ const ACTIVE_RUN_CLEAR_RETRY_DELAY_MS = 1_000;
124
126
  const ACTIVE_RUN_STUCK_THRESHOLD_MS = 90_000;
125
127
  const BACKGROUND_ACTIVE_RUN_STUCK_THRESHOLD_MS = 13 * 60_000;
126
128
  const ACTIVE_RUN_POLL_INTERVAL_MS = 150;
@@ -303,6 +305,7 @@ export async function waitForThreadRunToClear(apiUrl, threadId) {
303
305
  return true;
304
306
  const deadline = Date.now() + ACTIVE_RUN_CLEAR_TIMEOUT_MS;
305
307
  let activeRunToResume = null;
308
+ let consecutiveClearPolls = 0;
306
309
  const resumeActiveRun = (info) => {
307
310
  if (!info.runId)
308
311
  return;
@@ -323,11 +326,22 @@ export async function waitForThreadRunToClear(apiUrl, threadId) {
323
326
  const res = await fetch(`${apiUrl}/runs/active?threadId=${encodeURIComponent(threadId)}`);
324
327
  if (res.ok) {
325
328
  const info = (await res.json());
326
- if (!info?.active ||
329
+ const runLooksClear = !info?.active ||
327
330
  info?.status !== "running" ||
328
- activeRunLooksStale(info))
329
- return true;
330
- if (info.runId) {
331
+ activeRunLooksStale(info);
332
+ if (runLooksClear) {
333
+ consecutiveClearPolls += 1;
334
+ // A terminal/no-active snapshot can briefly appear between a
335
+ // foreground run and its server-owned continuation. Require two
336
+ // consecutive clear polls before releasing a queued follow-up.
337
+ if (consecutiveClearPolls >= ACTIVE_RUN_CLEAR_STABLE_POLLS) {
338
+ return true;
339
+ }
340
+ }
341
+ else {
342
+ consecutiveClearPolls = 0;
343
+ }
344
+ if (!runLooksClear && info.runId) {
331
345
  activeRunToResume = info;
332
346
  if (info.awaitingRedispatch === true) {
333
347
  // This is not the brief terminal-write lag the 5s waiter was built
@@ -340,8 +354,12 @@ export async function waitForThreadRunToClear(apiUrl, threadId) {
340
354
  }
341
355
  }
342
356
  }
357
+ else {
358
+ consecutiveClearPolls = 0;
359
+ }
343
360
  }
344
361
  catch {
362
+ consecutiveClearPolls = 0;
345
363
  // Transient poll failure — try again until the short grace period ends.
346
364
  }
347
365
  await new Promise((resolve) => window.setTimeout(resolve, ACTIVE_RUN_POLL_INTERVAL_MS));
@@ -1730,6 +1748,15 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
1730
1748
  tabId,
1731
1749
  onActiveRunChange: setHasActiveServerRun,
1732
1750
  });
1751
+ // Server truth must participate in submit gating, not only in the missing
1752
+ // final-response warning. The local lifecycle can clear during a transport
1753
+ // handoff while the server still owns the turn.
1754
+ const serverRunState = useRunStuckDetection({
1755
+ threadId: threadId ?? null,
1756
+ enabled: isActiveComposer,
1757
+ apiUrl,
1758
+ });
1759
+ const serverRunActive = serverRunState.runId != null && serverRunState.status === "running";
1733
1760
  // Real running state drives submission/queue gating; UI running also covers
1734
1761
  // short auto-continuation gaps so the latest assistant message does not flash
1735
1762
  // into a done state while the agent is still working.
@@ -1739,19 +1766,10 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
1739
1766
  isReconnecting,
1740
1767
  optimisticRunning,
1741
1768
  isAutoResuming,
1742
- hasActiveServerRun,
1769
+ hasActiveServerRun: hasActiveServerRun || serverRunActive,
1743
1770
  hasTerminalRunError: runErrorInfo !== null,
1744
1771
  });
1745
1772
  const textStreaming = showRunningInUI || externalStreaming;
1746
- // Server truth about this thread's run, used to suppress the "agent stopped
1747
- // without sending a final message" notice while the server still has the
1748
- // turn in flight. The poll backs itself off to 15s while nothing is running.
1749
- const serverRunState = useRunStuckDetection({
1750
- threadId: threadId ?? null,
1751
- enabled: isActiveComposer,
1752
- apiUrl,
1753
- });
1754
- const serverRunActive = serverRunState.runId != null && serverRunState.status === "running";
1755
1773
  const storedActiveRun = getActiveRun();
1756
1774
  const activeChatRunId = (serverRunState.status === "running" ? serverRunState.runId : null) ??
1757
1775
  (activeRunMatchesThread(storedActiveRun, threadId)
@@ -3318,6 +3336,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3318
3336
  dequeueInFlightRef.current = true;
3319
3337
  let cancelled = false;
3320
3338
  let started = false;
3339
+ let retryTimer = null;
3321
3340
  const timer = window.setTimeout(() => {
3322
3341
  started = true;
3323
3342
  void (async () => {
@@ -3329,8 +3348,19 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3329
3348
  // complete. Starting the queued turn during that window can reconnect
3330
3349
  // to the old run and replay the old answer under the new prompt.
3331
3350
  const runCleared = await waitForThreadRunToClear(apiUrl, threadId);
3332
- if (cancelled || !runCleared)
3351
+ if (cancelled)
3333
3352
  return;
3353
+ if (!runCleared) {
3354
+ // The server still owns this turn (including a deferred durable
3355
+ // successor). Keep the queued message visible and retry after a
3356
+ // short delay so one transient idle snapshot cannot strand it.
3357
+ retryTimer = window.setTimeout(() => {
3358
+ if (!cancelled) {
3359
+ setQueueWakeVersion((version) => version + 1);
3360
+ }
3361
+ }, ACTIVE_RUN_CLEAR_RETRY_DELAY_MS);
3362
+ return;
3363
+ }
3334
3364
  if (queueStopVersionRef.current !== stopVersion ||
3335
3365
  queuedMessagesRef.current[0]?.id !== next.id) {
3336
3366
  return;
@@ -3391,6 +3421,8 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
3391
3421
  return () => {
3392
3422
  cancelled = true;
3393
3423
  window.clearTimeout(timer);
3424
+ if (retryTimer !== null)
3425
+ window.clearTimeout(retryTimer);
3394
3426
  if (!started) {
3395
3427
  dequeueInFlightRef.current = false;
3396
3428
  }
@@ -12,10 +12,12 @@ export interface ConnectBuilderCardProps {
12
12
  /** The user's feature/change request, forwarded to the selected coding
13
13
  * agent when they click Send. Empty for generic "connect Builder" prompts. */
14
14
  prompt?: string;
15
+ /** Formatted staged chat context forwarded with the Builder request. */
16
+ context?: string;
15
17
  }
16
18
  /**
17
19
  * Rich inline card rendered for the `connect-builder` tool call. Shows the
18
20
  * Builder handoff when available, or routes local development requests to the
19
21
  * external coding agent.
20
22
  */
21
- export declare function ConnectBuilderCard({ configured: initialConfigured, builderEnabled: initialBuilderEnabled, connectUrl: initialConnectUrl, orgName: initialOrgName, prompt, }: ConnectBuilderCardProps): React.JSX.Element;
23
+ export declare function ConnectBuilderCard({ configured: initialConfigured, builderEnabled: initialBuilderEnabled, connectUrl: initialConnectUrl, orgName: initialOrgName, prompt, context, }: ConnectBuilderCardProps): React.JSX.Element;
@@ -30,7 +30,7 @@ function hasElectronShellBridge() {
30
30
  * Builder handoff when available, or routes local development requests to the
31
31
  * external coding agent.
32
32
  */
33
- export function ConnectBuilderCard({ configured: initialConfigured, builderEnabled: initialBuilderEnabled = true, connectUrl: initialConnectUrl, orgName: initialOrgName, prompt = "", }) {
33
+ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabled: initialBuilderEnabled = true, connectUrl: initialConnectUrl, orgName: initialOrgName, prompt = "", context = "", }) {
34
34
  // The connect-poll state machine is shared — the tool-call result is
35
35
  // frozen at render time, so the hook's mount-time fetch + focus refresh
36
36
  // is what catches a flow the user completed in another tab.
@@ -89,7 +89,10 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
89
89
  const res = await fetch(new URL(agentNativePath("/_agent-native/builder/run"), origin).href, {
90
90
  method: "POST",
91
91
  headers: { "Content-Type": "application/json" },
92
- body: JSON.stringify({ prompt }),
92
+ body: JSON.stringify({
93
+ prompt,
94
+ ...(context.trim() ? { context } : {}),
95
+ }),
93
96
  });
94
97
  const data = await res.json().catch(() => ({}));
95
98
  if (!res.ok) {
@@ -108,7 +111,7 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
108
111
  setSendErr(e instanceof Error ? e.message : "Send failed");
109
112
  setSending(false);
110
113
  }
111
- }, [prompt]);
114
+ }, [context, prompt]);
112
115
  const handleJoinWaitlist = useCallback(async () => {
113
116
  setJoiningWaitlist(true);
114
117
  setWaitlistErr(null);
@@ -15,8 +15,6 @@ export type AgentChatSurfaceKind =
15
15
  | "desktop";
16
16
  export declare const BACKGROUND_FOLLOW_ATTACH_WATCHDOG_MS = 90000;
17
17
  export declare const BACKGROUND_FOLLOW_IDLE_TIMEOUT_MS = 210000;
18
- export declare const MAX_FOLLOWED_BACKGROUND_RUNS = 24;
19
- export declare const MAX_BACKGROUND_FOLLOW_WALL_TIME_MS: number;
20
18
  /**
21
19
  * True when an action was streamed but never returned a result yet — i.e. a
22
20
  * `tool_start` with no matching `tool_done`. The server is still executing it,
@@ -170,29 +170,13 @@ export const BACKGROUND_FOLLOW_IDLE_TIMEOUT_MS = 210_000;
170
170
  // 22 minutes, all error:stale_run, user watching a spinner). These bound the
171
171
  // whole turn instead, and an identical repeated failure counts as no progress.
172
172
  //
173
- // CLIENT-ABOVE-SERVER INVARIANT (asserted in agent-chat-adapter.spec.ts).
174
- // These are a backstop for a server that has gone silent in a way the idle
175
- // timeout missesNOT the primary limit. They must stay ABOVE the server's
176
- // own ceilings so the server, which can actually tell progress from looping,
177
- // always terminates a turn first and writes a truthful terminal reason:
178
- // BACKGROUND_SOFT_TIMEOUT_CEILING_MS (13 min, run-manager.ts) — one chunk
179
- // MAX_TURN_WALL_CLOCK_MS (90 min, production-agent.ts) one turn
180
- // MAX_BACKGROUND_RUN_CONTINUATIONS (20, production-agent.ts)
181
- //
182
- // They were originally set to 10 min / 6 runs, which put the whole-turn client
183
- // budget BELOW the 13-minute ceiling of a single legal chunk. Any turn needing
184
- // a second full-length chunk was killed by the client while the server was
185
- // healthy and had 80 minutes left — measured in prod as turns dying at 11-25
186
- // minutes with `last_progress_at` tracking the abort, i.e. still streaming
187
- // tokens and completing tools when the client gave up. That is the top
188
- // non-auth cause of "the chat just stopped" reports.
189
- //
190
- // Killing a turn that is NOT progressing is already covered twice over, by
191
- // mechanisms that read progress rather than a clock: the 210s idle timeout
192
- // above, and MAX_REPEATED_BACKGROUND_TERMINAL_REASONS below. Do not re-tighten
193
- // these two to catch a stuck turn — fix the progress signal instead.
194
- export const MAX_FOLLOWED_BACKGROUND_RUNS = 24;
195
- export const MAX_BACKGROUND_FOLLOW_WALL_TIME_MS = 95 * 60_000;
173
+ // Defined in `app-config/run-lifecycle-invariants.ts`, not here, because the
174
+ // CLIENT-ABOVE-SERVER relationship they belong to is checked there against the
175
+ // RESOLVED server configuration — the server bounds are runtime-configurable
176
+ // now, so a spec pinning them against module constants stopped enforcing
177
+ // anything. Re-exported under the same names so importers are unchanged; that
178
+ // module has no runtime imports, so the bundle pays nothing for it.
179
+ import { MAX_BACKGROUND_FOLLOW_WALL_TIME_MS, MAX_FOLLOWED_BACKGROUND_RUNS, } from "../app-config/run-lifecycle-invariants.js";
196
180
  const MAX_REPEATED_BACKGROUND_TERMINAL_REASONS = 3;
197
181
  // A re-observed terminal run whose outcome would be an ERROR (never a
198
182
  // genuine "done" success) gets a short extra grace window before the follow
@@ -115,6 +115,13 @@ export declare function shouldShowMissingFinalResponse({ isCurrentTurnRunning, s
115
115
  hasActiveTool?: boolean;
116
116
  userStoppedRun?: boolean;
117
117
  }): boolean;
118
+ /**
119
+ * "The agent stopped" is derived from local client state, which dips to
120
+ * not-running at every chunk boundary and transport re-attach while the turn is
121
+ * still alive server-side. Requiring the shape to hold for a beat keeps the
122
+ * notice off the screen for those gaps without hiding a real stop for long.
123
+ */
124
+ export declare const MISSING_FINAL_RESPONSE_SETTLE_MS = 3000;
118
125
  export declare function useSettledFlag(active: boolean, delayMs: number): boolean;
119
126
  export declare function shouldShowAssistantWorkSummary({ isLast, isComplete, hasCollapsibleWork, hasUnresolvedTool, hasActiveTool, chatRunning, }: {
120
127
  isLast: boolean;
@@ -728,7 +728,7 @@ export function shouldShowMissingFinalResponse({ isCurrentTurnRunning, serverRun
728
728
  * still alive server-side. Requiring the shape to hold for a beat keeps the
729
729
  * notice off the screen for those gaps without hiding a real stop for long.
730
730
  */
731
- const MISSING_FINAL_RESPONSE_SETTLE_MS = 250;
731
+ export const MISSING_FINAL_RESPONSE_SETTLE_MS = 3_000;
732
732
  export function useSettledFlag(active, delayMs) {
733
733
  const [settled, setSettled] = useState(false);
734
734
  useEffect(() => {
@@ -120,6 +120,9 @@ function isMissingLlmProviderRunError(info) {
120
120
  ((code === "missing_credentials" || code === "missing_api_key") &&
121
121
  !text.trim()));
122
122
  }
123
+ function isDesktopChatRelayRunError(info) {
124
+ return [info.message, info.details].some((value) => typeof value === "string" && /desktop app chat relay failed/i.test(value));
125
+ }
123
126
  // ─── BuilderConnectCta ────────────────────────────────────────────────────────
124
127
  // Renders a single row with left-aligned copy and a right-aligned action.
125
128
  // Click opens the Builder OAuth popup via the shared
@@ -210,7 +213,7 @@ export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDism
210
213
  });
211
214
  const canRecover = info.recoverable === true;
212
215
  const shouldShowBuilderReconnect = isBuilderReconnectRunError(info);
213
- const shouldShowMissingProviderSetup = isMissingLlmProviderRunError(info);
216
+ const shouldShowProviderSetup = isMissingLlmProviderRunError(info) || isDesktopChatRelayRunError(info);
214
217
  const isProviderAuthError = isProviderAuthenticationError([info.message, info.details].filter(Boolean).join("\n"), info.errorCode);
215
218
  // Blocked on something the reader goes and fixes elsewhere, then comes back
216
219
  // to. Recoverable runs and email verification keep a retry path; rejected
@@ -292,7 +295,7 @@ export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDism
292
295
  onDismiss();
293
296
  }
294
297
  }, [builderReconnectResolved, onDismiss]);
295
- if (shouldShowMissingProviderSetup) {
298
+ if (shouldShowProviderSetup) {
296
299
  return (_jsxs("div", { className: "w-full", children: [_jsx(BuilderSetupCard, { fullWidth: true, layout: "sidebar", onConnected: handleMissingProviderConnected }), providerConnected ? (_jsx("div", { className: "flex justify-center px-3 pt-1", children: _jsxs("button", { type: "button", onClick: handleMissingProviderRetry, disabled: retryRequested, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-60", children: [_jsx(IconRefresh, { size: 13 }), t("agentChat.common.retry")] }) })) : null] }));
297
300
  }
298
301
  return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] p-3 text-sm", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-700 dark:text-amber-300", children: _jsx(IconAlertTriangle, { size: 14 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-medium text-foreground", children: runErrorHeadline(info, {
@@ -1,6 +1,7 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { IconLoader2, IconAlertTriangle, IconCircleX, IconCheck, IconChevronRight, IconCopy, IconCode, IconBrandSlack, IconTerminal2, IconDatabase, IconSearch, IconFileCode, IconShieldCheck, IconX, } from "@tabler/icons-react";
3
3
  import React, { useState, useEffect, useCallback, useLayoutEffect, useRef, } from "react";
4
+ import { formatAgentChatContextItemsForPrompt } from "../agent-chat.js";
4
5
  import { AgentTaskCard } from "../AgentTaskCard.js";
5
6
  import { writeClipboardText } from "../clipboard.js";
6
7
  import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
@@ -12,6 +13,7 @@ import { findMcpIntegrationForToolName } from "../resources/mcp-integration-cata
12
13
  import { McpIntegrationLogo } from "../resources/McpIntegrationLogo.js";
13
14
  import { BashCell, EditCell, WriteCell, FilesChangedSummary, } from "../tool-cells/index.js";
14
15
  import { humanizeToolName, isCallAgentToolCallShadowed, isToolCallActive, } from "../tool-display.js";
16
+ import { useAgentChatContext } from "../use-agent-chat-context.js";
15
17
  import { cn } from "../utils.js";
16
18
  import { ActionChatUiSurface } from "./action-chat-ui-surface.js";
17
19
  import { SmoothMarkdownText, HighlightedCodeBlock, } from "./markdown-renderer.js";
@@ -280,6 +282,10 @@ function ApprovalAffordance({ toolName, toolCallId, approval, }) {
280
282
  }
281
283
  // ─── ToolCallDisplay ──────────────────────────────────────────────────────────
282
284
  export function ToolCallDisplay({ toolName, toolCallId, argsText, args, result, mcpApp, chatUI, isRunning, outcome, structuredMeta, activity, approval, repeatCount, isLatestRunning = isRunning, isActiveTail, }) {
285
+ const { items: agentChatContextItems } = useAgentChatContext(toolName === "connect-builder");
286
+ const builderContext = toolName === "connect-builder"
287
+ ? formatAgentChatContextItemsForPrompt(agentChatContextItems)
288
+ : "";
283
289
  const isDelegatedAgentCall = toolName === "call-agent" || toolName.startsWith("agent:");
284
290
  const effectiveIsRunning = isRunning ||
285
291
  (isDelegatedAgentCall &&
@@ -306,9 +312,9 @@ export function ToolCallDisplay({ toolName, toolCallId, argsText, args, result,
306
312
  if (toolKind === "write") {
307
313
  return wrapToolDisplay(_jsx(WriteCell, { meta: structuredMeta, isRunning: effectiveIsRunning }));
308
314
  }
309
- return wrapToolDisplay(_jsx(ToolCallDisplayGeneric, { toolName: toolName, toolCallId: toolCallId, argsText: argsText, args: args, result: result, mcpApp: mcpApp, chatUI: chatUI, isRunning: effectiveIsRunning, outcome: outcome, isActiveTail: showActiveTail, structuredMeta: structuredMeta, approval: approval, repeatCount: repeatCount }));
315
+ return wrapToolDisplay(_jsx(ToolCallDisplayGeneric, { toolName: toolName, toolCallId: toolCallId, argsText: argsText, args: args, result: result, mcpApp: mcpApp, chatUI: chatUI, isRunning: effectiveIsRunning, outcome: outcome, isActiveTail: showActiveTail, structuredMeta: structuredMeta, approval: approval, repeatCount: repeatCount, context: builderContext }));
310
316
  }
311
- function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result, mcpApp, chatUI, isRunning, outcome, isActiveTail, structuredMeta, approval, repeatCount, }) {
317
+ function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result, mcpApp, chatUI, isRunning, outcome, isActiveTail, structuredMeta, approval, repeatCount, context, }) {
312
318
  const t = useT();
313
319
  const suppressInlineOpenApp = React.useContext(SuppressInlineOpenAppContext);
314
320
  const isRawCallAgent = toolName === "call-agent";
@@ -339,7 +345,7 @@ function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result,
339
345
  return (_jsx(ConnectBuilderCard, { configured: !!parsed.configured, builderEnabled: parsed.builderEnabled !== false,
340
346
  // Ignore obsolete direct-auth URLs from older tool results. The
341
347
  // card fetches a fresh app-local connect URL on mount and click.
342
- connectUrl: parsed.connectUrl || "", orgName: parsed.orgName ?? null, prompt: typeof parsed.prompt === "string" ? parsed.prompt : "" }));
348
+ connectUrl: parsed.connectUrl || "", orgName: parsed.orgName ?? null, prompt: typeof parsed.prompt === "string" ? parsed.prompt : "", context: context }));
343
349
  }
344
350
  }
345
351
  catch {
@@ -80,6 +80,18 @@ const KNOWN_CHAT_ERROR_KEYS = new Map([
80
80
  "No LLM provider is connected. Open this app's Manage agent > LLM, then connect Builder.io or add a provider key.",
81
81
  "agentChat.errorMessages.noProviderConnected",
82
82
  ],
83
+ [
84
+ "No LLM provider is connected. Open this app's Manage agent > LLM, then connect Builder.io (free tier available) or add a provider key.",
85
+ "agentChat.errorMessages.noProviderConnected",
86
+ ],
87
+ [
88
+ "No LLM provider is connected. Open Settings > Agent > AI providers, then connect Builder.io or add a provider key.",
89
+ "agentChat.errorMessages.noProviderConnected",
90
+ ],
91
+ [
92
+ "No LLM provider is connected. Open Settings > Agent > AI providers, then connect Builder.io (free tier available) or add a provider key.",
93
+ "agentChat.errorMessages.noProviderConnected",
94
+ ],
83
95
  [
84
96
  "The provider behind this model rejected the request. Pick a different model, then retry.",
85
97
  "agentChat.errorMessages.builderModelUnauthorized",
@@ -1,5 +1,6 @@
1
1
  export { agentNativePath, appApiPath, appBasePath, appPath, } from "../api-path.js";
2
2
  export { useAgentRouteState, useSemanticNavigationState, type AgentRouteLocation, type SemanticNavigationCommandEnvelope, type UseAgentRouteStateOptions, type UseAgentRouteStateResult, type UseSemanticNavigationStateOptions, type UseSemanticNavigationStateResult, } from "../route-state.js";
3
+ export { AGENT_NATIVE_WORKSPACE_APP_ROUTE_MESSAGE_TYPE, postAgentNativeWorkspaceAppRoute, type AgentNativeWorkspaceAppRouteMessage, } from "../workspace-app-navigation.js";
3
4
  export { COMMAND_MENU_OPEN_EVENT, CommandMenu, openAgentSidebar, openAgentSettings, openCommandMenu, submitToAgent, useCommandMenuShortcut, type CommandMenuProps, type CommandMenuDoc, type CommandDocsGroupProps, type CommandGroupProps, type CommandItemProps, type CommandShortcutProps, } from "../CommandMenu.js";
4
5
  export { buildOpenRouteLink, buildOpenRoutePath, buildLegacyAgentSettingsRoute, buildResourceRoute, buildSettingsRoute, buildStandardAppRoute, buildTeamRoute, createStandardOpenPathResolver, STANDARD_APP_ROUTES, STANDARD_SETTINGS_TABS, type BuildResourceRouteOptions, type BuildStandardAppRouteOptions, type NavigationLink, type NavigationTarget, type StandardAppRouteId, type StandardOpenPathResolverOptions, type StandardOpenPathRoute, type StandardSettingsTabId, } from "../../navigation/index.js";
5
6
  export { postNavigate, isInAgentEmbed, AGENT_NAVIGATE_MESSAGE_TYPE, type AgentNavigateMessage, } from "../embed.js";
@@ -1,5 +1,6 @@
1
1
  export { agentNativePath, appApiPath, appBasePath, appPath, } from "../api-path.js";
2
2
  export { useAgentRouteState, useSemanticNavigationState, } from "../route-state.js";
3
+ export { AGENT_NATIVE_WORKSPACE_APP_ROUTE_MESSAGE_TYPE, postAgentNativeWorkspaceAppRoute, } from "../workspace-app-navigation.js";
3
4
  export { COMMAND_MENU_OPEN_EVENT, CommandMenu, openAgentSidebar, openAgentSettings, openCommandMenu, submitToAgent, useCommandMenuShortcut, } from "../CommandMenu.js";
4
5
  export { buildOpenRouteLink, buildOpenRoutePath, buildLegacyAgentSettingsRoute, buildResourceRoute, buildSettingsRoute, buildStandardAppRoute, buildTeamRoute, createStandardOpenPathResolver, STANDARD_APP_ROUTES, STANDARD_SETTINGS_TABS, } from "../../navigation/index.js";
5
6
  export { postNavigate, isInAgentEmbed, AGENT_NAVIGATE_MESSAGE_TYPE, } from "../embed.js";
@@ -4,6 +4,7 @@ import { useLocation, useNavigate, } from "react-router";
4
4
  import { isWorkspaceAppPath } from "./api-path.js";
5
5
  import { deleteClientAppState, readClientAppState, setClientAppState, } from "./application-state.js";
6
6
  import { navigateWithAgentChatViewTransition, } from "./chat-view-transition.js";
7
+ import { postAgentNativeWorkspaceAppRoute } from "./workspace-app-navigation.js";
7
8
  const SAFE_BROWSER_TAB_ID_RE = /^[A-Za-z0-9_-]{1,96}$/;
8
9
  function normalizeBrowserTabId(browserTabId) {
9
10
  if (typeof browserTabId !== "string")
@@ -195,6 +196,11 @@ export function useAgentRouteState(options) {
195
196
  const location = useLocation();
196
197
  const navigate = useNavigate();
197
198
  const browserTabId = useMemo(() => normalizeBrowserTabId(options.browserTabId), [options.browserTabId]);
199
+ useEffect(() => {
200
+ if (options.enabled === false)
201
+ return;
202
+ postAgentNativeWorkspaceAppRoute(`${location.pathname}${location.search}${location.hash}`);
203
+ }, [location.hash, location.pathname, location.search, options.enabled]);
198
204
  const navigationKeys = useMemo(() => {
199
205
  const scopedKey = appStateKeyForBrowserTab(navigationKey, browserTabId);
200
206
  const keys = [scopedKey];
@@ -0,0 +1,10 @@
1
+ export declare const AGENT_NATIVE_WORKSPACE_APP_ROUTE_MESSAGE_TYPE: "agent-native:workspace-app-route";
2
+ export interface AgentNativeWorkspaceAppRouteMessage {
3
+ type: typeof AGENT_NATIVE_WORKSPACE_APP_ROUTE_MESSAGE_TYPE;
4
+ path: string;
5
+ }
6
+ /**
7
+ * Report an app-local route to its immediate embedding host. The host checks
8
+ * the iframe source and origin before applying the route to its own URL.
9
+ */
10
+ export declare function postAgentNativeWorkspaceAppRoute(path: string): boolean;
@@ -0,0 +1,38 @@
1
+ export const AGENT_NATIVE_WORKSPACE_APP_ROUTE_MESSAGE_TYPE = "agent-native:workspace-app-route";
2
+ function normalizeWorkspaceAppRoute(path) {
3
+ if (typeof path !== "string" ||
4
+ !path.startsWith("/") ||
5
+ path.startsWith("//")) {
6
+ return null;
7
+ }
8
+ if (/[\u0000-\u001f\u007f]/.test(path))
9
+ return null;
10
+ try {
11
+ const url = new URL(path, "http://agent-native.invalid");
12
+ return `${url.pathname}${url.search}${url.hash}`;
13
+ }
14
+ catch {
15
+ // coercion-ok: malformed route input is not reported to the host.
16
+ return null;
17
+ }
18
+ }
19
+ /**
20
+ * Report an app-local route to its immediate embedding host. The host checks
21
+ * the iframe source and origin before applying the route to its own URL.
22
+ */
23
+ export function postAgentNativeWorkspaceAppRoute(path) {
24
+ if (typeof window === "undefined")
25
+ return false;
26
+ const parentWindow = window.parent;
27
+ if (!parentWindow || parentWindow === window)
28
+ return false;
29
+ const normalizedPath = normalizeWorkspaceAppRoute(path);
30
+ if (!normalizedPath)
31
+ return false;
32
+ const message = {
33
+ type: AGENT_NATIVE_WORKSPACE_APP_ROUTE_MESSAGE_TYPE,
34
+ path: normalizedPath,
35
+ };
36
+ parentWindow.postMessage(message, "*");
37
+ return true;
38
+ }
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- ok?: undefined;
17
16
  error: string;
17
+ ok?: undefined;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -254,6 +254,19 @@ export declare function assertSingleTemplateNetlifyBuildOutput(projectCwd: strin
254
254
  * Netlify platform 404s. Preserve any real redirects from `public/_redirects`.
255
255
  */
256
256
  export declare function writeSingleTemplateNetlifyRedirects(projectCwd: string): void;
257
+ /**
258
+ * Whether the emitted bundle actually imports the `libsql` native addon.
259
+ *
260
+ * The `@libsql/client` node entry `require`s it; `@libsql/client/web` and
261
+ * `better-sqlite3` do not. Probing the emitted output is the only gate that
262
+ * cannot be wrong: `getDialect()` reads `DATABASE_URL` at RUNTIME, and neither
263
+ * the docs nor the beta deploy workflow sets it at build time, so build-time
264
+ * dialect is unknowable. This mirrors `findServerlessBrowserRuntimeConsumer`,
265
+ * which already gates the Chromium copy the same way — the asymmetry is why a
266
+ * 9.3MB Linux SQLite driver shipped in the docs function, a deployment that
267
+ * runs Postgres and can never load it.
268
+ */
269
+ export declare function bundleImportsLibsqlNativeAddon(serverDir: string): boolean;
257
270
  /**
258
271
  * Nitro's file tracer can over-include optional desktop/dev packages that are
259
272
  * present in a monorepo install but cannot run in serverless. Netlify installs
@@ -3389,9 +3389,60 @@ export function writeSingleTemplateNetlifyRedirects(projectCwd) {
3389
3389
  }
3390
3390
  console.log('[deploy] Removed Netlify fallback rewrite to /.netlify/functions/server (incompatible with Nitro config.path: "/*").');
3391
3391
  }
3392
+ /**
3393
+ * Whether the emitted bundle actually imports the `libsql` native addon.
3394
+ *
3395
+ * The `@libsql/client` node entry `require`s it; `@libsql/client/web` and
3396
+ * `better-sqlite3` do not. Probing the emitted output is the only gate that
3397
+ * cannot be wrong: `getDialect()` reads `DATABASE_URL` at RUNTIME, and neither
3398
+ * the docs nor the beta deploy workflow sets it at build time, so build-time
3399
+ * dialect is unknowable. This mirrors `findServerlessBrowserRuntimeConsumer`,
3400
+ * which already gates the Chromium copy the same way — the asymmetry is why a
3401
+ * 9.3MB Linux SQLite driver shipped in the docs function, a deployment that
3402
+ * runs Postgres and can never load it.
3403
+ */
3404
+ export function bundleImportsLibsqlNativeAddon(serverDir) {
3405
+ const bareImport = /(?:require\(|from\s*)["']libsql["']/;
3406
+ const stack = [serverDir];
3407
+ while (stack.length > 0) {
3408
+ const dir = stack.pop();
3409
+ let entries;
3410
+ try {
3411
+ entries = fs.readdirSync(dir, { withFileTypes: true });
3412
+ }
3413
+ catch {
3414
+ continue;
3415
+ }
3416
+ for (const entry of entries) {
3417
+ const full = path.join(dir, entry.name);
3418
+ if (entry.isDirectory()) {
3419
+ // The copied native package itself is the thing being gated; its own
3420
+ // files must never count as a consumer.
3421
+ if (entry.name === "node_modules" || entry.name === "@libsql")
3422
+ continue;
3423
+ stack.push(full);
3424
+ continue;
3425
+ }
3426
+ if (!/\.(?:mjs|cjs|js)$/.test(entry.name))
3427
+ continue;
3428
+ try {
3429
+ if (bareImport.test(fs.readFileSync(full, "utf8")))
3430
+ return true;
3431
+ }
3432
+ catch {
3433
+ continue;
3434
+ }
3435
+ }
3436
+ }
3437
+ return false;
3438
+ }
3392
3439
  function copyInstalledLibsqlNativePackages(serverDir) {
3393
3440
  if (!serverDir || !fs.existsSync(serverDir))
3394
3441
  return;
3442
+ if (!bundleImportsLibsqlNativeAddon(serverDir)) {
3443
+ console.log("[deploy] Skipped the libsql native package: the emitted bundle never imports the `libsql` addon.");
3444
+ return;
3445
+ }
3395
3446
  const nodeModulesRoots = nodeModulesAncestors(cwd);
3396
3447
  const destScopeDir = path.join(serverDir, "node_modules", "@libsql");
3397
3448
  let copied = 0;
@@ -5,7 +5,23 @@ import { type AutomationExecutionIdentity } from "../automations/service.js";
5
5
  import { type Resource } from "../resources/store.js";
6
6
  import { type RequestContext } from "../server/request-context.js";
7
7
  import type { JobFrontmatter } from "./frontmatter.js";
8
+ /**
9
+ * Default hard abort for one in-process automation run. Read through
10
+ * `resolveBackgroundRunHardTimeoutMs()` at the use site — this is the host's
11
+ * real function budget for scheduled work, and it differs by deployment.
12
+ */
8
13
  export declare const BACKGROUND_RUN_HARD_TIMEOUT_MS: number;
14
+ /**
15
+ * Terminal failure of a background automation, carrying the machine-readable
16
+ * code the failure taxonomy already computes.
17
+ *
18
+ * The code used to be produced and then dropped, so "how often are runs cut
19
+ * off?" was a `LIKE '%no_progress%'` over an English sentence.
20
+ */
21
+ export declare class BackgroundAutomationRunError extends Error {
22
+ readonly errorCode: string;
23
+ constructor(message: string, errorCode: string);
24
+ }
9
25
  export interface BackgroundAutomationContext {
10
26
  name: string;
11
27
  meta: JobFrontmatter;
@@ -35,6 +51,15 @@ export interface BackgroundAutomationRunOptions {
35
51
  actionAutomation?: ActionAutomationContext;
36
52
  /** Reuse a history row created by a durable run-now enqueue. */
37
53
  historyId?: string;
54
+ /**
55
+ * Per-run overrides for the run-manager no-progress backstop. `startRun` has
56
+ * always accepted these; the automation path had no way to reach them, and
57
+ * the one indirect route (zeroing `agent.runSoftTimeoutMs`) is global and
58
+ * would strip foreground chat of its chunk boundary. Additive: unset means
59
+ * the configured/default behaviour, unchanged.
60
+ */
61
+ noProgressTimeoutMs?: number;
62
+ backgroundNoProgressTimeoutMs?: number;
38
63
  }
39
64
  export interface BackgroundAutomationRunResult {
40
65
  responseText: string;