@agent-native/core 0.84.67 → 0.85.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 (149) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +20 -0
  3. package/corpus/core/docs/content/locales/ar-SA/tracking.mdx +55 -0
  4. package/corpus/core/docs/content/locales/de-DE/tracking.mdx +55 -0
  5. package/corpus/core/docs/content/locales/es-ES/tracking.mdx +55 -0
  6. package/corpus/core/docs/content/locales/fr-FR/tracking.mdx +55 -0
  7. package/corpus/core/docs/content/locales/hi-IN/tracking.mdx +55 -0
  8. package/corpus/core/docs/content/locales/ja-JP/tracking.mdx +55 -0
  9. package/corpus/core/docs/content/locales/ko-KR/tracking.mdx +55 -0
  10. package/corpus/core/docs/content/locales/pt-BR/tracking.mdx +55 -0
  11. package/corpus/core/docs/content/locales/zh-CN/tracking.mdx +55 -0
  12. package/corpus/core/docs/content/locales/zh-TW/tracking.mdx +55 -0
  13. package/corpus/core/docs/content/tracking.mdx +2 -0
  14. package/corpus/core/package.json +1 -1
  15. package/corpus/core/src/agent/action-continuation-guidance.ts +1 -1
  16. package/corpus/core/src/agent/production-agent.ts +222 -38
  17. package/corpus/core/src/agent/run-manager.ts +79 -0
  18. package/corpus/core/src/agent/run-store.ts +122 -8
  19. package/corpus/core/src/agent/types.ts +13 -0
  20. package/corpus/core/src/client/RunStuckBanner.tsx +14 -0
  21. package/corpus/core/src/client/agent-chat-adapter.ts +475 -0
  22. package/corpus/core/src/client/analytics.ts +2 -0
  23. package/corpus/core/src/client/error-format.ts +4 -0
  24. package/corpus/core/src/client/extensions/ExtensionsSidebarSection.tsx +1 -1
  25. package/corpus/core/src/client/session-replay.ts +774 -2
  26. package/corpus/core/src/client/sse-event-processor.ts +65 -9
  27. package/corpus/core/src/client/use-run-stuck-detection.ts +31 -7
  28. package/corpus/core/src/db/runtime-diagnostics.ts +331 -0
  29. package/corpus/core/src/server/agent-chat-plugin.ts +115 -2
  30. package/corpus/core/src/server/core-routes-plugin.ts +91 -4
  31. package/corpus/core/src/server/http-response-telemetry.ts +189 -0
  32. package/corpus/core/src/server/self-dispatch.ts +29 -0
  33. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +53 -1
  34. package/corpus/templates/analytics/AGENTS.md +13 -0
  35. package/corpus/templates/analytics/actions/view-screen.ts +25 -0
  36. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +20 -0
  37. package/corpus/templates/analytics/app/global.css +55 -0
  38. package/corpus/templates/analytics/app/i18n/zh-TW.ts +100 -1
  39. package/corpus/templates/analytics/app/i18n-data.ts +1226 -0
  40. package/corpus/templates/analytics/app/pages/Settings.tsx +3 -0
  41. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +182 -144
  42. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +11 -18
  43. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +11 -18
  44. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +49 -1
  45. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +557 -0
  46. package/corpus/templates/analytics/app/pages/sessions/session-replay-devtools.ts +274 -0
  47. package/corpus/templates/analytics/app/pages/settings/AlertRulesSettingsCard.tsx +974 -0
  48. package/corpus/templates/analytics/changelog/2026-07-02-alert-rules-can-now-be-viewed-and-managed-from-settings.md +6 -0
  49. package/corpus/templates/analytics/changelog/2026-07-02-recent-ask-chats-show-a-loading-placeholder-while-your-chat-.md +6 -0
  50. package/corpus/templates/analytics/changelog/2026-07-02-session-replays-now-capture-console-logs-and-network-request.md +6 -0
  51. package/corpus/templates/analytics/changelog/2026-07-02-sharing-status-labels-use-neutral-icons-instead-of-colored-d.md +6 -0
  52. package/corpus/templates/analytics/changelog/2026-07-02-the-session-replay-viewer-has-a-dev-tools-panel-with-console.md +6 -0
  53. package/corpus/templates/analytics/server/db/schema.ts +3 -0
  54. package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +4 -0
  55. package/corpus/templates/analytics/server/lib/analytics-alerts.ts +142 -1
  56. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +369 -8
  57. package/corpus/templates/analytics/server/lib/session-replay.ts +76 -2
  58. package/corpus/templates/analytics/server/routes/api/session-replay/agent-diagnostics.json.get.ts +101 -0
  59. package/corpus/templates/analytics/shared/session-replay-diagnostics.ts +78 -0
  60. package/corpus/templates/design/app/pages/DesignEditor.tsx +24 -12
  61. package/corpus/templates/design/changelog/2026-07-02-the-temporary-code-tab-is-hidden-from-the-design-editor-side.md +6 -0
  62. package/corpus/templates/plan/actions/create-visual-recap.ts +1 -1
  63. package/corpus/templates/plan/actions/update-local-plan-folder.ts +3 -1
  64. package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
  65. package/corpus/templates/plan/changelog/2026-07-02-trying-to-edit-a-recap-or-plan-you-can-only-view-now-explain.md +6 -0
  66. package/corpus/templates/plan/server/plans.ts +26 -9
  67. package/dist/agent/action-continuation-guidance.js +1 -1
  68. package/dist/agent/action-continuation-guidance.js.map +1 -1
  69. package/dist/agent/production-agent.d.ts +9 -1
  70. package/dist/agent/production-agent.d.ts.map +1 -1
  71. package/dist/agent/production-agent.js +168 -34
  72. package/dist/agent/production-agent.js.map +1 -1
  73. package/dist/agent/run-manager.d.ts +29 -0
  74. package/dist/agent/run-manager.d.ts.map +1 -1
  75. package/dist/agent/run-manager.js +73 -0
  76. package/dist/agent/run-manager.js.map +1 -1
  77. package/dist/agent/run-store.d.ts +41 -0
  78. package/dist/agent/run-store.d.ts.map +1 -1
  79. package/dist/agent/run-store.js +105 -7
  80. package/dist/agent/run-store.js.map +1 -1
  81. package/dist/agent/types.d.ts +13 -0
  82. package/dist/agent/types.d.ts.map +1 -1
  83. package/dist/agent/types.js.map +1 -1
  84. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  85. package/dist/client/RunStuckBanner.js +13 -0
  86. package/dist/client/RunStuckBanner.js.map +1 -1
  87. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  88. package/dist/client/agent-chat-adapter.js +410 -0
  89. package/dist/client/agent-chat-adapter.js.map +1 -1
  90. package/dist/client/analytics.d.ts +1 -1
  91. package/dist/client/analytics.d.ts.map +1 -1
  92. package/dist/client/analytics.js.map +1 -1
  93. package/dist/client/error-format.d.ts.map +1 -1
  94. package/dist/client/error-format.js +2 -0
  95. package/dist/client/error-format.js.map +1 -1
  96. package/dist/client/extensions/ExtensionsSidebarSection.js +1 -1
  97. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
  98. package/dist/client/session-replay.d.ts +36 -0
  99. package/dist/client/session-replay.d.ts.map +1 -1
  100. package/dist/client/session-replay.js +579 -1
  101. package/dist/client/session-replay.js.map +1 -1
  102. package/dist/client/sse-event-processor.d.ts +20 -1
  103. package/dist/client/sse-event-processor.d.ts.map +1 -1
  104. package/dist/client/sse-event-processor.js +42 -11
  105. package/dist/client/sse-event-processor.js.map +1 -1
  106. package/dist/client/use-run-stuck-detection.d.ts +14 -4
  107. package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
  108. package/dist/client/use-run-stuck-detection.js +18 -4
  109. package/dist/client/use-run-stuck-detection.js.map +1 -1
  110. package/dist/collab/awareness.d.ts +2 -2
  111. package/dist/collab/awareness.d.ts.map +1 -1
  112. package/dist/collab/routes.d.ts +2 -2
  113. package/dist/db/runtime-diagnostics.d.ts +52 -0
  114. package/dist/db/runtime-diagnostics.d.ts.map +1 -0
  115. package/dist/db/runtime-diagnostics.js +250 -0
  116. package/dist/db/runtime-diagnostics.js.map +1 -0
  117. package/dist/notifications/routes.d.ts +1 -1
  118. package/dist/observability/routes.d.ts +3 -3
  119. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  120. package/dist/resources/handlers.d.ts +3 -3
  121. package/dist/secrets/routes.d.ts +3 -3
  122. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  123. package/dist/server/agent-chat-plugin.js +101 -2
  124. package/dist/server/agent-chat-plugin.js.map +1 -1
  125. package/dist/server/core-routes-plugin.d.ts +17 -0
  126. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  127. package/dist/server/core-routes-plugin.js +65 -5
  128. package/dist/server/core-routes-plugin.js.map +1 -1
  129. package/dist/server/http-response-telemetry.d.ts +4 -0
  130. package/dist/server/http-response-telemetry.d.ts.map +1 -0
  131. package/dist/server/http-response-telemetry.js +174 -0
  132. package/dist/server/http-response-telemetry.js.map +1 -0
  133. package/dist/server/self-dispatch.d.ts +17 -0
  134. package/dist/server/self-dispatch.d.ts.map +1 -1
  135. package/dist/server/self-dispatch.js +11 -0
  136. package/dist/server/self-dispatch.js.map +1 -1
  137. package/dist/server/transcribe-voice.d.ts +1 -1
  138. package/docs/content/locales/ar-SA/tracking.mdx +55 -0
  139. package/docs/content/locales/de-DE/tracking.mdx +55 -0
  140. package/docs/content/locales/es-ES/tracking.mdx +55 -0
  141. package/docs/content/locales/fr-FR/tracking.mdx +55 -0
  142. package/docs/content/locales/hi-IN/tracking.mdx +55 -0
  143. package/docs/content/locales/ja-JP/tracking.mdx +55 -0
  144. package/docs/content/locales/ko-KR/tracking.mdx +55 -0
  145. package/docs/content/locales/pt-BR/tracking.mdx +55 -0
  146. package/docs/content/locales/zh-CN/tracking.mdx +55 -0
  147. package/docs/content/locales/zh-TW/tracking.mdx +55 -0
  148. package/docs/content/tracking.mdx +2 -0
  149. package/package.json +1 -1
@@ -121,7 +121,8 @@ async function ensureRunTables(): Promise<void> {
121
121
  error_detail TEXT,
122
122
  terminal_reason TEXT,
123
123
  dispatch_mode TEXT,
124
- diag_stage TEXT
124
+ diag_stage TEXT,
125
+ dispatch_payload TEXT
125
126
  )
126
127
  `;
127
128
  const agentRunEventsCreateSql = `
@@ -196,6 +197,13 @@ async function ensureRunTables(): Promise<void> {
196
197
  ["dispatch_mode", "TEXT"],
197
198
  ["diag_stage", "TEXT"],
198
199
  ["worker_stage", "TEXT"],
200
+ // dispatch_payload holds the JSON request body for a background
201
+ // dispatch so the self-POST to the Netlify background function can
202
+ // stay tiny (Netlify caps background-function request bodies at
203
+ // 256KB — a large chat history silently exceeded it). The worker
204
+ // rehydrates the body from this column via the marker's payloadRef.
205
+ // Cleared on terminal status writes.
206
+ ["dispatch_payload", "TEXT"],
199
207
  ] as const) {
200
208
  await ensureColumnExists(
201
209
  "agent_runs",
@@ -280,6 +288,7 @@ async function ensureRunTables(): Promise<void> {
280
288
  "dispatch_mode",
281
289
  "diag_stage",
282
290
  "worker_stage",
291
+ "dispatch_payload",
283
292
  ] as const) {
284
293
  try {
285
294
  await client.execute(`ALTER TABLE agent_runs ADD COLUMN ${col} TEXT`);
@@ -410,13 +419,22 @@ export async function insertRun(
410
419
  id: string,
411
420
  threadId: string,
412
421
  turnId?: string,
413
- options?: { dispatchMode?: "foreground" | "background" },
422
+ options?: {
423
+ dispatchMode?: "foreground" | "background";
424
+ /**
425
+ * JSON-serialized request body for a background dispatch. Persisted on the
426
+ * run row so the self-POST to the background function carries only the
427
+ * tiny `__backgroundRun` marker (Netlify caps background-function request
428
+ * bodies at 256KB); the worker rehydrates the body from this column.
429
+ */
430
+ dispatchPayload?: string;
431
+ },
414
432
  ): Promise<void> {
415
433
  await ensureRunTables();
416
434
  const client = getDbExec();
417
435
  const now = Date.now();
418
436
  await client.execute({
419
- sql: `INSERT INTO agent_runs (id, thread_id, status, started_at, heartbeat_at, last_progress_at, turn_id, dispatch_mode) VALUES (?, ?, 'running', ?, ?, ?, ?, ?)`,
437
+ sql: `INSERT INTO agent_runs (id, thread_id, status, started_at, heartbeat_at, last_progress_at, turn_id, dispatch_mode, dispatch_payload) VALUES (?, ?, 'running', ?, ?, ?, ?, ?, ?)`,
420
438
  args: [
421
439
  id,
422
440
  threadId,
@@ -425,6 +443,7 @@ export async function insertRun(
425
443
  now,
426
444
  turnId ?? id,
427
445
  options?.dispatchMode ?? null,
446
+ options?.dispatchPayload ?? null,
428
447
  ],
429
448
  });
430
449
  }
@@ -557,6 +576,95 @@ export async function readBackgroundRunClaim(runId: string): Promise<{
557
576
  };
558
577
  }
559
578
 
579
+ /**
580
+ * Read the persisted dispatch payload for a background-dispatched run. The
581
+ * worker rehydrates its request body from this column when the dispatch marker
582
+ * carries `payloadRef: true` (the self-POST itself stays under Netlify's 256KB
583
+ * background-function body cap). Returns null when the row is missing or the
584
+ * payload was already cleared (terminal run).
585
+ */
586
+ export async function readRunDispatchPayload(
587
+ runId: string,
588
+ ): Promise<string | null> {
589
+ await ensureRunTables();
590
+ const client = getDbExec();
591
+ const { rows } = await client.execute({
592
+ sql: `SELECT dispatch_payload FROM agent_runs WHERE id = ? LIMIT 1`,
593
+ args: [runId],
594
+ });
595
+ const row = rows?.[0] as { dispatch_payload?: string | null } | undefined;
596
+ if (!row) return null;
597
+ const payload = row.dispatch_payload;
598
+ return typeof payload === "string" && payload.length > 0 ? payload : null;
599
+ }
600
+
601
+ /**
602
+ * Clear a run's persisted dispatch payload once the worker has claimed and
603
+ * rehydrated it — the payload can be large (full chat history) and has no use
604
+ * after the handoff. Best-effort; terminal status writes also clear it.
605
+ */
606
+ export async function clearRunDispatchPayload(runId: string): Promise<void> {
607
+ await ensureRunTables();
608
+ const client = getDbExec();
609
+ await client.execute({
610
+ sql: `UPDATE agent_runs SET dispatch_payload = NULL WHERE id = ?`,
611
+ args: [runId],
612
+ });
613
+ }
614
+
615
+ /**
616
+ * List background-dispatched runs that were never claimed by a worker within
617
+ * the unclaimed grace window. These are handoffs that were lost in flight —
618
+ * the async 202 (or the dispatching worker) died before any worker reached
619
+ * `claimBackgroundRun`. The periodic sweeper reaps them via
620
+ * `reapUnclaimedBackgroundRun` so a lost handoff becomes a loud, attributable
621
+ * error instead of a silent forever-hang. The foreground circuit-breaker
622
+ * already covers initial dispatches while the client is connected; this sweep
623
+ * exists for server-chained continuation handoffs, which have no foreground
624
+ * watching them.
625
+ */
626
+ export async function listUnclaimedBackgroundRunIds(): Promise<string[]> {
627
+ await ensureRunTables();
628
+ const client = getDbExec();
629
+ const { rows } = await client.execute({
630
+ // CAST keeps the ms-epoch param 64-bit on Postgres (see
631
+ // backgroundAwareStaleCutoffSql for the int4-inference failure mode).
632
+ sql: `SELECT id FROM agent_runs
633
+ WHERE status = 'running'
634
+ AND dispatch_mode = 'background'
635
+ AND COALESCE(heartbeat_at, started_at) < (CAST(? AS BIGINT) - ${UNCLAIMED_BACKGROUND_RUN_GRACE_MS})`,
636
+ args: [Date.now()],
637
+ });
638
+ const ids: string[] = [];
639
+ for (const row of rows ?? []) {
640
+ const id = (row as { id?: unknown }).id;
641
+ if (typeof id === "string" && id) ids.push(id);
642
+ }
643
+ return ids;
644
+ }
645
+
646
+ /**
647
+ * Count how many runs (chunks) a logical turn has consumed so far. This is the
648
+ * durable per-turn recovery ledger: unlike the in-marker `continuationCount`
649
+ * (which resets whenever a fresh client POST starts a new chain for the same
650
+ * turn), the SQL count survives every recovery path, so it bounds pathological
651
+ * turn loops regardless of which layer initiated each chunk.
652
+ */
653
+ export async function countRunsForTurn(
654
+ threadId: string,
655
+ turnId: string,
656
+ ): Promise<number> {
657
+ await ensureRunTables();
658
+ const client = getDbExec();
659
+ const { rows } = await client.execute({
660
+ sql: `SELECT COUNT(*) AS run_count FROM agent_runs WHERE thread_id = ? AND turn_id = ?`,
661
+ args: [threadId, turnId],
662
+ });
663
+ const raw = (rows?.[0] as { run_count?: unknown } | undefined)?.run_count;
664
+ const count = Number(raw);
665
+ return Number.isFinite(count) ? count : 0;
666
+ }
667
+
560
668
  /**
561
669
  * Resolve the authenticated owner email for a run by joining it to its chat
562
670
  * thread. The durable background worker's self-dispatch is cookieless
@@ -1027,7 +1135,9 @@ export async function updateRunStatus(
1027
1135
  await ensureRunTables();
1028
1136
  const client = getDbExec();
1029
1137
  await client.execute({
1030
- sql: `UPDATE agent_runs SET status = ?, completed_at = ? WHERE id = ?`,
1138
+ // Terminal writes also drop the (potentially large) dispatch payload
1139
+ // it only exists to rehydrate a not-yet-claimed background worker.
1140
+ sql: `UPDATE agent_runs SET status = ?, completed_at = ?, dispatch_payload = NULL WHERE id = ?`,
1031
1141
  args: [status, Date.now(), runId],
1032
1142
  });
1033
1143
  }
@@ -1047,7 +1157,9 @@ export async function updateRunStatusIfRunning(
1047
1157
  await ensureRunTables();
1048
1158
  const client = getDbExec();
1049
1159
  const { rowsAffected } = await client.execute({
1050
- sql: `UPDATE agent_runs SET status = ?, completed_at = ? WHERE id = ? AND status = 'running'`,
1160
+ // Terminal writes also drop the (potentially large) dispatch payload
1161
+ // it only exists to rehydrate a not-yet-claimed background worker.
1162
+ sql: `UPDATE agent_runs SET status = ?, completed_at = ?, dispatch_payload = NULL WHERE id = ? AND status = 'running'`,
1051
1163
  args: [status, Date.now(), runId],
1052
1164
  });
1053
1165
  return (rowsAffected ?? 0) > 0;
@@ -1071,11 +1183,13 @@ export async function markRunAborted(
1071
1183
  ): Promise<void> {
1072
1184
  await ensureRunTables();
1073
1185
  const client = getDbExec();
1074
- await client.execute({
1075
- sql: `UPDATE agent_runs SET status = 'aborted', abort_reason = ?, completed_at = ?, terminal_reason = ? WHERE id = ?`,
1186
+ const { rowsAffected } = await client.execute({
1187
+ sql: `UPDATE agent_runs SET status = 'aborted', abort_reason = ?, completed_at = ?, terminal_reason = ? WHERE id = ? AND status = 'running'`,
1076
1188
  args: [reason ?? "user", Date.now(), `aborted:${reason ?? "user"}`, runId],
1077
1189
  });
1078
- await safeAppendTerminalRunEvent(runId, { type: "done" }, "mark-aborted");
1190
+ if ((rowsAffected ?? 0) > 0) {
1191
+ await safeAppendTerminalRunEvent(runId, { type: "done" }, "mark-aborted");
1192
+ }
1079
1193
  }
1080
1194
 
1081
1195
  export async function isRunAborted(runId: string): Promise<boolean> {
@@ -178,6 +178,19 @@ export interface AgentChatRequest {
178
178
  * boundary and refuses to chain past `MAX_BACKGROUND_RUN_CONTINUATIONS`.
179
179
  */
180
180
  continuationCount?: number;
181
+ /**
182
+ * True when the dispatcher expects the self-POST to land in a real
183
+ * Netlify `-background` function (15-min budget) rather than the ~60s
184
+ * synchronous function. See `shouldUseBackgroundFunctionTimeoutForWorker`.
185
+ */
186
+ backgroundFunctionRuntimeExpected?: boolean;
187
+ /**
188
+ * True when the dispatch body carries ONLY this marker and the worker
189
+ * must rehydrate the full request body from the run row's
190
+ * `dispatch_payload` column (`readRunDispatchPayload`). Keeps the
191
+ * self-POST under Netlify's 256KB background-function body cap.
192
+ */
193
+ payloadRef?: boolean;
181
194
  };
182
195
  /**
183
196
  * Stable identity for the logical assistant turn this request belongs to.
@@ -163,6 +163,16 @@ export function RunStuckBanner({
163
163
  }
164
164
  const ownerId = autoRetryOwnerId ?? generatedOwnerIdRef.current;
165
165
  const backgroundWorkerStillAlive = isFreshBackgroundWorker(state);
166
+ // Background-dispatched runs are recovered by the SERVER (chained
167
+ // continuation chunks + lost-handoff sweep); an automatic client abort
168
+ // would kill a live server-chained run. Auto-retry is therefore disabled
169
+ // unconditionally for ANY background dispatch mode — not just a
170
+ // fresh-heartbeat worker — and the localStorage/Web-Locks auto-retry claim
171
+ // below is never taken for these runs (the adapter's follow loop is
172
+ // read-only, so multiple tabs need no retry dedup). Only the manual banner
173
+ // remains, on the wider background threshold from useRunStuckDetection.
174
+ const isBackgroundDispatch =
175
+ state.dispatchMode?.startsWith("background") === true;
166
176
 
167
177
  const lastReportedRef = useRef<{
168
178
  isStuck: boolean;
@@ -202,6 +212,9 @@ export function RunStuckBanner({
202
212
  useEffect(() => {
203
213
  if (
204
214
  !autoRetry ||
215
+ // Server owns recovery for background dispatch modes — never auto-abort
216
+ // (see comment on isBackgroundDispatch above).
217
+ isBackgroundDispatch ||
205
218
  backgroundWorkerStillAlive ||
206
219
  !state.isStuck ||
207
220
  !state.runId ||
@@ -238,6 +251,7 @@ export function RunStuckBanner({
238
251
  autoRetry,
239
252
  backgroundWorkerStillAlive,
240
253
  busy,
254
+ isBackgroundDispatch,
241
255
  onRetry,
242
256
  ownerId,
243
257
  state.isStuck,