@agent-native/core 0.114.9 → 0.114.10

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 (130) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +13 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +31 -3
  5. package/corpus/core/src/agent/engine/anthropic-engine.ts +20 -3
  6. package/corpus/core/src/agent/engine/builder-engine.ts +56 -9
  7. package/corpus/core/src/agent/engine/first-event-timeout.ts +64 -0
  8. package/corpus/core/src/cli/pr-visual-recap-workflow.ts +2 -3
  9. package/corpus/core/src/client/AssistantChat.tsx +7 -3
  10. package/corpus/core/src/client/resources/ResourcesPanel.tsx +1 -1
  11. package/corpus/core/src/integrations/computer-supervision-store.ts +4 -4
  12. package/corpus/core/src/integrations/pending-tasks-store.ts +1 -1
  13. package/corpus/core/src/integrations/remote-commands-store.ts +5 -5
  14. package/corpus/core/src/integrations/remote-devices-store.ts +3 -3
  15. package/corpus/core/src/integrations/remote-push-store.ts +3 -3
  16. package/corpus/core/src/observability/traces.ts +38 -1
  17. package/corpus/core/src/server/action-routes.ts +23 -1
  18. package/corpus/core/src/server/http-response-telemetry.ts +6 -1
  19. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
  20. package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
  21. package/corpus/templates/analytics/actions/account-deep-dive.ts +41 -6
  22. package/corpus/templates/analytics/changelog/2026-07-20-account-deep-dives-finish-with-partial-data-when-hubspot-lo.md +6 -0
  23. package/corpus/templates/analytics/changelog/2026-07-20-dashboard-email-reports-now-include-an-image-when-a-single-p.md +6 -0
  24. package/corpus/templates/analytics/server/lib/dashboard-report.ts +11 -3
  25. package/corpus/templates/calendar/actions/create-event.ts +5 -0
  26. package/corpus/templates/calendar/app/components/ThemeToggle.tsx +4 -1
  27. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +0 -1
  28. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +309 -421
  29. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +349 -0
  30. package/corpus/templates/calendar/app/i18n-data.ts +110 -0
  31. package/corpus/templates/calendar/app/lib/event-form-utils.ts +6 -0
  32. package/corpus/templates/calendar/app/pages/CalendarView.tsx +3 -0
  33. package/corpus/templates/calendar/app/root.tsx +4 -2
  34. package/corpus/templates/calendar/changelog/2026-07-20-events-can-be-updated-inline-with-date-time-timezone-and-rep.md +6 -0
  35. package/corpus/templates/calendar/changelog/2026-07-20-public-booking-pages-no-longer-hydrate-with-a-theme-mismatch.md +6 -0
  36. package/corpus/templates/calendar/shared/api.ts +1 -0
  37. package/corpus/templates/clips/AGENTS.md +7 -0
  38. package/corpus/templates/clips/actions/create-recording.ts +51 -10
  39. package/corpus/templates/clips/actions/get-recording-player-data.ts +34 -5
  40. package/corpus/templates/clips/actions/lib/transcript-preview.ts +48 -0
  41. package/corpus/templates/clips/actions/list-recordings.ts +15 -4
  42. package/corpus/templates/clips/actions/regenerate-summary.ts +32 -0
  43. package/corpus/templates/clips/actions/search-recordings.ts +28 -6
  44. package/corpus/templates/clips/actions/view-screen.ts +54 -47
  45. package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -5
  46. package/corpus/templates/clips/app/components/library/recording-card.tsx +29 -32
  47. package/corpus/templates/clips/app/components/meetings/quick-ask-sidebar.tsx +13 -0
  48. package/corpus/templates/clips/changelog/2026-07-20-agent-summaries-use-bounded-transcript-context.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-07-20-clip-summaries-now-finish-without-sending-a-long-transcript-.md +6 -0
  50. package/corpus/templates/clips/changelog/2026-07-20-clips-agents-only-discover-recordings-you-own-or-have-alread.md +6 -0
  51. package/corpus/templates/clips/changelog/2026-07-20-hosted-recordings-no-longer-start-with-an-impossible-buffered-upload.md +6 -0
  52. package/corpus/templates/clips/changelog/2026-07-20-meeting-recordings-now-offer-a-cleaner-transcript-view-with-.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-07-20-shared-clips-can-be-shared-with-limited-access-and-cards-sho.md +6 -0
  54. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +12 -8
  55. package/corpus/templates/clips/desktop/src/overlays/pill-logo.tsx +2 -15
  56. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +107 -340
  57. package/corpus/templates/clips/desktop/src/styles.css +27 -145
  58. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +6 -7
  59. package/corpus/templates/clips/server/lib/agent-recording-access.ts +73 -0
  60. package/corpus/templates/clips/server/plugins/agent-chat.ts +5 -2
  61. package/corpus/templates/plan/app/lib/plan-local-bridge.ts +18 -0
  62. package/corpus/templates/plan/app/pages/PlansPage.tsx +10 -11
  63. package/corpus/templates/plan/changelog/2026-07-20-bridged-local-plans-now-load-comments-from-the-local-workspace.md +6 -0
  64. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  65. package/dist/agent/engine/ai-sdk-engine.js +27 -4
  66. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  67. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  68. package/dist/agent/engine/anthropic-engine.js +19 -4
  69. package/dist/agent/engine/anthropic-engine.js.map +1 -1
  70. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  71. package/dist/agent/engine/builder-engine.js +40 -7
  72. package/dist/agent/engine/builder-engine.js.map +1 -1
  73. package/dist/agent/engine/first-event-timeout.d.ts +25 -0
  74. package/dist/agent/engine/first-event-timeout.d.ts.map +1 -0
  75. package/dist/agent/engine/first-event-timeout.js +49 -0
  76. package/dist/agent/engine/first-event-timeout.js.map +1 -0
  77. package/dist/cli/pr-visual-recap-workflow.d.ts +2 -2
  78. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  79. package/dist/cli/pr-visual-recap-workflow.js +2 -2
  80. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  81. package/dist/client/AssistantChat.d.ts.map +1 -1
  82. package/dist/client/AssistantChat.js +7 -3
  83. package/dist/client/AssistantChat.js.map +1 -1
  84. package/dist/client/resources/ResourcesPanel.js +1 -1
  85. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  86. package/dist/collab/routes.d.ts +1 -1
  87. package/dist/integrations/computer-supervision-store.js +4 -4
  88. package/dist/integrations/computer-supervision-store.js.map +1 -1
  89. package/dist/integrations/pending-tasks-store.js +1 -1
  90. package/dist/integrations/pending-tasks-store.js.map +1 -1
  91. package/dist/integrations/remote-commands-store.js +5 -5
  92. package/dist/integrations/remote-commands-store.js.map +1 -1
  93. package/dist/integrations/remote-devices-store.js +3 -3
  94. package/dist/integrations/remote-devices-store.js.map +1 -1
  95. package/dist/integrations/remote-push-store.js +3 -3
  96. package/dist/integrations/remote-push-store.js.map +1 -1
  97. package/dist/notifications/routes.d.ts +2 -2
  98. package/dist/observability/routes.d.ts +3 -3
  99. package/dist/observability/traces.d.ts.map +1 -1
  100. package/dist/observability/traces.js +23 -1
  101. package/dist/observability/traces.js.map +1 -1
  102. package/dist/secrets/routes.d.ts +9 -9
  103. package/dist/server/action-routes.d.ts.map +1 -1
  104. package/dist/server/action-routes.js +23 -1
  105. package/dist/server/action-routes.js.map +1 -1
  106. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  107. package/dist/server/http-response-telemetry.d.ts +2 -0
  108. package/dist/server/http-response-telemetry.d.ts.map +1 -1
  109. package/dist/server/http-response-telemetry.js +5 -1
  110. package/dist/server/http-response-telemetry.js.map +1 -1
  111. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
  112. package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
  113. package/package.json +2 -2
  114. package/src/agent/engine/ai-sdk-engine.ts +31 -3
  115. package/src/agent/engine/anthropic-engine.ts +20 -3
  116. package/src/agent/engine/builder-engine.ts +56 -9
  117. package/src/agent/engine/first-event-timeout.ts +64 -0
  118. package/src/cli/pr-visual-recap-workflow.ts +2 -3
  119. package/src/client/AssistantChat.tsx +7 -3
  120. package/src/client/resources/ResourcesPanel.tsx +1 -1
  121. package/src/integrations/computer-supervision-store.ts +4 -4
  122. package/src/integrations/pending-tasks-store.ts +1 -1
  123. package/src/integrations/remote-commands-store.ts +5 -5
  124. package/src/integrations/remote-devices-store.ts +3 -3
  125. package/src/integrations/remote-push-store.ts +3 -3
  126. package/src/observability/traces.ts +38 -1
  127. package/src/server/action-routes.ts +23 -1
  128. package/src/server/http-response-telemetry.ts +6 -1
  129. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
  130. package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
@@ -175,7 +175,7 @@ export async function decideComputerApproval(input: {
175
175
  SET status = ?, decision_result_json = ?, decided_by = ?,
176
176
  decided_at = ?, updated_at = ?
177
177
  WHERE id = ? AND owner_email = ?
178
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)
178
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)
179
179
  AND action_hash = ? AND status = 'pending' AND expires_at > ?`,
180
180
  args: [
181
181
  input.decision,
@@ -204,7 +204,7 @@ export async function getComputerApprovalForOwner(input: {
204
204
  const { rows } = await getDbExec().execute({
205
205
  sql: `SELECT * FROM integration_computer_approvals
206
206
  WHERE id = ? AND owner_email = ?
207
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)
207
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)
208
208
  LIMIT 1`,
209
209
  args: [
210
210
  input.id,
@@ -228,7 +228,7 @@ export async function listComputerApprovalsForOwner(input: {
228
228
  await ensureComputerApprovalStore();
229
229
  const clauses = [
230
230
  "owner_email = ?",
231
- "((org_id IS NULL AND ? IS NULL) OR org_id = ?)",
231
+ "((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)",
232
232
  ];
233
233
  const args: Array<string | number | null> = [
234
234
  input.ownerEmail,
@@ -295,7 +295,7 @@ export async function authorizeComputerOperation(
295
295
  const { rows } = await client.execute({
296
296
  sql: `SELECT * FROM integration_computer_approvals
297
297
  WHERE id = ? AND owner_email = ?
298
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)
298
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)
299
299
  AND device_id = ? AND task_id = ? AND run_id = ?
300
300
  LIMIT 1`,
301
301
  args: [
@@ -316,7 +316,7 @@ export async function resolveIntegrationSourceContext(
316
316
  FROM integration_pending_tasks
317
317
  WHERE id = ?
318
318
  AND owner_email = ?
319
- AND (org_id = ? OR (org_id IS NULL AND ? IS NULL))
319
+ AND (org_id = ? OR (org_id IS NULL AND CAST(? AS TEXT) IS NULL))
320
320
  AND platform = 'slack'
321
321
  LIMIT 1`,
322
322
  args: [id, ownerEmail, orgId, orgId],
@@ -368,7 +368,7 @@ export async function listRemoteCommandsForOwner(input: {
368
368
  const { rows } = await getDbExec().execute({
369
369
  sql: `SELECT * FROM integration_remote_commands
370
370
  WHERE owner_email = ?
371
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)
371
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)
372
372
  ORDER BY updated_at DESC
373
373
  LIMIT ?`,
374
374
  args: [input.ownerEmail, input.orgId ?? null, input.orgId ?? null, limit],
@@ -435,7 +435,7 @@ export async function claimNextComputerCommand(input: {
435
435
  const { rows } = await client.execute({
436
436
  sql: `SELECT * FROM integration_remote_commands
437
437
  WHERE device_id = ? AND owner_email = ?
438
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)
438
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)
439
439
  AND kind = 'computer-operation' AND status = 'pending'
440
440
  AND next_check_at <= ?${operationClassClause}
441
441
  ORDER BY computer_sequence ASC, created_at ASC
@@ -477,7 +477,7 @@ export async function claimNextComputerCommand(input: {
477
477
  sql: `UPDATE integration_remote_commands
478
478
  SET status = 'failed', error_message = ?, completed_at = ?, updated_at = ?
479
479
  WHERE id = ? AND device_id = ? AND owner_email = ?
480
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)
480
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)
481
481
  AND status = 'pending'`,
482
482
  args: [
483
483
  error instanceof Error
@@ -500,13 +500,13 @@ export async function claimNextComputerCommand(input: {
500
500
  ? `UPDATE integration_remote_commands
501
501
  SET status = 'claimed', attempts = attempts + 1, claimed_at = ?, updated_at = ?
502
502
  WHERE id = ? AND device_id = ? AND owner_email = ?
503
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)
503
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)
504
504
  AND status = 'pending' AND lease_expires_at > ?
505
505
  RETURNING *`
506
506
  : `UPDATE integration_remote_commands
507
507
  SET status = 'claimed', attempts = attempts + 1, claimed_at = ?, updated_at = ?
508
508
  WHERE id = ? AND device_id = ? AND owner_email = ?
509
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)
509
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)
510
510
  AND status = 'pending' AND lease_expires_at > ?`,
511
511
  args: [
512
512
  now,
@@ -243,7 +243,7 @@ export async function getRemoteDeviceForOwner(input: {
243
243
  sql: `SELECT * FROM integration_remote_devices
244
244
  WHERE id = ?
245
245
  AND owner_email = ?
246
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)
246
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)
247
247
  LIMIT 1`,
248
248
  args: [
249
249
  input.id,
@@ -287,7 +287,7 @@ export async function listRemoteDevicesForOwner(input: {
287
287
  const { rows } = await getDbExec().execute({
288
288
  sql: `SELECT * FROM integration_remote_devices
289
289
  WHERE owner_email = ?
290
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)${statusClause}
290
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)${statusClause}
291
291
  ORDER BY COALESCE(last_seen_at, updated_at) DESC
292
292
  LIMIT ?`,
293
293
  args,
@@ -379,7 +379,7 @@ export async function revokeRemoteDeviceForOwner(input: {
379
379
  updated_at = ?
380
380
  WHERE id = ?
381
381
  AND owner_email = ?
382
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)`,
382
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)`,
383
383
  args: [
384
384
  now,
385
385
  now,
@@ -330,7 +330,7 @@ export async function listRemotePushRegistrationsForOwner(input: {
330
330
  const { rows } = await getDbExec().execute({
331
331
  sql: `SELECT * FROM integration_remote_push_registrations
332
332
  WHERE owner_email = ?
333
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)${statusClause}
333
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)${statusClause}
334
334
  ORDER BY COALESCE(last_seen_at, updated_at) DESC
335
335
  LIMIT ?`,
336
336
  args: [input.ownerEmail, input.orgId ?? null, input.orgId ?? null, limit],
@@ -352,7 +352,7 @@ export async function unregisterRemotePushRegistrationForOwner(input: {
352
352
  sql: `UPDATE integration_remote_push_registrations
353
353
  SET status = 'inactive', updated_at = ?
354
354
  WHERE owner_email = ?
355
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)
355
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)
356
356
  AND (${input.id ? "id = ?" : "0 = 1"} OR ${
357
357
  tokenHash ? "token_hash = ?" : "0 = 1"
358
358
  })`,
@@ -432,7 +432,7 @@ export async function listRemotePushNotificationsForOwner(input: {
432
432
  const { rows } = await getDbExec().execute({
433
433
  sql: `SELECT * FROM integration_remote_push_notifications
434
434
  WHERE owner_email = ?
435
- AND ((org_id IS NULL AND ? IS NULL) OR org_id = ?)${statusClause}
435
+ AND ((org_id IS NULL AND CAST(? AS TEXT) IS NULL) OR org_id = ?)${statusClause}
436
436
  ORDER BY created_at DESC
437
437
  LIMIT ?`,
438
438
  args,
@@ -27,6 +27,9 @@ function costUsdFromCenticents(value: number): number {
27
27
  }
28
28
 
29
29
  const MAX_TRACKED_GENERATION_TOOL_CALLS = 50;
30
+ const MAX_TOOL_ERROR_MESSAGE_LENGTH = 500;
31
+ const STANDALONE_API_KEY_PATTERN =
32
+ /\b(?:sk-(?:proj-|ant-)?[A-Za-z0-9_-]{8,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{8,}|AIza[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{16,})\b/g;
30
33
 
31
34
  type GenerationToolCall = {
32
35
  name: string;
@@ -34,8 +37,35 @@ type GenerationToolCall = {
34
37
  duration_ms: number;
35
38
  status: "success" | "error";
36
39
  error_class: "tool_error" | "legacy_inferred_error" | "interrupted" | null;
40
+ error_message?: string;
37
41
  };
38
42
 
43
+ function truncateToolErrorMessage(value: string): string {
44
+ return value.length > MAX_TOOL_ERROR_MESSAGE_LENGTH
45
+ ? `${value.slice(0, MAX_TOOL_ERROR_MESSAGE_LENGTH)}…`
46
+ : value;
47
+ }
48
+
49
+ function redactToolErrorMessage(value: string): string {
50
+ const credentialName =
51
+ "authorization|cookie|api[_ -]?key|password|secret|token|access[_ -]?token|refresh[_ -]?token";
52
+ const labeledCredential = `(["']?\\b(?:${credentialName})\\b["']?\\s*[:=]\\s*["']?)`;
53
+ return value
54
+ .replace(
55
+ new RegExp(
56
+ `${labeledCredential}(?:Bearer|Basic)\\s+[^"'\\s,;)}\\]]+`,
57
+ "gi",
58
+ ),
59
+ "$1[REDACTED]",
60
+ )
61
+ .replace(
62
+ new RegExp(`${labeledCredential}[^"'\\s,;)}\\[\\]]+`, "gi"),
63
+ "$1[REDACTED]",
64
+ )
65
+ .replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, "[REDACTED]")
66
+ .replace(STANDALONE_API_KEY_PATTERN, "[REDACTED]");
67
+ }
68
+
39
69
  function emitLlmGenerationTrackingEvent(args: {
40
70
  runId: string;
41
71
  threadId: string | null;
@@ -439,6 +469,10 @@ export async function instrumentAgentLoop(opts: {
439
469
  : explicitError
440
470
  ? "tool_error"
441
471
  : "legacy_inferred_error",
472
+ error_message:
473
+ isError && config.captureToolResults
474
+ ? truncateToolErrorMessage(redactToolErrorMessage(event.result))
475
+ : undefined,
442
476
  });
443
477
  }
444
478
 
@@ -532,6 +566,7 @@ export async function instrumentAgentLoop(opts: {
532
566
  for (const [counter, pending] of pendingTools) {
533
567
  toolCallCount += 1;
534
568
  failedTools += 1;
569
+ const interruptedMessage = "Tool call interrupted before completion";
535
570
  if (counter < MAX_TRACKED_GENERATION_TOOL_CALLS) {
536
571
  generationToolCalls.set(counter, {
537
572
  name: pending.toolName,
@@ -539,9 +574,11 @@ export async function instrumentAgentLoop(opts: {
539
574
  duration_ms: Math.max(0, runEnd - pending.startMs),
540
575
  status: "error",
541
576
  error_class: "interrupted",
577
+ error_message: config.captureToolResults
578
+ ? interruptedMessage
579
+ : undefined,
542
580
  });
543
581
  }
544
- const interruptedMessage = "Tool call interrupted before completion";
545
582
  if (pending.otelSpan) {
546
583
  openOtelToolSpans.delete(pending.otelSpan);
547
584
  endAgentSpan(pending.otelSpan, {
@@ -26,10 +26,12 @@ import {
26
26
  seedAgentRunOwnerContext,
27
27
  type AgentRunOwnerContext,
28
28
  } from "./agent-run-context.js";
29
+ import { captureError } from "./capture-error.js";
29
30
  import {
30
31
  getAllowedCorsOrigin as resolveAllowedCorsOrigin,
31
32
  readCorsAllowedOrigins,
32
33
  } from "./cors-origins.js";
34
+ import { getHttpRequestTelemetryId } from "./http-response-telemetry.js";
33
35
 
34
36
  declare const __AGENT_NATIVE_BUILD_ID__: string | undefined;
35
37
  declare const __AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__: string | undefined;
@@ -640,7 +642,27 @@ export function mountActionRoutes(
640
642
  if (isUserFacing) {
641
643
  return { error: msg };
642
644
  }
643
- console.error(`[agent-native] action '${name}' failed:`, err);
645
+ const requestId = getHttpRequestTelemetryId(event);
646
+ const captureId = captureError(err, {
647
+ route: routePath,
648
+ method: reqMethod,
649
+ tags: {
650
+ action: name,
651
+ caller: resolvedCaller
652
+ ? "a2a"
653
+ : isFrontendActionRequest(event)
654
+ ? "frontend"
655
+ : "http",
656
+ status_code: String(status),
657
+ },
658
+ ...(requestId ? { extra: { request_id: requestId } } : {}),
659
+ });
660
+ console.error(`[agent-native] action '${name}' failed:`, {
661
+ action: name,
662
+ ...(requestId ? { requestId } : {}),
663
+ ...(captureId ? { captureId } : {}),
664
+ error: err?.stack ?? String(err),
665
+ });
644
666
  return { error: "Internal server error" };
645
667
  }
646
668
  },
@@ -284,11 +284,16 @@ function emitTelemetry(
284
284
  function requestTelemetryState(
285
285
  event: H3Event,
286
286
  ): HttpRequestTelemetryState | undefined {
287
- return (event.context as Record<PropertyKey, unknown>)[
287
+ return (event.context as Record<PropertyKey, unknown> | undefined)?.[
288
288
  REQUEST_TELEMETRY_KEY
289
289
  ] as HttpRequestTelemetryState | undefined;
290
290
  }
291
291
 
292
+ /** Return the durable request id while a request is still being handled. */
293
+ export function getHttpRequestTelemetryId(event: H3Event): string | undefined {
294
+ return requestTelemetryState(event)?.requestId;
295
+ }
296
+
292
297
  function appendServerTiming(
293
298
  response: Response,
294
299
  event: H3Event,
@@ -32,7 +32,7 @@ await putSetting("observability-config", {
32
32
  enabled: true,
33
33
  capturePrompts: false,
34
34
  captureToolArgs: true, // capture action input args
35
- captureToolResults: false,
35
+ captureToolResults: false, // include failed tool error text on tracked $ai_generation tool call entries
36
36
  evalSampleRate: 0.05, // 5% of runs get LLM-as-judge eval
37
37
  inferredSentimentEnabled: false,
38
38
  inferredSentimentSampleRate: 0,
@@ -160,7 +160,7 @@ Other framework-level baseline events:
160
160
  - `signup` from Better Auth user creation, with `auth_provider`, `auth_user_id`, and first-touch referral attribution (`referral_source`, `referrer_user`, `referral_medium`, `referral_campaign`, `utm_*`, `first_touch_path`, `landing_referrer` — see "Referral / viral attribution" above)
161
161
  - `builder connect clicked` and `builder connect popup blocked` from browser Connect Builder CTAs
162
162
  - `builder connect started`, `builder connect succeeded`, `builder connect failed`, `builder disconnect succeeded`, and `builder disconnect failed` from the Builder connection routes, with LLM connection context when resolvable
163
- - `$ai_generation` from instrumented agent loops, with PostHog AI Observability fields such as `$ai_trace_id`, `$ai_session_id`, `$ai_model`, `$ai_provider`, `$ai_input_tokens`, `$ai_output_tokens`, `$ai_latency`, `$ai_total_cost_usd`, and mirrored Agent Native query fields such as `run_id`, `thread_id`, `cost_cents_x100`, `duration_ms`, `tool_calls`, and `status`. A bounded `tools` array contains names, start offsets, durations, statuses, and coarse error classes only; interrupted tools and failed runs remain visible, and delegated runs include protocol/task/parent-run/parent-turn correlation. Prompt, tool argument, result, and output content is excluded.
163
+ - `$ai_generation` from instrumented agent loops, with PostHog AI Observability fields such as `$ai_trace_id`, `$ai_session_id`, `$ai_model`, `$ai_provider`, `$ai_input_tokens`, `$ai_output_tokens`, `$ai_latency`, `$ai_total_cost_usd`, and mirrored Agent Native query fields such as `run_id`, `thread_id`, `cost_cents_x100`, `duration_ms`, `tool_calls`, and `status`. A bounded `tools` array contains names, start offsets, durations, statuses, and coarse error classes only; interrupted tools and failed runs remain visible, and delegated runs include protocol/task/parent-run/parent-turn correlation. Prompt, tool argument, result, and output content is excluded unless `captureToolResults` is opted in (see the `observability` skill), in which case each failed tool call also carries a `error_message` string truncated to 500 characters and already scrubbed of bearer tokens, API keys, and key/value secret patterns.
164
164
 
165
165
  For new lifecycle events, call `track()` server-side when the server is the source of truth, and `trackEvent()` client-side only for browser interactions.
166
166
 
@@ -381,16 +381,51 @@ export default defineAction({
381
381
  run: async (args) => {
382
382
  const trimmedQuery = args.query.trim();
383
383
  const gaps: string[] = [];
384
- const [dealResult, pipelines, owners] = await Promise.all([
385
- searchHubSpotObjects({
384
+
385
+ const enrichmentLookups = Promise.allSettled([
386
+ getDealPipelines(),
387
+ getDealOwners(),
388
+ ]);
389
+ let dealResult;
390
+ try {
391
+ dealResult = await searchHubSpotObjects({
386
392
  objectType: "deals",
387
393
  query: trimmedQuery,
388
394
  properties: DEAL_DEEP_DIVE_PROPERTIES,
389
395
  limit: args.dealLimit,
390
- }),
391
- getDealPipelines(),
392
- getDealOwners(),
393
- ]);
396
+ });
397
+ } catch (err) {
398
+ throw new Error(
399
+ `HubSpot deal search failed for "${trimmedQuery}": ${
400
+ err instanceof Error ? err.message : String(err)
401
+ }`,
402
+ );
403
+ }
404
+ const [pipelinesSettled, ownersSettled] = await enrichmentLookups;
405
+
406
+ let pipelines: Pipeline[] = [];
407
+ if (pipelinesSettled.status === "fulfilled") {
408
+ pipelines = pipelinesSettled.value;
409
+ } else {
410
+ const err = pipelinesSettled.reason;
411
+ gaps.push(
412
+ `HubSpot deal pipelines: ${
413
+ err instanceof Error ? err.message : String(err)
414
+ }`,
415
+ );
416
+ }
417
+
418
+ let owners: Record<string, string> = {};
419
+ if (ownersSettled.status === "fulfilled") {
420
+ owners = ownersSettled.value;
421
+ } else {
422
+ const err = ownersSettled.reason;
423
+ gaps.push(
424
+ `HubSpot deal owners: ${
425
+ err instanceof Error ? err.message : String(err)
426
+ }`,
427
+ );
428
+ }
394
429
 
395
430
  const lookups = stageLookups(getVisiblePipelines(pipelines));
396
431
  const deals = dealResult.records.map((deal) =>
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-20
4
+ ---
5
+
6
+ Account deep dives now finish with partial data when HubSpot pipeline or owner lookups fail, and failed deal searches report a clear error.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-20
4
+ ---
5
+
6
+ Dashboard email reports now include an image when a single panel is still loading.
@@ -361,7 +361,7 @@ function positiveIntEnv(name: string): number | null {
361
361
  async function waitForDashboardReportReady(
362
362
  page: any,
363
363
  timeout: number,
364
- ): Promise<void> {
364
+ ): Promise<boolean> {
365
365
  try {
366
366
  await page.waitForFunction(
367
367
  `(() => {
@@ -379,6 +379,7 @@ async function waitForDashboardReportReady(
379
379
  await document.fonts?.ready;
380
380
  })()`);
381
381
  await page.waitForTimeout(750);
382
+ return true;
382
383
  } catch (err: any) {
383
384
  const detail = await page
384
385
  .evaluate(`(() => {
@@ -391,6 +392,12 @@ async function waitForDashboardReportReady(
391
392
  };
392
393
  })()`)
393
394
  .catch(() => null);
395
+ if (detail?.ready === "true") {
396
+ console.warn(
397
+ "[dashboard-report] Dashboard surface stayed partially loaded; capturing the available panels.",
398
+ );
399
+ return false;
400
+ }
394
401
  const message = detail
395
402
  ? `${err?.message ?? String(err)}; dashboard state: ${JSON.stringify(detail)}`
396
403
  : `${err?.message ?? String(err)}; dashboard page was not inspectable`;
@@ -788,6 +795,7 @@ async function captureDashboardPngWithFallback(
788
795
  captureScale: 0.85,
789
796
  ...(serverless
790
797
  ? {
798
+ readyTimeout: 45_000,
791
799
  secondReadyTimeout: 25_000,
792
800
  totalTimeout: SERVERLESS_FULL_ATTEMPT_TIMEOUT_MS,
793
801
  }
@@ -799,7 +807,7 @@ async function captureDashboardPngWithFallback(
799
807
  captureScale: 0.7,
800
808
  ...(serverless
801
809
  ? {
802
- readyTimeout: 55_000,
810
+ readyTimeout: 35_000,
803
811
  secondReadyTimeout: 15_000,
804
812
  totalTimeout: SERVERLESS_LIGHTWEIGHT_ATTEMPT_TIMEOUT_MS,
805
813
  }
@@ -818,7 +826,7 @@ async function captureDashboardPngWithFallback(
818
826
  reportPanelLimit: 8,
819
827
  ...(serverless
820
828
  ? {
821
- readyTimeout: 40_000,
829
+ readyTimeout: 25_000,
822
830
  secondReadyTimeout: 10_000,
823
831
  totalTimeout: 45_000,
824
832
  }
@@ -69,6 +69,10 @@ export default defineAction({
69
69
  colorId: googleColorIdInput.describe(
70
70
  "Google Calendar event color id, 1 through 11.",
71
71
  ),
72
+ recurrence: z
73
+ .array(z.string())
74
+ .optional()
75
+ .describe("Google recurrence rules such as RRULE:FREQ=WEEKLY;BYDAY=MO."),
72
76
  reminderMinutes: reminderMinutesInput.describe(
73
77
  "Convenience field for a single reminder in minutes before the event.",
74
78
  ),
@@ -168,6 +172,7 @@ export default defineAction({
168
172
  attendees,
169
173
  attachments: args.attachments,
170
174
  colorId: args.colorId,
175
+ recurrence: args.recurrence,
171
176
  ...reminderFields,
172
177
  ...statusEventFields,
173
178
  createdAt: new Date().toISOString(),
@@ -1,6 +1,7 @@
1
1
  import { useT } from "@agent-native/core/client/i18n";
2
2
  import { IconSun, IconMoon } from "@tabler/icons-react";
3
3
  import { useTheme } from "next-themes";
4
+ import { useEffect, useState } from "react";
4
5
 
5
6
  import { Button } from "@/components/ui/button";
6
7
  import {
@@ -12,7 +13,9 @@ import { cn } from "@/lib/utils";
12
13
 
13
14
  export function ThemeToggle({ className }: { className?: string }) {
14
15
  const { resolvedTheme, setTheme } = useTheme();
15
- const isDark = resolvedTheme === "dark";
16
+ const [mounted, setMounted] = useState(false);
17
+ useEffect(() => setMounted(true), []);
18
+ const isDark = mounted && resolvedTheme === "dark";
16
19
  const t = useT();
17
20
 
18
21
  return (
@@ -539,7 +539,6 @@ export function EventDetailPanel({
539
539
  <ExtensionSlot
540
540
  id="calendar.event-detail.bottom"
541
541
  context={eventDetailSlotContext}
542
- showEmptyAffordance
543
542
  />
544
543
  )}
545
544
  </div>