@agent-native/core 0.101.3 → 0.101.4

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 (41) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +7 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/client/RunStuckBanner.tsx +12 -5
  5. package/corpus/core/src/client/sse-event-processor.ts +36 -1
  6. package/corpus/core/src/client/use-run-stuck-detection.ts +162 -25
  7. package/corpus/core/src/deploy/build.ts +78 -25
  8. package/corpus/templates/chat/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +6 -0
  9. package/corpus/templates/clips/app/components/player/media-duration.ts +24 -0
  10. package/corpus/templates/clips/app/components/player/video-player.tsx +9 -18
  11. package/corpus/templates/clips/changelog/2026-07-14-paused-time-no-longer-counts-toward-chrome-extension-recordi.md +6 -0
  12. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +30 -4
  13. package/corpus/templates/clips/chrome-extension/src/recording-duration.ts +32 -0
  14. package/corpus/templates/slides/actions/_uploaded-files.ts +13 -2
  15. package/corpus/templates/slides/actions/import-docx.ts +3 -9
  16. package/corpus/templates/slides/actions/import-file.ts +11 -14
  17. package/corpus/templates/slides/actions/import-pptx.ts +3 -9
  18. package/corpus/templates/slides/changelog/2026-07-14-powerpoint-template-uploads-now-work-in-hosted-slides-deploy.md +6 -0
  19. package/corpus/templates/slides/server/handlers/uploads.ts +100 -50
  20. package/corpus/templates/slides/server/lib/tenant-files.ts +38 -11
  21. package/corpus/templates/slides/server/lib/uploaded-reference-storage.ts +108 -0
  22. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  23. package/dist/client/RunStuckBanner.js +9 -5
  24. package/dist/client/RunStuckBanner.js.map +1 -1
  25. package/dist/client/sse-event-processor.d.ts.map +1 -1
  26. package/dist/client/sse-event-processor.js +33 -1
  27. package/dist/client/sse-event-processor.js.map +1 -1
  28. package/dist/client/use-run-stuck-detection.d.ts +4 -4
  29. package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
  30. package/dist/client/use-run-stuck-detection.js +104 -19
  31. package/dist/client/use-run-stuck-detection.js.map +1 -1
  32. package/dist/collab/awareness.d.ts +2 -2
  33. package/dist/collab/awareness.d.ts.map +1 -1
  34. package/dist/deploy/build.d.ts +15 -14
  35. package/dist/deploy/build.d.ts.map +1 -1
  36. package/dist/deploy/build.js +64 -23
  37. package/dist/deploy/build.js.map +1 -1
  38. package/dist/notifications/routes.d.ts +3 -3
  39. package/dist/progress/routes.d.ts +1 -1
  40. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  41. package/package.json +1 -1
@@ -28,6 +28,89 @@ export function useRunStuckDetection({ threadId, enabled = true, stuckThresholdM
28
28
  const base = apiUrl ?? agentNativePath("/_agent-native/agent-chat");
29
29
  let cancelled = false;
30
30
  let timer = null;
31
+ let snapshotTransitionTimer = null;
32
+ let snapshotVersion = 0;
33
+ const effectiveThresholdFor = (dispatchMode, heartbeatSinceMs) => {
34
+ const liveBackgroundWorker = dispatchMode === "background-processing" &&
35
+ heartbeatSinceMs != null &&
36
+ heartbeatSinceMs >= 0 &&
37
+ heartbeatSinceMs < FRESH_BACKGROUND_HEARTBEAT_MS;
38
+ const serverContinued = dispatchMode === "foreground-self-chain" ||
39
+ dispatchMode?.startsWith("background") === true;
40
+ return liveBackgroundWorker
41
+ ? Math.min(backgroundStuckThresholdMs, liveBackgroundStuckThresholdMs)
42
+ : serverContinued
43
+ ? backgroundStuckThresholdMs
44
+ : stuckThresholdMs;
45
+ };
46
+ const scheduleSnapshotTransition = (snapshot, observedAtMs, version) => {
47
+ if (snapshotTransitionTimer)
48
+ clearTimeout(snapshotTransitionTimer);
49
+ snapshotTransitionTimer = null;
50
+ if (cancelled ||
51
+ version !== snapshotVersion ||
52
+ !snapshot.active ||
53
+ snapshot.status !== "running" ||
54
+ snapshot.runId == null) {
55
+ return;
56
+ }
57
+ const currentElapsedMs = Math.max(0, Date.now() - observedAtMs);
58
+ const currentHeartbeatSinceMs = snapshot.heartbeatSinceMs == null
59
+ ? null
60
+ : snapshot.heartbeatSinceMs + currentElapsedMs;
61
+ const currentStuckSinceMs = snapshot.stuckSinceMs == null
62
+ ? null
63
+ : snapshot.stuckSinceMs + currentElapsedMs;
64
+ const effectiveThresholdMs = effectiveThresholdFor(snapshot.dispatchMode, currentHeartbeatSinceMs);
65
+ const currentlyStuck = Boolean(currentStuckSinceMs != null &&
66
+ currentStuckSinceMs > effectiveThresholdMs);
67
+ const transitionDelaysMs = [];
68
+ if (currentHeartbeatSinceMs != null &&
69
+ currentHeartbeatSinceMs >= 0 &&
70
+ currentHeartbeatSinceMs < FRESH_BACKGROUND_HEARTBEAT_MS) {
71
+ transitionDelaysMs.push(FRESH_BACKGROUND_HEARTBEAT_MS - currentHeartbeatSinceMs + 1);
72
+ }
73
+ if (currentStuckSinceMs != null && !currentlyStuck) {
74
+ transitionDelaysMs.push(Math.max(1, effectiveThresholdMs - currentStuckSinceMs + 1));
75
+ }
76
+ if (transitionDelaysMs.length === 0)
77
+ return;
78
+ // Both ages originate from the server clock. Advance those snapshots by
79
+ // only a local elapsed duration, which remains safe when client/server
80
+ // wall clocks differ. Wake at the earlier semantic boundary, then
81
+ // reschedule if the other boundary is still ahead.
82
+ const delayMs = Math.max(1, Math.min(...transitionDelaysMs));
83
+ snapshotTransitionTimer = setTimeout(() => {
84
+ snapshotTransitionTimer = null;
85
+ if (cancelled || version !== snapshotVersion)
86
+ return;
87
+ const elapsedSinceObservationMs = Math.max(0, Date.now() - observedAtMs);
88
+ const nextHeartbeatSinceMs = snapshot.heartbeatSinceMs == null
89
+ ? null
90
+ : snapshot.heartbeatSinceMs + elapsedSinceObservationMs;
91
+ const nextStuckSinceMs = snapshot.stuckSinceMs == null
92
+ ? null
93
+ : snapshot.stuckSinceMs + elapsedSinceObservationMs;
94
+ const nextEffectiveThresholdMs = effectiveThresholdFor(snapshot.dispatchMode, nextHeartbeatSinceMs);
95
+ const nextIsStuck = Boolean(nextStuckSinceMs != null &&
96
+ nextStuckSinceMs > nextEffectiveThresholdMs);
97
+ setState((current) => {
98
+ if (version !== snapshotVersion ||
99
+ current.runId !== snapshot.runId ||
100
+ current.lastProgressAt !== snapshot.lastProgressAt ||
101
+ current.heartbeatAt !== snapshot.heartbeatAt) {
102
+ return current;
103
+ }
104
+ return {
105
+ ...current,
106
+ isStuck: nextIsStuck,
107
+ stuckSinceMs: nextStuckSinceMs,
108
+ heartbeatSinceMs: nextHeartbeatSinceMs,
109
+ };
110
+ });
111
+ scheduleSnapshotTransition(snapshot, observedAtMs, version);
112
+ }, delayMs);
113
+ };
31
114
  const poll = async () => {
32
115
  if (cancelled)
33
116
  return;
@@ -49,30 +132,29 @@ export function useRunStuckDetection({ threadId, enabled = true, stuckThresholdM
49
132
  const heartbeatAt = data.heartbeatAt ?? null;
50
133
  const heartbeatSinceMs = heartbeatAt != null ? nowMs - heartbeatAt : null;
51
134
  const dispatchMode = typeof data.dispatchMode === "string" ? data.dispatchMode : null;
52
- // Server-continued runs get the wider threshold: the server's own
53
- // recovery (150s no-progress backstop + chained continuations) must
54
- // get its chance before the user sees a "stuck" affordance.
55
- const serverContinued = dispatchMode === "foreground-self-chain" ||
56
- dispatchMode?.startsWith("background") === true;
57
135
  // A claimed durable worker with a fresh heartbeat can legitimately
58
- // be waiting on a bounded long-running tool/sub-agent call. Showing
59
- // Retry at the generic 3-minute continuation threshold aborts healthy
60
- // work and starts the same call again. Let the worker's own 12-minute
61
- // watchdog act first; a dead/stale heartbeat still gets the earlier
62
- // background fallback below.
63
- const liveBackgroundWorker = dispatchMode === "background-processing" &&
64
- heartbeatSinceMs != null &&
65
- heartbeatSinceMs >= 0 &&
66
- heartbeatSinceMs < FRESH_BACKGROUND_HEARTBEAT_MS;
67
- const effectiveThresholdMs = liveBackgroundWorker
68
- ? liveBackgroundStuckThresholdMs
69
- : serverContinued
70
- ? backgroundStuckThresholdMs
71
- : stuckThresholdMs;
136
+ // be waiting on a bounded long-running tool/sub-agent call. Still
137
+ // surface informational status at the normal background threshold;
138
+ // RunStuckBanner uses the fresh heartbeat to withhold Retry while
139
+ // keeping an explicit Cancel available. The legacy live-worker
140
+ // threshold may make that notice earlier, but never later.
141
+ const effectiveThresholdMs = effectiveThresholdFor(dispatchMode, heartbeatSinceMs);
72
142
  const isStuck = Boolean(data.active &&
73
143
  data.status === "running" &&
74
144
  stuckSinceMs != null &&
75
145
  stuckSinceMs > effectiveThresholdMs);
146
+ const observedAtMs = Date.now();
147
+ const version = ++snapshotVersion;
148
+ scheduleSnapshotTransition({
149
+ active: data.active,
150
+ runId: data.runId ?? null,
151
+ status: data.status ?? null,
152
+ lastProgressAt,
153
+ stuckSinceMs,
154
+ heartbeatAt,
155
+ heartbeatSinceMs,
156
+ dispatchMode,
157
+ }, observedAtMs, version);
76
158
  setState({
77
159
  isStuck,
78
160
  runId: data.runId ?? null,
@@ -107,8 +189,11 @@ export function useRunStuckDetection({ threadId, enabled = true, stuckThresholdM
107
189
  timer = setTimeout(poll, 2_000);
108
190
  return () => {
109
191
  cancelled = true;
192
+ snapshotVersion += 1;
110
193
  if (timer)
111
194
  clearTimeout(timer);
195
+ if (snapshotTransitionTimer)
196
+ clearTimeout(snapshotTransitionTimer);
112
197
  };
113
198
  }, [
114
199
  threadId,
@@ -1 +1 @@
1
- {"version":3,"file":"use-run-stuck-detection.js","sourceRoot":"","sources":["../../src/client/use-run-stuck-detection.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState, useCallback } from \"react\";\n\nimport { agentNativePath } from \"./api-path.js\";\n\n/**\n * Per-thread chat run health, derived from the durable `last_progress_at`\n * timestamp on the server. Drives the user-visible \"this chat looks stuck\"\n * affordance — distinct from the silent reconnect logic in\n * `agent-chat-adapter.ts`, which keeps trying in the background. When\n * automatic recovery isn't making progress (for whatever reason), this\n * hook surfaces a Retry / Cancel button to the user instead of leaving\n * them staring at a frozen spinner.\n */\nexport interface RunStuckState {\n /** True when an active run hasn't emitted an event for `stuckThresholdMs`. */\n isStuck: boolean;\n /** ID of the active run, or null when nothing is in flight. */\n runId: string | null;\n /** Server-side run status (\"running\" / \"completed\" / \"errored\" / etc.). */\n status: string | null;\n /** Server timestamp (ms) of the last emitted event, or null if none yet. */\n lastProgressAt: number | null;\n /** Milliseconds since `lastProgressAt`, or null. */\n stuckSinceMs: number | null;\n /** Server timestamp (ms) of the last process-alive heartbeat. */\n heartbeatAt: number | null;\n /** Milliseconds since `heartbeatAt`, computed against the server clock. */\n heartbeatSinceMs: number | null;\n /** How the run was dispatched/continued, e.g. foreground-self-chain or background-processing. */\n dispatchMode: string | null;\n /**\n * Server-authoritative: true when the run holds an open tool call or A2A\n * `agent_call` delegation (`in_flight_since` marker set). Preferred over the\n * client-side proxy for deciding whether Retry (which aborts the run) is\n * safe to offer. Null when the server bundle predates this field.\n */\n hasInFlightWork: boolean | null;\n}\n\nexport interface UseRunStuckDetectionOptions {\n /** The thread to monitor. Pass null/undefined to disable polling. */\n threadId: string | null | undefined;\n /**\n * Set false to skip scheduling the poll loop entirely — used to gate\n * polling to only the active chat tab when multiple tabs are mounted\n * (inactive tabs are kept alive via display:none, not unmounted).\n * Defaults to true.\n */\n enabled?: boolean;\n /**\n * Threshold above which an in-flight FOREGROUND run is considered stuck.\n * The default sits comfortably above the adapter's 75s no-progress\n * reconnect — by then automatic recovery has already had its chance.\n */\n stuckThresholdMs?: number;\n /**\n * Threshold for BACKGROUND-dispatched runs (dispatchMode starts with\n * \"background\"). The server owns recovery for these — its run-manager\n * no-progress backstop (150s) and unclaimed-run sweep act first — so the\n * user-facing \"stuck\" affordance is a late fallback, not a race against\n * them. Selected inside the hook because the dispatch mode is only known\n * from the same poll response that computes the elapsed time.\n */\n backgroundStuckThresholdMs?: number;\n /**\n * Threshold for a claimed durable background worker that is still sending\n * fresh process heartbeats. These workers can legitimately spend up to the\n * 12-minute tool/no-progress window on large Design, Plan, or Assets work.\n * Default 13 minutes, matching the durable chunk handoff boundary.\n */\n liveBackgroundStuckThresholdMs?: number;\n /** Poll interval. Default 5_000ms. */\n pollIntervalMs?: number;\n /** API base path. Default `/_agent-native/agent-chat`. */\n apiUrl?: string;\n}\n\nconst DEFAULT_STUCK_THRESHOLD_MS = 90_000;\nexport const DEFAULT_BACKGROUND_STUCK_THRESHOLD_MS = 180_000;\nexport const DEFAULT_LIVE_BACKGROUND_STUCK_THRESHOLD_MS = 13 * 60_000;\nconst DEFAULT_POLL_INTERVAL_MS = 5_000;\nconst IDLE_BACKOFF_INTERVAL_MS = 15_000;\nconst FRESH_BACKGROUND_HEARTBEAT_MS = 30_000;\n\ninterface ActiveRunResponse {\n active: boolean;\n runId?: string;\n status?: string;\n heartbeatAt: number | null;\n lastProgressAt?: number | null;\n dispatchMode?: string | null;\n /** Server clock at response time, used to compute elapsed server-relative. */\n serverNow?: number;\n /** True when the run holds an open tool/A2A call (in_flight_since marker). */\n hasInFlightWork?: boolean;\n}\n\nconst EMPTY_STATE: RunStuckState = {\n isStuck: false,\n runId: null,\n status: null,\n lastProgressAt: null,\n stuckSinceMs: null,\n heartbeatAt: null,\n heartbeatSinceMs: null,\n dispatchMode: null,\n hasInFlightWork: null,\n};\n\nexport function useRunStuckDetection({\n threadId,\n enabled = true,\n stuckThresholdMs = DEFAULT_STUCK_THRESHOLD_MS,\n backgroundStuckThresholdMs = DEFAULT_BACKGROUND_STUCK_THRESHOLD_MS,\n liveBackgroundStuckThresholdMs = DEFAULT_LIVE_BACKGROUND_STUCK_THRESHOLD_MS,\n pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n apiUrl,\n}: UseRunStuckDetectionOptions): RunStuckState {\n const [state, setState] = useState<RunStuckState>(EMPTY_STATE);\n\n useEffect(() => {\n // Reset on every thread change so the previous thread's stuck banner\n // doesn't bleed onto the new one before the first poll completes.\n setState(EMPTY_STATE);\n if (!threadId || !enabled) return;\n\n const base = apiUrl ?? agentNativePath(\"/_agent-native/agent-chat\");\n let cancelled = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const poll = async () => {\n if (cancelled) return;\n let nextDelay = pollIntervalMs;\n try {\n const res = await fetch(\n `${base}/runs/active?threadId=${encodeURIComponent(threadId)}`,\n { credentials: \"same-origin\" },\n );\n if (cancelled) return;\n if (res.ok) {\n const data = (await res.json()) as ActiveRunResponse;\n const lastProgressAt = data.lastProgressAt ?? null;\n // Measure elapsed against the SERVER clock (serverNow) rather than the\n // client's Date.now(). lastProgressAt is a server timestamp, so client\n // clock skew of more than stuckThresholdMs would otherwise mark every\n // run stuck (clock ahead) or never stuck (clock behind). Fall back to\n // the client clock for older bundles that don't send serverNow.\n const nowMs = data.serverNow ?? Date.now();\n const stuckSinceMs =\n lastProgressAt != null ? nowMs - lastProgressAt : null;\n const heartbeatAt = data.heartbeatAt ?? null;\n const heartbeatSinceMs =\n heartbeatAt != null ? nowMs - heartbeatAt : null;\n const dispatchMode =\n typeof data.dispatchMode === \"string\" ? data.dispatchMode : null;\n // Server-continued runs get the wider threshold: the server's own\n // recovery (150s no-progress backstop + chained continuations) must\n // get its chance before the user sees a \"stuck\" affordance.\n const serverContinued =\n dispatchMode === \"foreground-self-chain\" ||\n dispatchMode?.startsWith(\"background\") === true;\n // A claimed durable worker with a fresh heartbeat can legitimately\n // be waiting on a bounded long-running tool/sub-agent call. Showing\n // Retry at the generic 3-minute continuation threshold aborts healthy\n // work and starts the same call again. Let the worker's own 12-minute\n // watchdog act first; a dead/stale heartbeat still gets the earlier\n // background fallback below.\n const liveBackgroundWorker =\n dispatchMode === \"background-processing\" &&\n heartbeatSinceMs != null &&\n heartbeatSinceMs >= 0 &&\n heartbeatSinceMs < FRESH_BACKGROUND_HEARTBEAT_MS;\n const effectiveThresholdMs = liveBackgroundWorker\n ? liveBackgroundStuckThresholdMs\n : serverContinued\n ? backgroundStuckThresholdMs\n : stuckThresholdMs;\n const isStuck = Boolean(\n data.active &&\n data.status === \"running\" &&\n stuckSinceMs != null &&\n stuckSinceMs > effectiveThresholdMs,\n );\n setState({\n isStuck,\n runId: data.runId ?? null,\n status: data.status ?? null,\n lastProgressAt,\n stuckSinceMs,\n heartbeatAt,\n heartbeatSinceMs,\n dispatchMode,\n hasInFlightWork:\n typeof data.hasInFlightWork === \"boolean\"\n ? data.hasInFlightWork\n : null,\n });\n // Back off polling when nothing is in flight — there's no point\n // hammering the endpoint while the chat is idle. We still poll\n // occasionally so a fresh run started in another tab is picked up.\n if (!data.active || data.status !== \"running\") {\n nextDelay = IDLE_BACKOFF_INTERVAL_MS;\n }\n }\n } catch {\n // Network blip — leave previous state. Next tick will retry.\n }\n if (!cancelled) {\n timer = setTimeout(poll, nextDelay);\n }\n };\n\n // Stagger the first poll so a freshly-started run isn't immediately\n // classified as stuck before the server has had a chance to record\n // any progress events.\n timer = setTimeout(poll, 2_000);\n\n return () => {\n cancelled = true;\n if (timer) clearTimeout(timer);\n };\n }, [\n threadId,\n enabled,\n stuckThresholdMs,\n backgroundStuckThresholdMs,\n liveBackgroundStuckThresholdMs,\n pollIntervalMs,\n apiUrl,\n ]);\n\n return state;\n}\n\n/**\n * POST `/runs/:id/abort` so the server flips the run to \"aborted\" and the\n * adapter's reconnect loop exits cleanly. Returns the run id that was\n * aborted (or null on failure) so callers can correlate observability\n * events. Best-effort — failures are swallowed, since the user's intent\n * is already captured locally.\n */\nexport function useAbortRun(apiUrl?: string) {\n return useCallback(\n async (runId: string, reason: string = \"user\"): Promise<string | null> => {\n const base = apiUrl ?? agentNativePath(\"/_agent-native/agent-chat\");\n try {\n const res = await fetch(\n `${base}/runs/${encodeURIComponent(runId)}/abort`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"same-origin\",\n body: JSON.stringify({ reason }),\n },\n );\n if (!res.ok) return null;\n return runId;\n } catch {\n return null;\n }\n },\n [apiUrl],\n );\n}\n"]}
1
+ {"version":3,"file":"use-run-stuck-detection.js","sourceRoot":"","sources":["../../src/client/use-run-stuck-detection.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState, useCallback } from \"react\";\n\nimport { agentNativePath } from \"./api-path.js\";\n\n/**\n * Per-thread chat run health, derived from the durable `last_progress_at`\n * timestamp on the server. Drives the user-visible \"this chat looks stuck\"\n * affordance — distinct from the silent reconnect logic in\n * `agent-chat-adapter.ts`, which keeps trying in the background. When\n * automatic recovery isn't making progress (for whatever reason), this\n * hook surfaces a Retry / Cancel button to the user instead of leaving\n * them staring at a frozen spinner.\n */\nexport interface RunStuckState {\n /** True when an active run hasn't emitted an event for `stuckThresholdMs`. */\n isStuck: boolean;\n /** ID of the active run, or null when nothing is in flight. */\n runId: string | null;\n /** Server-side run status (\"running\" / \"completed\" / \"errored\" / etc.). */\n status: string | null;\n /** Server timestamp (ms) of the last emitted event, or null if none yet. */\n lastProgressAt: number | null;\n /** Milliseconds since `lastProgressAt`, or null. */\n stuckSinceMs: number | null;\n /** Server timestamp (ms) of the last process-alive heartbeat. */\n heartbeatAt: number | null;\n /** Milliseconds since `heartbeatAt`, computed against the server clock. */\n heartbeatSinceMs: number | null;\n /** How the run was dispatched/continued, e.g. foreground-self-chain or background-processing. */\n dispatchMode: string | null;\n /**\n * Server-authoritative: true when the run holds an open tool call or A2A\n * `agent_call` delegation (`in_flight_since` marker set). Preferred over the\n * client-side proxy for deciding whether Retry (which aborts the run) is\n * safe to offer. Null when the server bundle predates this field.\n */\n hasInFlightWork: boolean | null;\n}\n\nexport interface UseRunStuckDetectionOptions {\n /** The thread to monitor. Pass null/undefined to disable polling. */\n threadId: string | null | undefined;\n /**\n * Set false to skip scheduling the poll loop entirely — used to gate\n * polling to only the active chat tab when multiple tabs are mounted\n * (inactive tabs are kept alive via display:none, not unmounted).\n * Defaults to true.\n */\n enabled?: boolean;\n /**\n * Threshold above which an in-flight FOREGROUND run is considered stuck.\n * The default sits comfortably above the adapter's 75s no-progress\n * reconnect — by then automatic recovery has already had its chance.\n */\n stuckThresholdMs?: number;\n /**\n * Threshold for BACKGROUND-dispatched runs (dispatchMode starts with\n * \"background\"). The server owns recovery for these — its run-manager\n * no-progress backstop (150s) and unclaimed-run sweep act first — so the\n * user-facing \"stuck\" affordance is a late fallback, not a race against\n * them. Selected inside the hook because the dispatch mode is only known\n * from the same poll response that computes the elapsed time.\n */\n backgroundStuckThresholdMs?: number;\n /**\n * Legacy upper bound for a claimed durable background worker that is still\n * sending fresh process heartbeats. Informational quiet-run UI is never\n * delayed past `backgroundStuckThresholdMs`; this option can only request an\n * earlier notice for live workers. Default 13 minutes.\n */\n liveBackgroundStuckThresholdMs?: number;\n /** Poll interval. Default 5_000ms. */\n pollIntervalMs?: number;\n /** API base path. Default `/_agent-native/agent-chat`. */\n apiUrl?: string;\n}\n\nconst DEFAULT_STUCK_THRESHOLD_MS = 90_000;\nexport const DEFAULT_BACKGROUND_STUCK_THRESHOLD_MS = 180_000;\nexport const DEFAULT_LIVE_BACKGROUND_STUCK_THRESHOLD_MS = 13 * 60_000;\nconst DEFAULT_POLL_INTERVAL_MS = 5_000;\nconst IDLE_BACKOFF_INTERVAL_MS = 15_000;\nconst FRESH_BACKGROUND_HEARTBEAT_MS = 30_000;\n\ninterface ActiveRunResponse {\n active: boolean;\n runId?: string;\n status?: string;\n heartbeatAt: number | null;\n lastProgressAt?: number | null;\n dispatchMode?: string | null;\n /** Server clock at response time, used to compute elapsed server-relative. */\n serverNow?: number;\n /** True when the run holds an open tool/A2A call (in_flight_since marker). */\n hasInFlightWork?: boolean;\n}\n\nconst EMPTY_STATE: RunStuckState = {\n isStuck: false,\n runId: null,\n status: null,\n lastProgressAt: null,\n stuckSinceMs: null,\n heartbeatAt: null,\n heartbeatSinceMs: null,\n dispatchMode: null,\n hasInFlightWork: null,\n};\n\nexport function useRunStuckDetection({\n threadId,\n enabled = true,\n stuckThresholdMs = DEFAULT_STUCK_THRESHOLD_MS,\n backgroundStuckThresholdMs = DEFAULT_BACKGROUND_STUCK_THRESHOLD_MS,\n liveBackgroundStuckThresholdMs = DEFAULT_LIVE_BACKGROUND_STUCK_THRESHOLD_MS,\n pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n apiUrl,\n}: UseRunStuckDetectionOptions): RunStuckState {\n const [state, setState] = useState<RunStuckState>(EMPTY_STATE);\n\n useEffect(() => {\n // Reset on every thread change so the previous thread's stuck banner\n // doesn't bleed onto the new one before the first poll completes.\n setState(EMPTY_STATE);\n if (!threadId || !enabled) return;\n\n const base = apiUrl ?? agentNativePath(\"/_agent-native/agent-chat\");\n let cancelled = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let snapshotTransitionTimer: ReturnType<typeof setTimeout> | null = null;\n let snapshotVersion = 0;\n\n type RunHealthSnapshot = {\n active: boolean;\n runId: string | null;\n status: string | null;\n lastProgressAt: number | null;\n stuckSinceMs: number | null;\n heartbeatAt: number | null;\n heartbeatSinceMs: number | null;\n dispatchMode: string | null;\n };\n\n const effectiveThresholdFor = (\n dispatchMode: string | null,\n heartbeatSinceMs: number | null,\n ) => {\n const liveBackgroundWorker =\n dispatchMode === \"background-processing\" &&\n heartbeatSinceMs != null &&\n heartbeatSinceMs >= 0 &&\n heartbeatSinceMs < FRESH_BACKGROUND_HEARTBEAT_MS;\n const serverContinued =\n dispatchMode === \"foreground-self-chain\" ||\n dispatchMode?.startsWith(\"background\") === true;\n return liveBackgroundWorker\n ? Math.min(backgroundStuckThresholdMs, liveBackgroundStuckThresholdMs)\n : serverContinued\n ? backgroundStuckThresholdMs\n : stuckThresholdMs;\n };\n\n const scheduleSnapshotTransition = (\n snapshot: RunHealthSnapshot,\n observedAtMs: number,\n version: number,\n ) => {\n if (snapshotTransitionTimer) clearTimeout(snapshotTransitionTimer);\n snapshotTransitionTimer = null;\n if (\n cancelled ||\n version !== snapshotVersion ||\n !snapshot.active ||\n snapshot.status !== \"running\" ||\n snapshot.runId == null\n ) {\n return;\n }\n\n const currentElapsedMs = Math.max(0, Date.now() - observedAtMs);\n const currentHeartbeatSinceMs =\n snapshot.heartbeatSinceMs == null\n ? null\n : snapshot.heartbeatSinceMs + currentElapsedMs;\n const currentStuckSinceMs =\n snapshot.stuckSinceMs == null\n ? null\n : snapshot.stuckSinceMs + currentElapsedMs;\n const effectiveThresholdMs = effectiveThresholdFor(\n snapshot.dispatchMode,\n currentHeartbeatSinceMs,\n );\n const currentlyStuck = Boolean(\n currentStuckSinceMs != null &&\n currentStuckSinceMs > effectiveThresholdMs,\n );\n const transitionDelaysMs: number[] = [];\n if (\n currentHeartbeatSinceMs != null &&\n currentHeartbeatSinceMs >= 0 &&\n currentHeartbeatSinceMs < FRESH_BACKGROUND_HEARTBEAT_MS\n ) {\n transitionDelaysMs.push(\n FRESH_BACKGROUND_HEARTBEAT_MS - currentHeartbeatSinceMs + 1,\n );\n }\n if (currentStuckSinceMs != null && !currentlyStuck) {\n transitionDelaysMs.push(\n Math.max(1, effectiveThresholdMs - currentStuckSinceMs + 1),\n );\n }\n if (transitionDelaysMs.length === 0) return;\n\n // Both ages originate from the server clock. Advance those snapshots by\n // only a local elapsed duration, which remains safe when client/server\n // wall clocks differ. Wake at the earlier semantic boundary, then\n // reschedule if the other boundary is still ahead.\n const delayMs = Math.max(1, Math.min(...transitionDelaysMs));\n snapshotTransitionTimer = setTimeout(() => {\n snapshotTransitionTimer = null;\n if (cancelled || version !== snapshotVersion) return;\n const elapsedSinceObservationMs = Math.max(\n 0,\n Date.now() - observedAtMs,\n );\n const nextHeartbeatSinceMs =\n snapshot.heartbeatSinceMs == null\n ? null\n : snapshot.heartbeatSinceMs + elapsedSinceObservationMs;\n const nextStuckSinceMs =\n snapshot.stuckSinceMs == null\n ? null\n : snapshot.stuckSinceMs + elapsedSinceObservationMs;\n const nextEffectiveThresholdMs = effectiveThresholdFor(\n snapshot.dispatchMode,\n nextHeartbeatSinceMs,\n );\n const nextIsStuck = Boolean(\n nextStuckSinceMs != null &&\n nextStuckSinceMs > nextEffectiveThresholdMs,\n );\n setState((current) => {\n if (\n version !== snapshotVersion ||\n current.runId !== snapshot.runId ||\n current.lastProgressAt !== snapshot.lastProgressAt ||\n current.heartbeatAt !== snapshot.heartbeatAt\n ) {\n return current;\n }\n return {\n ...current,\n isStuck: nextIsStuck,\n stuckSinceMs: nextStuckSinceMs,\n heartbeatSinceMs: nextHeartbeatSinceMs,\n };\n });\n scheduleSnapshotTransition(snapshot, observedAtMs, version);\n }, delayMs);\n };\n\n const poll = async () => {\n if (cancelled) return;\n let nextDelay = pollIntervalMs;\n try {\n const res = await fetch(\n `${base}/runs/active?threadId=${encodeURIComponent(threadId)}`,\n { credentials: \"same-origin\" },\n );\n if (cancelled) return;\n if (res.ok) {\n const data = (await res.json()) as ActiveRunResponse;\n const lastProgressAt = data.lastProgressAt ?? null;\n // Measure elapsed against the SERVER clock (serverNow) rather than the\n // client's Date.now(). lastProgressAt is a server timestamp, so client\n // clock skew of more than stuckThresholdMs would otherwise mark every\n // run stuck (clock ahead) or never stuck (clock behind). Fall back to\n // the client clock for older bundles that don't send serverNow.\n const nowMs = data.serverNow ?? Date.now();\n const stuckSinceMs =\n lastProgressAt != null ? nowMs - lastProgressAt : null;\n const heartbeatAt = data.heartbeatAt ?? null;\n const heartbeatSinceMs =\n heartbeatAt != null ? nowMs - heartbeatAt : null;\n const dispatchMode =\n typeof data.dispatchMode === \"string\" ? data.dispatchMode : null;\n // A claimed durable worker with a fresh heartbeat can legitimately\n // be waiting on a bounded long-running tool/sub-agent call. Still\n // surface informational status at the normal background threshold;\n // RunStuckBanner uses the fresh heartbeat to withhold Retry while\n // keeping an explicit Cancel available. The legacy live-worker\n // threshold may make that notice earlier, but never later.\n const effectiveThresholdMs = effectiveThresholdFor(\n dispatchMode,\n heartbeatSinceMs,\n );\n const isStuck = Boolean(\n data.active &&\n data.status === \"running\" &&\n stuckSinceMs != null &&\n stuckSinceMs > effectiveThresholdMs,\n );\n const observedAtMs = Date.now();\n const version = ++snapshotVersion;\n scheduleSnapshotTransition(\n {\n active: data.active,\n runId: data.runId ?? null,\n status: data.status ?? null,\n lastProgressAt,\n stuckSinceMs,\n heartbeatAt,\n heartbeatSinceMs,\n dispatchMode,\n },\n observedAtMs,\n version,\n );\n setState({\n isStuck,\n runId: data.runId ?? null,\n status: data.status ?? null,\n lastProgressAt,\n stuckSinceMs,\n heartbeatAt,\n heartbeatSinceMs,\n dispatchMode,\n hasInFlightWork:\n typeof data.hasInFlightWork === \"boolean\"\n ? data.hasInFlightWork\n : null,\n });\n // Back off polling when nothing is in flight — there's no point\n // hammering the endpoint while the chat is idle. We still poll\n // occasionally so a fresh run started in another tab is picked up.\n if (!data.active || data.status !== \"running\") {\n nextDelay = IDLE_BACKOFF_INTERVAL_MS;\n }\n }\n } catch {\n // Network blip — leave previous state. Next tick will retry.\n }\n if (!cancelled) {\n timer = setTimeout(poll, nextDelay);\n }\n };\n\n // Stagger the first poll so a freshly-started run isn't immediately\n // classified as stuck before the server has had a chance to record\n // any progress events.\n timer = setTimeout(poll, 2_000);\n\n return () => {\n cancelled = true;\n snapshotVersion += 1;\n if (timer) clearTimeout(timer);\n if (snapshotTransitionTimer) clearTimeout(snapshotTransitionTimer);\n };\n }, [\n threadId,\n enabled,\n stuckThresholdMs,\n backgroundStuckThresholdMs,\n liveBackgroundStuckThresholdMs,\n pollIntervalMs,\n apiUrl,\n ]);\n\n return state;\n}\n\n/**\n * POST `/runs/:id/abort` so the server flips the run to \"aborted\" and the\n * adapter's reconnect loop exits cleanly. Returns the run id that was\n * aborted (or null on failure) so callers can correlate observability\n * events. Best-effort — failures are swallowed, since the user's intent\n * is already captured locally.\n */\nexport function useAbortRun(apiUrl?: string) {\n return useCallback(\n async (runId: string, reason: string = \"user\"): Promise<string | null> => {\n const base = apiUrl ?? agentNativePath(\"/_agent-native/agent-chat\");\n try {\n const res = await fetch(\n `${base}/runs/${encodeURIComponent(runId)}/abort`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"same-origin\",\n body: JSON.stringify({ reason }),\n },\n );\n if (!res.ok) return null;\n return runId;\n } catch {\n return null;\n }\n },\n [apiUrl],\n );\n}\n"]}
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
62
62
  error: string;
63
63
  states?: undefined;
64
64
  } | {
65
- error?: undefined;
66
65
  states: {
67
66
  clientId: number;
68
67
  state: string;
69
68
  }[];
69
+ error?: undefined;
70
70
  }>>;
71
71
  /**
72
72
  * GET /_agent-native/collab/:docId/users
@@ -77,10 +77,10 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
77
77
  error: string;
78
78
  users?: undefined;
79
79
  } | {
80
- error?: undefined;
81
80
  users: {
82
81
  clientId: number;
83
82
  lastSeen: number;
84
83
  }[];
84
+ error?: undefined;
85
85
  }>>;
86
86
  //# sourceMappingURL=awareness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"awareness.d.ts","sourceRoot":"","sources":["../../src/collab/awareness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgB3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAOD,wBAAgB,mBAAmB,IAAI,YAAY,CAElD;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,cAAc,GAAG,SAAS,GAChC,IAAI,CAON;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,KAAK,CAAC,EAAE,cAAc,GACrB,IAAI,CAgBN;AAoBD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAmB,GAC7B,IAAI,CAON;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1E;AAiBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAO1E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,CAOnE;AAkCD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;kBAwDa,MAAM;eAAS,MAAM;;GAoB1D,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;kBAYM,MAAM;kBAAY,MAAM;;GAMvD,CAAC"}
1
+ {"version":3,"file":"awareness.d.ts","sourceRoot":"","sources":["../../src/collab/awareness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgB3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAOD,wBAAgB,mBAAmB,IAAI,YAAY,CAElD;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,cAAc,GAAG,SAAS,GAChC,IAAI,CAON;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,KAAK,CAAC,EAAE,cAAc,GACrB,IAAI,CAgBN;AAoBD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAmB,GAC7B,IAAI,CAON;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1E;AAiBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAO1E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,CAOnE;AAkCD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;kBAwDa,MAAM;eAAS,MAAM;;;GAoB1D,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;kBAYM,MAAM;kBAAY,MAAM;;;GAMvD,CAAC"}
@@ -151,12 +151,13 @@ export declare function isDurableBackgroundDeployEnabled(): boolean;
151
151
  */
152
152
  export declare function emitSingleTemplateNetlifyBackgroundFunction(projectCwd: string): void;
153
153
  /**
154
- * Nitro can preserve Vite's `yjs` external in a split server chunk even when
155
- * its own server build has emitted `_libs/yjs.mjs`. Netlify does not install
156
- * that bare package at runtime, so make every emitted server chunk use the
157
- * bundled copy before its function is packaged.
154
+ * Nitro receives the React Router SSR build as prebuilt chunks, so its normal
155
+ * dependency resolver cannot reliably fold the preserved bare `yjs` imports
156
+ * into the same module instance used by core's server collaboration code.
157
+ * Keep Yjs external through Nitro, bundle its complete public ESM surface once,
158
+ * then point every emitted server chunk at that one portable runtime module.
158
159
  */
159
- export declare function rewriteBareYjsImportsForServerlessOutput(serverDir: string): string[];
160
+ export declare function bundleYjsRuntimeForServerlessOutput(serverDir: string, projectCwd: string): string[];
160
161
  export declare function assertSingleTemplateNetlifyBuildOutput(projectCwd: string): void;
161
162
  /**
162
163
  * Strip the harmful single-template catch-all rewrite that points at
@@ -204,17 +205,17 @@ export interface NitroBuildPipelineOptions {
204
205
  */
205
206
  export declare function runNitroBuildPipeline(opts: NitroBuildPipelineOptions): Promise<void>;
206
207
  /**
207
- * Dependencies that must be bundled into every Nitro server output instead of
208
- * being left as runtime package imports.
209
- *
210
- * `yjs` is a direct core dependency, but it is deliberately externalized from
211
- * the intermediate Vite SSR graph so that Vite and Nitro do not create two
212
- * incompatible Yjs constructors. On file-traced serverless presets, leaving it
213
- * external at Nitro's final build can emit `import "yjs"` into a function
214
- * chunk without placing the package in that function's `node_modules`. Bundle
215
- * it in Nitro's final output so every template receives the one portable copy.
208
+ * Dependencies Nitro itself must bundle outside the controlled serverless
209
+ * output pass. Netlify, Vercel, and Lambda keep Yjs external through Nitro;
210
+ * `bundleYjsRuntimeForServerlessOutput` then creates their one portable copy.
216
211
  */
217
212
  export declare const NITRO_SERVER_RUNTIME_BUNDLED_DEPS: readonly ["yjs"];
213
+ /**
214
+ * Locate the core-owned ESM entry used by the controlled serverless bundling
215
+ * pass. Resolving from this module keeps the build independent of whether a
216
+ * template exposes core's transitive Yjs dependency at its own package root.
217
+ */
218
+ export declare function resolveNitroBundledYjsEntry(): string;
218
219
  /**
219
220
  * Edge runtimes have no node_modules, while Node/serverless outputs only need
220
221
  * the small set above bundled to keep their package manifests traceable.
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/deploy/build.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;GAYG;AAuCH,OAAO,EAML,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAI7B,eAAO,MAAM,6BAA6B,UAiBzC,CAAC;AAEF,eAAO,MAAM,mCAAmC,UAiB/C,CAAC;AACF,eAAO,MAAM,8BAA8B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiFjE,CAAC;AA6BF,eAAO,MAAM,2CAA2C,EAAE,MAAM,CAC9D,MAAM,EACN,MAAM,CAmSP,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,UAAU,wBAAwB;IAChC,KAAK,EAAE,6BAA6B,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,6BAA6B,CAAC,CAAC;IACtD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,UAAU,6BAA6B;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,UAAU,6BAA6B;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAwBD,wBAAgB,wCAAwC,CACtD,WAAW,EAAE,MAAM,EAAE,GACpB,MAAM,CAaR;AAgBD,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,CAAC;AAgBvE,wBAAgB,yCAAyC,CACvD,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,MAAM,EACjB,WAAW,SAAK,GACf,IAAI,CAQN;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,eAAe,EAAE,EACzB,WAAW,EAAE,MAAM,EAAE,EACrB,kBAAkB,GAAE,MAAM,EAAO,EACjC,OAAO,GAAE,gBAAgB,EAAO,EAChC,aAAa,GAAE,oBAAoB,GAAG,IAAW,EACjD,mBAAmB,GAAE,MAAM,EAAO,EAClC,gBAAgB,SAAmC,EACnD,OAAO,GAAE,0BAA+B,GACvC,MAAM,CA4pBR;AA4FD,wBAAgB,8CAA8C,CAC5D,QAAQ,EAAE,wBAAwB,EAClC,QAAQ,SAAmC,GAC1C,MAAM,CAiCR;AA6dD,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AA+GD,wBAAgB,OAAO,CACrB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,iBAAiB,cAAoB,QAoCtC;AAiCD,KAAK,0BAA0B,GAAG,OAAO,GAAG,KAAK,CAAC;AAQlD,wBAAgB,qCAAqC,CACnD,YAAY,GAAE,MAAM,CAAC,QAA2B,EAChD,QAAQ,GAAE,MAAM,CAAC,YAA2B,EAC5C,UAAU,GAAE,0BAA0B,GAAG,IAAgD,GACxF,OAAO,CAOT;AAqED,wBAAgB,gCAAgC,CAC9C,gBAAgB,EAAE,MAAM,EAAE,GACzB,MAAM,GAAG,IAAI,CA8Bf;AAED,wBAAgB,0BAA0B,CACxC,gBAAgB,EAAE,MAAM,EAAE,GACzB,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAqCpD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gCAAgC,IAAI,OAAO,CAK1D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,wBAAgB,2CAA2C,CACzD,UAAU,EAAE,MAAM,GACjB,IAAI,CAkJN;AAuCD;;;;;GAKG;AACH,wBAAgB,wCAAwC,CACtD,SAAS,EAAE,MAAM,GAChB,MAAM,EAAE,CA+CV;AAED,wBAAgB,sCAAsC,CACpD,UAAU,EAAE,MAAM,GACjB,IAAI,CA6JN;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAsC5E;AAuED;;;;;;GAMG;AACH,wBAAgB,yCAAyC,CACvD,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,IAAI,CAqDN;AA6ID;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,gBAAgB,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,UAAU,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,GAAG,CAAC;IACX,KAAK,EAAE,eAAe,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAkBD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iCAAiC,YAAI,KAAK,CAAU,CAAC;AAElE;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,GACnB,IAAI,GAAG,SAAS,MAAM,EAAE,CAK1B"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/deploy/build.ts"],"names":[],"mappings":"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"}
@@ -2101,15 +2101,15 @@ function walkServerJavaScriptFiles(dir, onFile) {
2101
2101
  }
2102
2102
  }
2103
2103
  /**
2104
- * Nitro can preserve Vite's `yjs` external in a split server chunk even when
2105
- * its own server build has emitted `_libs/yjs.mjs`. Netlify does not install
2106
- * that bare package at runtime, so make every emitted server chunk use the
2107
- * bundled copy before its function is packaged.
2104
+ * Nitro receives the React Router SSR build as prebuilt chunks, so its normal
2105
+ * dependency resolver cannot reliably fold the preserved bare `yjs` imports
2106
+ * into the same module instance used by core's server collaboration code.
2107
+ * Keep Yjs external through Nitro, bundle its complete public ESM surface once,
2108
+ * then point every emitted server chunk at that one portable runtime module.
2108
2109
  */
2109
- export function rewriteBareYjsImportsForServerlessOutput(serverDir) {
2110
+ export function bundleYjsRuntimeForServerlessOutput(serverDir, projectCwd) {
2110
2111
  const bareImports = [];
2111
2112
  const unsupportedSubpathImports = [];
2112
- const bundledYjsPath = path.join(serverDir, "_libs", "yjs.mjs");
2113
2113
  walkServerJavaScriptFiles(serverDir, (filePath) => {
2114
2114
  const source = fs.readFileSync(filePath, "utf-8");
2115
2115
  if (!hasBareYjsRuntimeImport(source))
@@ -2125,9 +2125,17 @@ export function rewriteBareYjsImportsForServerlessOutput(serverDir) {
2125
2125
  }
2126
2126
  if (bareImports.length === 0)
2127
2127
  return [];
2128
- if (!fs.existsSync(bundledYjsPath)) {
2129
- throw new Error(`[deploy] Serverless output left yjs as a runtime import but did not emit ${bundledYjsPath}`);
2130
- }
2128
+ const bundledYjsPath = path.join(serverDir, "_libs", "yjs-runtime.mjs");
2129
+ fs.mkdirSync(path.dirname(bundledYjsPath), { recursive: true });
2130
+ execFileSync(findEsbuild(), [
2131
+ resolveNitroBundledYjsEntry(),
2132
+ "--bundle",
2133
+ "--format=esm",
2134
+ "--platform=node",
2135
+ "--target=node22",
2136
+ "--minify",
2137
+ `--outfile=${bundledYjsPath}`,
2138
+ ], { cwd: projectCwd, stdio: "pipe" });
2131
2139
  for (const filePath of bareImports) {
2132
2140
  const bundledImport = path
2133
2141
  .relative(path.dirname(filePath), bundledYjsPath)
@@ -2217,6 +2225,19 @@ export function assertSingleTemplateNetlifyBuildOutput(projectCwd) {
2217
2225
  if (bareYjsImports.length > 0) {
2218
2226
  failures.push(`Netlify server bundle leaves yjs as a runtime import: ${bareYjsImports.join(", ")}`);
2219
2227
  }
2228
+ // Nitro's `_libs/yjs.mjs` is a private tree-shaken chunk, not a package
2229
+ // facade. Repointing a prebuilt SSR chunk at it can request public exports
2230
+ // (notably `Text`) that the private chunk did not retain. The controlled
2231
+ // serverless pass must instead target the complete `yjs-runtime.mjs` bundle.
2232
+ const privateYjsImports = [];
2233
+ walkServerJavaScriptFiles(serverDir, (filePath) => {
2234
+ if (/\b(?:from\s*|import\s*\(\s*|import\s*)(["'])[^"']*_libs\/yjs\.mjs\1/.test(fs.readFileSync(filePath, "utf-8"))) {
2235
+ privateYjsImports.push(path.relative(projectCwd, filePath));
2236
+ }
2237
+ });
2238
+ if (privateYjsImports.length > 0) {
2239
+ failures.push(`Netlify server bundle imports Nitro's internal tree-shaken _libs/yjs.mjs: ${privateYjsImports.join(", ")}`);
2240
+ }
2220
2241
  if (isDurableBackgroundDeployEnabled()) {
2221
2242
  const backgroundDir = path.join(internalDir, AGENT_BACKGROUND_FUNCTION_NAME);
2222
2243
  const backgroundEntryPath = path.join(backgroundDir, `${AGENT_BACKGROUND_FUNCTION_NAME}.mjs`);
@@ -2567,17 +2588,25 @@ const BROWSER_ONLY_SERVER_LIBS = [
2567
2588
  "mermaid",
2568
2589
  ];
2569
2590
  /**
2570
- * Dependencies that must be bundled into every Nitro server output instead of
2571
- * being left as runtime package imports.
2572
- *
2573
- * `yjs` is a direct core dependency, but it is deliberately externalized from
2574
- * the intermediate Vite SSR graph so that Vite and Nitro do not create two
2575
- * incompatible Yjs constructors. On file-traced serverless presets, leaving it
2576
- * external at Nitro's final build can emit `import "yjs"` into a function
2577
- * chunk without placing the package in that function's `node_modules`. Bundle
2578
- * it in Nitro's final output so every template receives the one portable copy.
2591
+ * Dependencies Nitro itself must bundle outside the controlled serverless
2592
+ * output pass. Netlify, Vercel, and Lambda keep Yjs external through Nitro;
2593
+ * `bundleYjsRuntimeForServerlessOutput` then creates their one portable copy.
2579
2594
  */
2580
2595
  export const NITRO_SERVER_RUNTIME_BUNDLED_DEPS = ["yjs"];
2596
+ /**
2597
+ * Locate the core-owned ESM entry used by the controlled serverless bundling
2598
+ * pass. Resolving from this module keeps the build independent of whether a
2599
+ * template exposes core's transitive Yjs dependency at its own package root.
2600
+ */
2601
+ export function resolveNitroBundledYjsEntry() {
2602
+ const requireFromCore = createRequire(import.meta.url);
2603
+ const packageDir = path.dirname(requireFromCore.resolve("yjs/package.json"));
2604
+ const entry = path.join(packageDir, "dist", "yjs.mjs");
2605
+ if (!fs.existsSync(entry)) {
2606
+ throw new Error(`[build] Could not resolve the Yjs ESM entry at ${entry}`);
2607
+ }
2608
+ return entry;
2609
+ }
2581
2610
  /**
2582
2611
  * Edge runtimes have no node_modules, while Node/serverless outputs only need
2583
2612
  * the small set above bundled to keep their package manifests traceable.
@@ -2586,7 +2615,11 @@ export function nitroNoExternalsForPreset(targetPreset) {
2586
2615
  return targetPreset.startsWith("cloudflare") ||
2587
2616
  targetPreset.startsWith("deno")
2588
2617
  ? true
2589
- : NITRO_SERVER_RUNTIME_BUNDLED_DEPS;
2618
+ : targetPreset === "netlify" ||
2619
+ targetPreset === "vercel" ||
2620
+ targetPreset === "aws-lambda"
2621
+ ? []
2622
+ : NITRO_SERVER_RUNTIME_BUNDLED_DEPS;
2590
2623
  }
2591
2624
  /**
2592
2625
  * Rolldown plugin for the Nitro server bundle that replaces the browser-only
@@ -2722,6 +2755,14 @@ export default bundle;
2722
2755
  // (ReferenceError: window is not defined → every request 502s). Mirrors the
2723
2756
  // Vite `ssrStubPlugin`, which only covers the `build/server` step.
2724
2757
  rollupConfig: {
2758
+ // Nitro treats the intermediate React Router SSR files as prebuilt
2759
+ // chunks, while core's server collaboration files participate in the
2760
+ // final Rolldown graph. Externalize Yjs consistently on serverless so
2761
+ // both graphs retain their public import shapes; the controlled
2762
+ // post-build pass below bundles and rewrites them to one module.
2763
+ ...(preset === "netlify" || preset === "vercel" || preset === "aws-lambda"
2764
+ ? { external: ["yjs"] }
2765
+ : {}),
2725
2766
  plugins: [createBrowserOnlyServerStubPlugin()],
2726
2767
  },
2727
2768
  ...(providedPluginsNitroPlugin
@@ -2729,9 +2770,9 @@ export default bundle;
2729
2770
  : {}),
2730
2771
  routeRules: mcpEmbedStaticAssetRouteRules(appBasePath),
2731
2772
  // Edge presets (cloudflare, deno) bundle all deps because node_modules are
2732
- // unavailable at runtime. Node/serverless presets also bundle Yjs: Nitro's
2733
- // file tracer otherwise leaves a bare import that Netlify function bundles
2734
- // cannot resolve under pnpm.
2773
+ // unavailable at runtime. Ordinary Node presets bundle Yjs through Nitro.
2774
+ // Controlled serverless presets externalize it above, then emit one full
2775
+ // runtime module after Nitro has preserved every consumer's public imports.
2735
2776
  noExternals: nitroNoExternalsForPreset(preset),
2736
2777
  });
2737
2778
  await runNitroBuildPipeline({
@@ -2747,7 +2788,7 @@ export default bundle;
2747
2788
  copyInstalledResvgPackages(nitro.options.output.serverDir);
2748
2789
  copyInstalledFfmpegStaticPackage(nitro.options.output.serverDir);
2749
2790
  sanitizeServerlessFunctionPackageManifest(nitro.options.output.serverDir);
2750
- rewriteBareYjsImportsForServerlessOutput(nitro.options.output.serverDir);
2791
+ bundleYjsRuntimeForServerlessOutput(nitro.options.output.serverDir, cwd);
2751
2792
  }
2752
2793
  // Durable background agent runs (default-OFF / opt-in; enable with a truthy
2753
2794
  // AGENT_CHAT_DURABLE_BACKGROUND). Additive ONLY: emits a SECOND Netlify