@agent-native/core 0.148.0 → 0.149.0

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 (165) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/changelog/2026-08-10-wide-tables-in-analytics-chat-now-scroll-horizontally-within.md +6 -0
  3. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +13 -53
  4. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -70
  5. package/corpus/templates/calendar/app/lib/event-layout.ts +122 -0
  6. package/corpus/templates/calendar/app/routes/_app.availability.tsx +1 -1
  7. package/corpus/templates/calendar/changelog/2026-08-10-opening-availability-settings-from-the-agent-lands-on-the-av.md +6 -0
  8. package/corpus/templates/calendar/vitest.config.ts +6 -1
  9. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +3 -1
  10. package/corpus/templates/clips/app/components/player/video-player.tsx +44 -4
  11. package/corpus/templates/clips/changelog/2026-08-10-fixed-transcription-failures-for-recordings-without-captured.md +6 -0
  12. package/corpus/templates/clips/changelog/2026-08-10-mobile-clips-can-now-enter-fullscreen-on-iphone-safari.md +6 -0
  13. package/corpus/templates/content/actions/_property-utils.ts +11 -1
  14. package/corpus/templates/content/actions/view-screen.ts +62 -12
  15. package/corpus/templates/content/server/plugins/agent-chat.ts +2 -0
  16. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +22 -0
  17. package/corpus/templates/design/AGENTS.md +3 -0
  18. package/corpus/templates/design/actions/create-design-from-template.ts +19 -0
  19. package/corpus/templates/design/actions/get-design-snapshot.ts +18 -0
  20. package/corpus/templates/design/actions/get-design-template.ts +178 -0
  21. package/corpus/templates/design/actions/list-designs.ts +3 -1
  22. package/corpus/templates/design/actions/view-screen.ts +31 -0
  23. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +225 -214
  24. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  25. package/corpus/templates/design/app/i18n-data.ts +78 -0
  26. package/corpus/templates/design/app/lib/design-authors.ts +64 -0
  27. package/corpus/templates/design/app/pages/Index.tsx +114 -4
  28. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +1 -0
  29. package/corpus/templates/design/changelog/2026-08-07-alt-dragging-an-element-to-copy-it-now-keeps-the-copy-visibl.md +6 -0
  30. package/corpus/templates/design/changelog/2026-08-07-design-cards-now-show-who-created-each-design-with-a-filter-.md +6 -0
  31. package/corpus/templates/design/changelog/2026-08-07-drawing-a-shape-on-the-overview-canvas-now-keeps-the-shape-s.md +6 -0
  32. package/corpus/templates/design/changelog/2026-08-07-hold-option-alt-while-dragging-a-frame-on-the-overview-canva.md +6 -0
  33. package/corpus/templates/design/server/lib/design-template-data.ts +143 -0
  34. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -0
  35. package/corpus/templates/forms/AGENTS.md +2 -2
  36. package/corpus/templates/forms/actions/create-form.ts +1 -1
  37. package/corpus/templates/forms/actions/update-form.ts +5 -5
  38. package/corpus/templates/forms/changelog/2026-08-10-forms-now-keep-agent-applied-field-changes-visible-after-an-.md +6 -0
  39. package/corpus/templates/forms/changelog/2026-08-10-published-form-links-always-use-the-working-public-form-url.md +6 -0
  40. package/corpus/templates/forms/server/plugins/agent-chat.ts +2 -2
  41. package/corpus/templates/slides/AGENTS.md +4 -3
  42. package/corpus/templates/slides/actions/import-google-slides-reference.ts +56 -10
  43. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +12 -74
  44. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +5 -1
  45. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +15 -4
  46. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +116 -55
  47. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -0
  48. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +143 -18
  49. package/corpus/templates/slides/app/i18n/en-US.ts +6 -0
  50. package/corpus/templates/slides/app/lib/generation-state.ts +7 -1
  51. package/corpus/templates/slides/app/pages/DeckEditor.tsx +32 -14
  52. package/corpus/templates/slides/app/pages/Index.tsx +110 -128
  53. package/corpus/templates/slides/changelog/2026-08-10-imported-reference-decks-now-show-clear-success-and-stay-sel.md +6 -0
  54. package/corpus/templates/slides/changelog/2026-08-10-pasted-google-slides-links-import-after-reconnecting-google-.md +6 -0
  55. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +4 -0
  56. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +13 -0
  57. package/corpus/templates/slides/server/plugins/agent-chat.ts +1 -1
  58. package/dist/agent/engine/registry.d.ts +8 -2
  59. package/dist/agent/engine/registry.js +63 -29
  60. package/dist/agent/production-agent.js +7 -0
  61. package/dist/agent/run-manager.d.ts +5 -4
  62. package/dist/agent/run-manager.js +6 -5
  63. package/dist/cli/claude-code-participant.d.ts +2 -1
  64. package/dist/cli/claude-code-participant.js +19 -0
  65. package/dist/cli/code-agent-executor.js +223 -0
  66. package/dist/cli/create-workspace.js +3 -3
  67. package/dist/cli/create.d.ts +3 -2
  68. package/dist/cli/create.js +20 -24
  69. package/dist/client/AgentPanel.js +8 -4
  70. package/dist/client/AssistantChat.js +34 -1
  71. package/dist/client/agent-chat-adapter.d.ts +1 -0
  72. package/dist/client/agent-chat-adapter.js +10 -2
  73. package/dist/client/blocks/library/diagram.js +1 -1
  74. package/dist/client/blocks/library/sanitize-html.js +66 -2
  75. package/dist/client/blocks/library/wireframe-kit.js +69 -0
  76. package/dist/client/chat/markdown-renderer.d.ts +1 -0
  77. package/dist/client/chat/markdown-renderer.js +4 -0
  78. package/dist/client/chat/message-components.d.ts +1 -0
  79. package/dist/client/chat/message-components.js +9 -1
  80. package/dist/client/chat/tool-call-display.d.ts +8 -0
  81. package/dist/client/chat/tool-call-display.js +30 -21
  82. package/dist/client/chat-first/app-pane.js +1 -1
  83. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  84. package/dist/client/chat-first/apps-rail.js +12 -12
  85. package/dist/client/chat-first/chat-history.d.ts +1 -1
  86. package/dist/client/chat-first/chat-history.js +2 -0
  87. package/dist/client/chat-first/copy.js +1 -0
  88. package/dist/client/chat-first/primary-nav.js +1 -1
  89. package/dist/client/chat-first/types.d.ts +1 -0
  90. package/dist/client/resources/McpConnectionSuggestion.js +2 -2
  91. package/dist/client/settings/SettingsTabsPage.d.ts +3 -1
  92. package/dist/client/settings/SettingsTabsPage.js +2 -2
  93. package/dist/client/sse-event-processor.d.ts +8 -0
  94. package/dist/client/sse-event-processor.js +13 -6
  95. package/dist/collab/struct-routes.d.ts +1 -1
  96. package/dist/deploy/build.d.ts +5 -7
  97. package/dist/deploy/build.js +19 -12
  98. package/dist/localization/default-messages.js +1 -1
  99. package/dist/observability/routes.d.ts +3 -3
  100. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  101. package/dist/scripts/call-agent.js +3 -3
  102. package/dist/scripts/describe-workspace-apps.js +1 -1
  103. package/dist/secrets/routes.d.ts +6 -6
  104. package/dist/server/agent-capabilities.js +0 -0
  105. package/dist/server/agent-chat/context-tools.js +1 -1
  106. package/dist/server/agent-chat/prompt-resources.js +1 -1
  107. package/dist/server/agent-chat-plugin.js +1 -1
  108. package/dist/server/auth.js +190 -42
  109. package/dist/server/better-auth-migrations.js +53 -0
  110. package/dist/server/http-response-telemetry.js +7 -1
  111. package/dist/server/onboarding-html.js +119 -33
  112. package/dist/server/transcribe-voice.d.ts +1 -1
  113. package/dist/shared/password-policy.d.ts +2 -1
  114. package/dist/shared/password-policy.js +2 -1
  115. package/dist/styles/agent-conversation.css +20 -3
  116. package/dist/styles/agent-native.css +14 -2
  117. package/dist/styles/blocks.css +132 -0
  118. package/dist/templates/default/app/i18n/ar-SA.ts +1 -1
  119. package/dist/templates/default/app/i18n/de-DE.ts +1 -1
  120. package/dist/templates/default/app/i18n/en-US.ts +1 -1
  121. package/dist/templates/default/app/i18n/es-ES.ts +1 -1
  122. package/dist/templates/default/app/i18n/fr-FR.ts +1 -1
  123. package/dist/templates/default/app/i18n/hi-IN.ts +1 -1
  124. package/dist/templates/default/app/i18n/ja-JP.ts +1 -1
  125. package/dist/templates/default/app/i18n/ko-KR.ts +1 -1
  126. package/dist/templates/default/app/i18n/pt-BR.ts +1 -1
  127. package/dist/templates/default/app/i18n/zh-CN.ts +1 -1
  128. package/dist/templates/default/app/i18n/zh-TW.ts +1 -1
  129. package/dist/templates/default/pnpm-workspace.yaml +1 -0
  130. package/dist/templates/workspace-root/pnpm-workspace.yaml +2 -0
  131. package/docs/content/actions.mdx +4 -1
  132. package/docs/content/agent-surfaces.mdx +8 -6
  133. package/docs/content/automations.mdx +1 -1
  134. package/docs/content/cloneable-saas.mdx +6 -6
  135. package/docs/content/database.mdx +6 -1
  136. package/docs/content/deployment.mdx +10 -8
  137. package/docs/content/docs-components.mdx +22 -14
  138. package/docs/content/drop-in-agent.mdx +1 -1
  139. package/docs/content/environment-variables.mdx +4 -3
  140. package/docs/content/getting-started-actions.mdx +5 -4
  141. package/docs/content/getting-started-pages.mdx +7 -6
  142. package/docs/content/getting-started.mdx +4 -3
  143. package/docs/content/notifications.mdx +6 -6
  144. package/docs/content/progress.mdx +5 -5
  145. package/docs/content/routing.mdx +3 -1
  146. package/docs/content/server-database.mdx +13 -4
  147. package/docs/content/server-overview.mdx +9 -9
  148. package/docs/content/server-plugins.mdx +10 -4
  149. package/docs/content/server-routes.mdx +7 -2
  150. package/docs/content/tracking.mdx +4 -4
  151. package/docs/content/what-is-agent-native.mdx +4 -4
  152. package/package.json +2 -2
  153. package/src/templates/default/app/i18n/ar-SA.ts +1 -1
  154. package/src/templates/default/app/i18n/de-DE.ts +1 -1
  155. package/src/templates/default/app/i18n/en-US.ts +1 -1
  156. package/src/templates/default/app/i18n/es-ES.ts +1 -1
  157. package/src/templates/default/app/i18n/fr-FR.ts +1 -1
  158. package/src/templates/default/app/i18n/hi-IN.ts +1 -1
  159. package/src/templates/default/app/i18n/ja-JP.ts +1 -1
  160. package/src/templates/default/app/i18n/ko-KR.ts +1 -1
  161. package/src/templates/default/app/i18n/pt-BR.ts +1 -1
  162. package/src/templates/default/app/i18n/zh-CN.ts +1 -1
  163. package/src/templates/default/app/i18n/zh-TW.ts +1 -1
  164. package/src/templates/default/pnpm-workspace.yaml +1 -0
  165. package/src/templates/workspace-root/pnpm-workspace.yaml +2 -0
@@ -75,6 +75,7 @@ function prepareToolStackExitClone(clone, snapshot) {
75
75
  export function ToolCallStackMotion({ children, className, }) {
76
76
  const stackRef = useRef(null);
77
77
  const previousRef = useRef(new Map());
78
+ const previousStructureRef = useRef([]);
78
79
  const activeMotionsRef = useRef(new Map());
79
80
  const exitAnimationsRef = useRef(new Map());
80
81
  useLayoutEffect(() => {
@@ -104,6 +105,11 @@ export function ToolCallStackMotion({ children, className, }) {
104
105
  }
105
106
  exitAnimationsRef.current.clear();
106
107
  const elements = Array.from(stack.querySelectorAll("[data-agent-tool-call-id], [data-agent-tool-summary]"));
108
+ const structure = elements.map((element, index) => toolStackMotionKey(element, index));
109
+ const previousStructure = previousStructureRef.current;
110
+ const structureChanged = structure.length !== previousStructure.length ||
111
+ structure.some((key, index) => key !== previousStructure[index]);
112
+ previousStructureRef.current = structure;
107
113
  const current = new Map();
108
114
  for (const [index, element] of elements.entries()) {
109
115
  const rect = element.getBoundingClientRect();
@@ -116,7 +122,11 @@ export function ToolCallStackMotion({ children, className, }) {
116
122
  });
117
123
  }
118
124
  previousRef.current = current;
119
- if (previous.size === 0 || prefersReducedMotionForToolStack())
125
+ // Entry motion changes the measured transform without changing the stack
126
+ // structure. Do not turn that animation's cleanup into a second FLIP pass.
127
+ if (previous.size === 0 ||
128
+ !structureChanged ||
129
+ prefersReducedMotionForToolStack())
120
130
  return;
121
131
  const summary = elements.find((element) => element.dataset.agentToolSummary !== undefined);
122
132
  const summaryRect = summary?.getBoundingClientRect();
@@ -227,13 +237,6 @@ export function ToolActivityPresentation({ toolName, isRunning, isActiveTail, to
227
237
  // Presentation follows the active chat tail rather than execution state so
228
238
  // that newly revealed completed tools still get their entrance motion.
229
239
  const [animateEntry, setAnimateEntry] = useState(isActiveTail);
230
- const previousActiveTailRef = useRef(isActiveTail);
231
- useEffect(() => {
232
- if (isActiveTail && !previousActiveTailRef.current) {
233
- setAnimateEntry(true);
234
- }
235
- previousActiveTailRef.current = isActiveTail;
236
- }, [isActiveTail]);
237
240
  useEffect(() => {
238
241
  if (!animateEntry)
239
242
  return;
@@ -438,29 +441,35 @@ export function AnimatedCollapse({ open, children, }) {
438
441
  * turn. Approve re-issues the turn with the call's `approvalKey`; Deny dismisses
439
442
  * the prompt locally (the action stays un-run).
440
443
  */
441
- function ApprovalAffordance({ toolName, approval, }) {
444
+ function ApprovalAffordance({ toolName, toolCallId, approval, }) {
442
445
  const ctx = React.useContext(ApprovalContext);
443
- const [approved, setApproved] = useState(false);
444
- const [denied, setDenied] = useState(false);
445
- // Once approved, the turn is re-issued; collapse to a quiet note so the user
446
- // can't double-fire the approval.
447
- if (approved) {
446
+ const [localResolution, setLocalResolution] = useState(null);
447
+ const retainedResolution = ctx?.getApprovalResolution?.(approval.approvalKey, toolCallId) ?? null;
448
+ const resolution = retainedResolution ??
449
+ localResolution ??
450
+ (approval.dismissed === true ? "denied" : null);
451
+ // Once resolved, collapse to a quiet note so a repository refresh cannot
452
+ // restore the action buttons while the continuation is running.
453
+ if (resolution === "approved") {
448
454
  return (_jsxs("div", { className: "mt-1.5 text-xs text-muted-foreground", children: ["Approved. Re-running ", toolName, "..."] }));
449
455
  }
450
456
  // Deny defaults to local-only (the action simply stays un-run). When the
451
457
  // host also provided `onDeny` (e.g. a Code session resolving its own
452
458
  // pending approval), it fires alongside the local state.
453
- if (denied) {
459
+ if (resolution === "denied") {
454
460
  return (_jsxs("div", { className: "mt-1.5 text-xs text-muted-foreground", children: ["Denied. ", toolName, " did not run."] }));
455
461
  }
456
462
  return (_jsxs("div", { className: "mt-1.5 flex items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5", children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsxs("span", { className: "mr-auto text-xs text-muted-foreground", children: ["Approve to run ", toolName, "?"] }), ctx && (_jsxs("button", { type: "button", onClick: () => {
457
- setApproved(true);
463
+ setLocalResolution("approved");
464
+ ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId);
458
465
  ctx.onApprove(approval.approvalKey);
459
466
  }, className: cn("inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: [_jsx(IconCheck, { className: "h-3.5 w-3.5" }), "Approve"] })), ctx?.onAlwaysAllow && (_jsxs("button", { type: "button", onClick: () => {
460
- setApproved(true);
467
+ setLocalResolution("approved");
468
+ ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId);
461
469
  ctx.onAlwaysAllow?.(approval.approvalKey);
462
470
  }, title: "Approve and always allow this exact command", className: cn("inline-flex items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5" }), "Always allow"] })), _jsxs("button", { type: "button", onClick: () => {
463
- setDenied(true);
471
+ setLocalResolution("denied");
472
+ ctx?.onApprovalResolved?.(approval.approvalKey, "denied", toolCallId);
464
473
  ctx?.onDeny?.(approval.approvalKey);
465
474
  }, className: cn("inline-flex items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconX, { className: "h-3.5 w-3.5" }), "Deny"] })] }));
466
475
  }
@@ -481,9 +490,9 @@ export function ToolCallDisplay({ toolName, toolCallId, argsText, args, result,
481
490
  if (toolKind === "write") {
482
491
  return wrapToolDisplay(_jsx(WriteCell, { meta: structuredMeta, isRunning: isRunning }));
483
492
  }
484
- return wrapToolDisplay(_jsx(ToolCallDisplayGeneric, { toolName: toolName, argsText: argsText, args: args, result: result, mcpApp: mcpApp, chatUI: chatUI, isRunning: isRunning, outcome: outcome, isActiveTail: showActiveTail, structuredMeta: structuredMeta, approval: approval, repeatCount: repeatCount }));
493
+ return wrapToolDisplay(_jsx(ToolCallDisplayGeneric, { toolName: toolName, toolCallId: toolCallId, argsText: argsText, args: args, result: result, mcpApp: mcpApp, chatUI: chatUI, isRunning: isRunning, outcome: outcome, isActiveTail: showActiveTail, structuredMeta: structuredMeta, approval: approval, repeatCount: repeatCount }));
485
494
  }
486
- function ToolCallDisplayGeneric({ toolName, argsText, args, result, mcpApp, chatUI, isRunning, outcome, isActiveTail, structuredMeta, approval, repeatCount, }) {
495
+ function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result, mcpApp, chatUI, isRunning, outcome, isActiveTail, structuredMeta, approval, repeatCount, }) {
487
496
  const isRawCallAgent = toolName === "call-agent";
488
497
  const isAgentCall = toolName.startsWith("agent:") || isRawCallAgent;
489
498
  const [expanded, setExpanded] = useState(isAgentCall);
@@ -585,7 +594,7 @@ function ToolCallDisplayGeneric({ toolName, argsText, args, result, mcpApp, chat
585
594
  ? structuredMeta.agentDurationMs
586
595
  : agentActivity?.durationMs }));
587
596
  }
588
- return (_jsxs("div", { className: "group/tool my-0.5 w-full overflow-hidden", children: [mcpApp && _jsx(McpAppRenderer, { app: mcpApp, className: "mb-1.5" }), _jsxs("button", { type: "button", onClick: () => canExpand && setExpanded(!isExpanded), "aria-expanded": canExpand ? isExpanded : undefined, className: cn("flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-[13px] text-muted-foreground transition-colors", canExpand && "hover:text-foreground", isRunning && "text-muted-foreground"), children: [_jsx("span", { className: "relative flex size-4 shrink-0 items-center justify-center", children: isRunning ? (_jsx(IconLoader2, { className: "size-3.5 animate-spin" })) : isAgentError ? (_jsx(IconCircleX, { className: "size-3.5 text-destructive" })) : isUnknownOutcome ? (_jsx(IconAlertTriangle, { className: "size-3.5 text-muted-foreground" })) : (_jsxs(_Fragment, { children: [_jsx(ToolIcon, { className: cn("size-3.5 transition-opacity", canExpand && "group-hover/tool:opacity-0") }), canExpand && (_jsx(IconChevronRight, { className: cn("absolute size-3.5 opacity-0 transition-[opacity,transform] group-hover/tool:opacity-100", isExpanded && "rotate-90") }))] })) }), _jsx("span", { className: cn("min-w-0 truncate font-normal", isActiveTail && "agent-running-shimmer"), children: displayName }), repeatCount && repeatCount > 1 && (_jsxs("span", { className: "shrink-0 rounded border border-border/60 px-1.5 py-0.5 text-[10px] leading-none text-muted-foreground", title: `Repeated ${repeatCount} times`, children: [repeatCount, "x"] }))] }), _jsx(AnimatedCollapse, { open: isExpanded && !isAgentCall && (hasArgs || result !== undefined), children: _jsxs("div", { className: "mt-1 space-y-2 pl-5", children: [inputPayload && (_jsx(SimpleCodeViewer, { text: inputPayload.text, lang: inputPayload.lang })), resultPayload && (_jsx(ToolOutputPopover, { open: outputOpen, onOpenChange: setOutputOpen, title: outputTitle, payload: resultPayload, children: _jsx("button", { type: "button", "aria-label": `View ${toolName} output`, className: "inline-flex size-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:bg-accent hover:text-foreground", children: _jsx(IconCode, { className: "size-3.5" }) }) }))] }) }), isUnknownOutcome && (_jsx("p", { role: "status", className: "ps-5 text-xs text-muted-foreground", children: "Interrupted before this finished reporting \u2014 it may or may not have completed. Check before retrying." })), approval && (_jsx(ApprovalAffordance, { toolName: toolName, approval: approval }))] }));
597
+ return (_jsxs("div", { className: "group/tool my-0.5 w-full overflow-hidden", children: [mcpApp && _jsx(McpAppRenderer, { app: mcpApp, className: "mb-1.5" }), _jsxs("button", { type: "button", onClick: () => canExpand && setExpanded(!isExpanded), "aria-expanded": canExpand ? isExpanded : undefined, className: cn("flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-[13px] text-muted-foreground transition-colors", canExpand && "hover:text-foreground", isRunning && "text-muted-foreground"), children: [_jsx("span", { className: "relative flex size-4 shrink-0 items-center justify-center", children: isRunning ? (_jsx(IconLoader2, { className: "size-3.5 animate-spin" })) : isAgentError ? (_jsx(IconCircleX, { className: "size-3.5 text-destructive" })) : isUnknownOutcome ? (_jsx(IconAlertTriangle, { className: "size-3.5 text-muted-foreground" })) : (_jsxs(_Fragment, { children: [_jsx(ToolIcon, { className: cn("size-3.5 transition-opacity", canExpand && "group-hover/tool:opacity-0") }), canExpand && (_jsx(IconChevronRight, { className: cn("absolute size-3.5 opacity-0 transition-[opacity,transform] group-hover/tool:opacity-100", isExpanded && "rotate-90") }))] })) }), _jsx("span", { className: cn("min-w-0 truncate font-normal", isActiveTail && "agent-running-shimmer"), children: displayName }), repeatCount && repeatCount > 1 && (_jsxs("span", { className: "shrink-0 rounded border border-border/60 px-1.5 py-0.5 text-[10px] leading-none text-muted-foreground", title: `Repeated ${repeatCount} times`, children: [repeatCount, "x"] }))] }), _jsx(AnimatedCollapse, { open: isExpanded && !isAgentCall && (hasArgs || result !== undefined), children: _jsxs("div", { className: "mt-1 space-y-2 pl-5", children: [inputPayload && (_jsx(SimpleCodeViewer, { text: inputPayload.text, lang: inputPayload.lang })), resultPayload && (_jsx(ToolOutputPopover, { open: outputOpen, onOpenChange: setOutputOpen, title: outputTitle, payload: resultPayload, children: _jsx("button", { type: "button", "aria-label": `View ${toolName} output`, className: "inline-flex size-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:bg-accent hover:text-foreground", children: _jsx(IconCode, { className: "size-3.5" }) }) }))] }) }), isUnknownOutcome && (_jsx("p", { role: "status", className: "ps-5 text-xs text-muted-foreground", children: "Interrupted before this finished reporting \u2014 it may or may not have completed. Check before retrying." })), approval && (_jsx(ApprovalAffordance, { toolName: toolName, toolCallId: toolCallId, approval: approval }))] }));
589
598
  }
590
599
  function AgentCallCell({ agentName, activity, progress, responseText, isRunning, isError, durationMs, }) {
591
600
  const t = useT();
@@ -2,5 +2,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Skeleton } from "@agent-native/toolkit/ui";
3
3
  import { defaultChatFirstCopy } from "./copy.js";
4
4
  export function ChatFirstAppPane({ app, status, embedUrl, errorMessage, onRetry, renderEmbed, copy = defaultChatFirstCopy, }) {
5
- return (_jsx("section", { "data-chat-first-app-pane": true, className: "flex h-full min-h-0 w-full flex-col overflow-hidden bg-background", "aria-label": app ? `${app.name} app` : copy("appUnavailable"), children: status === "loading" ? (_jsxs("div", { className: "space-y-3 p-4", children: [_jsx(Skeleton, { className: "h-5 w-40" }), _jsx(Skeleton, { className: "h-4 w-full" }), _jsx(Skeleton, { className: "h-4 w-2/3" })] })) : status === "ready" && app && embedUrl ? (_jsx("div", { className: "relative min-h-0 flex-1 overflow-hidden", children: renderEmbed({ url: embedUrl, title: app.name }) })) : status === "error" ? (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 p-6 text-center", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: errorMessage || copy("appUnavailable") }), onRetry ? (_jsx("button", { type: "button", className: "rounded-md border border-border px-3 py-1.5 text-xs text-foreground hover:bg-accent", onClick: onRetry, children: copy("retry") })) : null] })) : (_jsx("div", { className: "flex h-full items-center justify-center p-6 text-center text-sm text-muted-foreground", children: copy("appUnavailable") })) }));
5
+ return (_jsx("section", { "data-chat-first-app-pane": true, className: "flex h-full min-h-0 w-full flex-col overflow-hidden bg-background", "aria-label": app ? `${app.name} app` : copy("appUnavailable"), children: status === "loading" ? (_jsxs("div", { className: "space-y-3 p-4", children: [_jsx(Skeleton, { className: "h-5 w-40" }), _jsx(Skeleton, { className: "h-4 w-full" }), _jsx(Skeleton, { className: "h-4 w-2/3" })] })) : status === "ready" && app && embedUrl ? (_jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: renderEmbed({ url: embedUrl, title: app.name }) })) : status === "error" ? (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 p-6 text-center", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: errorMessage || copy("appUnavailable") }), onRetry ? (_jsx("button", { type: "button", className: "rounded-md border border-border px-3 py-1.5 text-xs text-foreground hover:bg-accent", onClick: onRetry, children: copy("retry") })) : null] })) : (_jsx("div", { className: "flex h-full items-center justify-center p-6 text-center text-sm text-muted-foreground", children: copy("appUnavailable") })) }));
6
6
  }
@@ -1,2 +1,2 @@
1
1
  import type { ChatFirstAppRailProps } from "./types.js";
2
- export declare function ChatFirstAppsRail({ apps, activeAppId, loading, error, collapsed, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onCreateApp, createAppTrigger, renderIcon, copy, }: ChatFirstAppRailProps): import("react").JSX.Element;
2
+ export declare const ChatFirstAppsRail: import("react").MemoExoticComponent<({ apps, activeAppId, loading, error, collapsed, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy, }: ChatFirstAppRailProps) => import("react").JSX.Element>;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger, Skeleton, Tooltip, TooltipContent, TooltipTrigger, } from "@agent-native/toolkit/ui";
3
- import { IconChevronDown, IconChevronUp, IconPin, IconPlus, } from "@tabler/icons-react";
4
- import { useMemo, useState } from "react";
3
+ import { IconApps, IconChevronDown, IconChevronUp, IconPin, IconPlus, } from "@tabler/icons-react";
4
+ import { memo, useMemo, useState } from "react";
5
5
  import { orderChatFirstAppIds, readChatFirstAppLayout, writeChatFirstAppLayout, } from "../chat-first.js";
6
6
  import { cn } from "../utils.js";
7
7
  import { defaultChatFirstCopy } from "./copy.js";
@@ -11,16 +11,16 @@ function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, on
11
11
  const orderedApps = orderedIds
12
12
  .map((id) => appsById.get(id))
13
13
  .filter((app) => Boolean(app));
14
- return (_jsx("ul", { className: "space-y-px", children: orderedApps.map((app) => {
14
+ return (_jsx("ul", { className: "space-y-1", children: orderedApps.map((app) => {
15
15
  const active = activeAppId === app.id;
16
16
  const pinned = layout.pinnedIds.includes(app.id);
17
17
  const index = orderedApps.indexOf(app);
18
- return (_jsxs(ContextMenu, { children: [_jsx(ContextMenuTrigger, { asChild: true, children: _jsxs("li", { draggable: true, "data-chat-first-app": true, "data-app-id": app.id, className: cn("group flex h-6 w-full min-w-0 items-center gap-1 rounded-md px-1 text-sm transition-colors", active
18
+ return (_jsxs(ContextMenu, { children: [_jsx(ContextMenuTrigger, { asChild: true, children: _jsxs("li", { draggable: true, "data-chat-first-app": true, "data-app-id": app.id, className: cn("group flex h-8 w-full min-w-0 items-center gap-1 rounded-md px-1 text-sm", active
19
19
  ? "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
20
20
  : "text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onDragStart: (event) => {
21
21
  event.dataTransfer.effectAllowed = "move";
22
22
  onDragStart(app.id);
23
- }, onDragOver: (event) => event.preventDefault(), onDrop: () => onDrop(app.id), onDragEnd: onDragEnd, children: [_jsxs("button", { type: "button", className: "flex min-w-0 flex-1 items-center gap-2 px-1 text-start", onClick: () => onOpenApp(app), onKeyDown: (event) => {
23
+ }, onDragOver: (event) => event.preventDefault(), onDrop: () => onDrop(app.id), onDragEnd: onDragEnd, children: [_jsxs("button", { type: "button", className: "flex h-full min-w-0 flex-1 items-center gap-2 px-1 text-start", onClick: () => onOpenApp(app), onKeyDown: (event) => {
24
24
  if (!event.altKey)
25
25
  return;
26
26
  if (event.key === "ArrowUp") {
@@ -31,12 +31,12 @@ function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, on
31
31
  event.preventDefault();
32
32
  onMove(app.id, 1);
33
33
  }
34
- }, "aria-keyshortcuts": "Alt+ArrowUp Alt+ArrowDown", "aria-label": copy("openApp", { name: app.name }), title: app.name, children: [renderIcon(app), _jsx("span", { className: "truncate", children: app.name })] }), _jsx("button", { type: "button", className: cn("flex size-6 shrink-0 items-center justify-center rounded text-sidebar-foreground/45 opacity-0 transition-opacity hover:bg-sidebar-accent hover:text-sidebar-foreground group-hover:opacity-100 focus-visible:opacity-100", pinned && "text-sidebar-foreground/70 opacity-100"), "aria-label": copy(pinned ? "unpinApp" : "pinApp", {
34
+ }, "aria-keyshortcuts": "Alt+ArrowUp Alt+ArrowDown", "aria-label": copy("openApp", { name: app.name }), children: [renderIcon(app), _jsx("span", { className: "truncate", children: app.name })] }), _jsx("button", { type: "button", className: cn("flex size-6 shrink-0 items-center justify-center rounded text-sidebar-foreground/45 opacity-0 hover:bg-sidebar-accent hover:text-sidebar-foreground group-hover:opacity-100 focus-visible:opacity-100", pinned && "text-sidebar-foreground/70 opacity-100"), "aria-label": copy(pinned ? "unpinApp" : "pinApp", {
35
35
  name: app.name,
36
- }), "aria-pressed": pinned, title: pinned ? copy("removePinned") : copy("pinTop"), onClick: () => onTogglePinned(app.id), children: _jsx(IconPin, { size: 13, strokeWidth: pinned ? 2.2 : 1.6, "aria-hidden": "true" }) }), _jsx("span", { className: "sr-only", children: `${index + 1} of ${orderedApps.length}` })] }) }), _jsxs(ContextMenuContent, { children: [_jsxs(ContextMenuItem, { onSelect: () => onTogglePinned(app.id), children: [_jsx(IconPin, { size: 14, "aria-hidden": "true" }), pinned ? copy("removePinned") : copy("pinTop")] }), _jsx(ContextMenuSeparator, {}), _jsxs(ContextMenuItem, { disabled: index === 0, onSelect: () => onMove(app.id, -1), children: [_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" }), copy("moveUp")] }), _jsxs(ContextMenuItem, { disabled: index === orderedApps.length - 1, onSelect: () => onMove(app.id, 1), children: [_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" }), copy("moveDown")] })] })] }, app.id));
36
+ }), "aria-pressed": pinned, onClick: () => onTogglePinned(app.id), children: _jsx(IconPin, { size: 13, strokeWidth: pinned ? 2.2 : 1.6, "aria-hidden": "true" }) }), _jsx("span", { className: "sr-only", children: `${index + 1} of ${orderedApps.length}` })] }) }), _jsxs(ContextMenuContent, { children: [_jsxs(ContextMenuItem, { onSelect: () => onTogglePinned(app.id), children: [_jsx(IconPin, { size: 14, "aria-hidden": "true" }), pinned ? copy("removePinned") : copy("pinTop")] }), _jsx(ContextMenuSeparator, {}), _jsxs(ContextMenuItem, { disabled: index === 0, onSelect: () => onMove(app.id, -1), children: [_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" }), copy("moveUp")] }), _jsxs(ContextMenuItem, { disabled: index === orderedApps.length - 1, onSelect: () => onMove(app.id, 1), children: [_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" }), copy("moveDown")] })] })] }, app.id));
37
37
  }) }));
38
38
  }
39
- export function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, collapsed = false, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onCreateApp, createAppTrigger, renderIcon, copy = defaultChatFirstCopy, }) {
39
+ export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, collapsed = false, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy = defaultChatFirstCopy, }) {
40
40
  const [localLayout, setLocalLayout] = useState(() => readChatFirstAppLayout());
41
41
  const layout = controlledLayout ?? localLayout;
42
42
  const [draggedAppId, setDraggedAppId] = useState(null);
@@ -95,9 +95,9 @@ export function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, c
95
95
  if (collapsed) {
96
96
  return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "flex flex-col items-center gap-1 px-1.5 pt-2", "aria-label": copy("workspaceApps"), children: [createTrigger, loading && apps.length === 0
97
97
  ? [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "size-9 rounded-md" }, index)))
98
- : visibleApps.map((app) => (_jsx("button", { type: "button", "data-chat-first-app": true, "data-app-id": app.id, className: cn("flex size-9 items-center justify-center rounded-md transition-colors", activeAppId === app.id
98
+ : visibleApps.map((app) => (_jsx("button", { type: "button", "data-chat-first-app": true, "data-app-id": app.id, className: cn("flex size-9 items-center justify-center rounded-md", activeAppId === app.id
99
99
  ? "bg-sidebar-accent text-sidebar-accent-foreground"
100
- : "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onClick: () => onOpenApp(app), "aria-label": copy("openApp", { name: app.name }), title: app.name, children: renderIcon(app) }, app.id))), error ? (_jsx("span", { className: "size-1.5 rounded-full bg-destructive", title: error, "aria-label": error })) : null] }));
100
+ : "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onClick: () => onOpenApp(app), "aria-label": copy("openApp", { name: app.name }), title: app.name, children: renderIcon(app) }, app.id))), onOpenAllApps ? (_jsx("button", { type: "button", "data-chat-first-all-apps": true, className: "flex size-9 items-center justify-center rounded-md text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", onClick: onOpenAllApps, "aria-label": copy("allApps"), title: copy("allApps"), children: _jsx(IconApps, { size: 16, "aria-hidden": "true" }) })) : null, error ? (_jsx("span", { className: "size-1.5 rounded-full bg-destructive", title: error, "aria-label": error })) : null] }));
101
101
  }
102
- return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-2 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-1 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-1", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-7 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-1", children: [_jsx("p", { className: "text-xs text-sidebar-foreground/55", children: copy("noWorkspaceApps") }), createAppTrigger ? (createTrigger) : onCreateApp ? (_jsxs("button", { type: "button", className: "mt-1 inline-flex h-6 items-center gap-1.5 rounded-md border border-sidebar-border px-2 text-xs text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, children: [_jsx(IconPlus, { size: 13, "aria-hidden": "true" }), copy("createApp")] })) : (createTrigger)] })) : (_jsx(AppRows, { apps: visibleApps, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onTogglePinned: togglePinned, onMove: moveApp, renderIcon: renderIcon, copy: copy })), hasMoreApps || showAllApps ? (_jsxs("button", { type: "button", className: "mt-0.5 flex h-6 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: () => setShowAllApps((value) => !value), children: [showAllApps ? (_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" })) : (_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" })), showAllApps ? copy("showLess") : copy("showMore")] })) : null, error ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "mt-1 flex items-center gap-1 px-1 text-[10px] text-destructive/80", onClick: onRetry, "aria-label": error, children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), onRetry ? copy("retry") : copy("appsLoadError")] }) }), _jsx(TooltipContent, { side: "right", children: error })] })) : null] }));
103
- }
102
+ return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-2 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-1 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-1", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-8 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-1", children: [_jsx("p", { className: "text-xs text-sidebar-foreground/55", children: copy("noWorkspaceApps") }), createAppTrigger ? (createTrigger) : onCreateApp ? (_jsxs("button", { type: "button", className: "mt-1 inline-flex h-6 items-center gap-1.5 rounded-md border border-sidebar-border px-2 text-xs text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, children: [_jsx(IconPlus, { size: 13, "aria-hidden": "true" }), copy("createApp")] })) : (createTrigger)] })) : (_jsx(AppRows, { apps: visibleApps, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onTogglePinned: togglePinned, onMove: moveApp, renderIcon: renderIcon, copy: copy })), hasMoreApps || showAllApps ? (_jsxs("button", { type: "button", className: "mt-0.5 flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: () => setShowAllApps((value) => !value), children: [showAllApps ? (_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" })) : (_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" })), showAllApps ? copy("showLess") : copy("showMore")] })) : null, onOpenAllApps ? (_jsx("div", { className: "mt-1 border-t border-sidebar-border/60 pt-1", children: _jsxs("button", { type: "button", "data-chat-first-all-apps": true, className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onOpenAllApps, children: [_jsx(IconApps, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: copy("allApps") })] }) })) : null, error ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "mt-1 flex items-center gap-1 px-1 text-[10px] text-destructive/80", onClick: onRetry, "aria-label": error, children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), onRetry ? copy("retry") : copy("appsLoadError")] }) }), _jsx(TooltipContent, { side: "right", children: error })] })) : null] }));
103
+ });
@@ -9,4 +9,4 @@ export interface ChatFirstChatHistoryProps extends Omit<ChatHistoryListProps, "i
9
9
  * host. Hosts still own thread fetching and actions, but row rhythm and the
10
10
  * Chats section treatment stay identical.
11
11
  */
12
- export declare function ChatFirstChatHistory({ items, label, className, ...props }: ChatFirstChatHistoryProps): import("react").JSX.Element;
12
+ export declare function ChatFirstChatHistory({ items, label, className, ...props }: ChatFirstChatHistoryProps): import("react").JSX.Element | null;
@@ -6,5 +6,7 @@ import { ChatHistoryList, } from "../chat/ChatHistoryList.js";
6
6
  * Chats section treatment stay identical.
7
7
  */
8
8
  export function ChatFirstChatHistory({ items, label = "Chats", className, ...props }) {
9
+ if (items.length === 0)
10
+ return null;
9
11
  return (_jsxs("section", { "data-chat-first-chat-history": true, className: ["min-h-0 min-w-0", className].filter(Boolean).join(" "), children: [_jsx("p", { className: "mb-1 px-2 text-[11px] font-medium text-sidebar-foreground/50", children: label }), _jsx(ChatHistoryList, { ...props, items: items, variant: "rail", className: "min-w-0" })] }));
10
12
  }
@@ -2,6 +2,7 @@ const DEFAULT_COPY = {
2
2
  newChat: "New chat",
3
3
  search: "Search",
4
4
  workspaceApps: "Apps",
5
+ allApps: "All apps",
5
6
  integrations: "Integrations",
6
7
  scheduled: "Scheduled",
7
8
  createWorkspaceApp: "Create app",
@@ -2,5 +2,5 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
2
2
  import { IconClock, IconPlus, IconPlugConnected, IconSearch, } from "@tabler/icons-react";
3
3
  import { defaultChatFirstCopy } from "./copy.js";
4
4
  export function ChatFirstPrimaryNavigation({ onNewChat, onOpenIntegrations, onOpenScheduled, onSearch, copy = defaultChatFirstCopy, }) {
5
- return (_jsxs(_Fragment, { children: [onNewChat ? (_jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onNewChat, children: [_jsx(IconPlus, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("newChat") })] })) : null, _jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onOpenIntegrations, children: [_jsx(IconPlugConnected, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("integrations") })] }), _jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onOpenScheduled, children: [_jsx(IconClock, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("scheduled") })] }), onSearch ? (_jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onSearch, children: [_jsx(IconSearch, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("search") })] })) : null] }));
5
+ return (_jsxs(_Fragment, { children: [onNewChat ? (_jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onNewChat, children: [_jsx(IconPlus, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("newChat") })] })) : null, _jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onOpenIntegrations, children: [_jsx(IconPlugConnected, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("integrations") })] }), _jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onOpenScheduled, children: [_jsx(IconClock, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("scheduled") })] }), onSearch ? (_jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onSearch, children: [_jsx(IconSearch, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("search") })] })) : null] }));
6
6
  }
@@ -44,6 +44,7 @@ export interface ChatFirstAppRailProps {
44
44
  onLayoutError?: (reason: "unavailable" | "write-failed") => void;
45
45
  onRetry?: () => void;
46
46
  onOpenApp: (app: ChatFirstAppItem) => void;
47
+ onOpenAllApps?: () => void;
47
48
  onCreateApp?: () => void;
48
49
  createAppTrigger?: ReactNode;
49
50
  renderIcon: (app: ChatFirstAppItem) => ReactNode;
@@ -145,7 +145,7 @@ export function McpConnectionSuggestion({ text, contextText = "", variant = "com
145
145
  : t("mcpIntegrations.connect");
146
146
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: variant === "response"
147
147
  ? "agent-mcp-connection-suggestion agent-mcp-connection-suggestion--response mt-3 flex max-w-[520px] items-center gap-2 rounded-lg border border-primary/20 bg-primary/5 px-3 py-2 text-[12px]"
148
- : "agent-mcp-connection-suggestion agent-mcp-connection-suggestion--composer mx-auto mb-2 flex w-[min(calc(100%_-_1.5rem),684px)] items-center gap-2 rounded-lg border border-primary/20 bg-primary/5 px-3 py-2 text-[12px]", "data-mcp-connection-suggestion": integration.id, "data-mcp-connection-suggestion-variant": variant, children: [_jsx(McpIntegrationLogo, { name: integration.name, logoUrl: integration.logoUrl, integrationId: integration.id, className: "size-6 rounded-md text-[10px]", imageClassName: "size-5" }), _jsx("span", { className: "min-w-0 flex-1 leading-snug text-foreground", children: t(hasApiFallback(apiFallback)
148
+ : "agent-mcp-connection-suggestion agent-mcp-connection-suggestion--composer mx-auto mb-2 flex w-[min(calc(100%_-_1.5rem),750px)] items-center gap-2 rounded-lg border border-primary/20 bg-primary/5 px-3 py-2 text-[12px]", "data-mcp-connection-suggestion": integration.id, "data-mcp-connection-suggestion-variant": variant, children: [_jsx(McpIntegrationLogo, { name: integration.name, logoUrl: integration.logoUrl, integrationId: integration.id, className: "size-6 rounded-md text-[10px]", imageClassName: "size-5" }), _jsx("span", { className: "min-w-0 flex-1 leading-snug text-foreground", children: t(hasApiFallback(apiFallback)
149
149
  ? "mcpIntegrations.connectSuggestionWithApiToken"
150
150
  : "mcpIntegrations.connectSuggestion", { name: integration.name }) }), _jsxs("button", { type: "button", onClick: () => void connect(), disabled: connecting, "aria-busy": connecting, className: "inline-flex shrink-0 items-center gap-1 rounded-md bg-primary px-2.5 py-1.5 text-[11px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-60", children: [connecting && _jsx(IconLoader2, { className: "h-3 w-3 animate-spin" }), !connecting &&
151
151
  canStartOAuth(integration) &&
@@ -153,7 +153,7 @@ export function McpConnectionSuggestion({ text, contextText = "", variant = "com
153
153
  !canStartOAuth(integration) &&
154
154
  !hasApiFallback(apiFallback) && (_jsx(IconArrowUpRight, { className: "h-3 w-3" }))] }), _jsx("button", { type: "button", onClick: () => setDismissedId(integration.id), className: "inline-flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-background hover:text-foreground", "aria-label": t("mcpIntegrations.dismissSuggestion"), children: _jsx(IconX, { className: "h-3.5 w-3.5" }) })] }), error && (_jsx("div", { className: variant === "response"
155
155
  ? "agent-mcp-connection-suggestion-error agent-mcp-connection-suggestion-error--response mt-1 max-w-[520px] text-[11px] text-destructive"
156
- : "agent-mcp-connection-suggestion-error agent-mcp-connection-suggestion-error--composer mx-auto mb-2 w-[min(calc(100%_-_1.5rem),684px)] text-[11px] text-destructive", children: error })), _jsx(McpIntegrationDialog, { open: dialogOpen, onOpenChange: (open) => {
156
+ : "agent-mcp-connection-suggestion-error agent-mcp-connection-suggestion-error--composer mx-auto mb-2 w-[min(calc(100%_-_1.5rem),750px)] text-[11px] text-destructive", children: error })), _jsx(McpIntegrationDialog, { open: dialogOpen, onOpenChange: (open) => {
157
157
  setDialogOpen(open);
158
158
  if (!open)
159
159
  clearMcpConnectionResume();
@@ -60,6 +60,8 @@ export interface SettingsTabsPageProps {
60
60
  defaultTab?: string;
61
61
  className?: string;
62
62
  navClassName?: string;
63
+ /** Optional content rendered at the top of the settings navigation rail. */
64
+ navHeader?: ReactNode;
63
65
  contentClassName?: string;
64
66
  /** Whether to render the settings search box. Defaults to true. */
65
67
  enableSearch?: boolean;
@@ -83,5 +85,5 @@ export interface SettingsTabsPageProps {
83
85
  */
84
86
  onValueChange?: (tabId: string) => void;
85
87
  }
86
- export declare function SettingsTabsPage({ general, account, team, whatsNew, extraTabs, generalLabel, accountLabel, teamLabel, whatsNewLabel, ariaLabel, defaultTab, className, navClassName, contentClassName, enableSearch, searchPlaceholder, searchEntries, generalSearchEntries, value, onValueChange, }: SettingsTabsPageProps): import("react").JSX.Element;
88
+ export declare function SettingsTabsPage({ general, account, team, whatsNew, extraTabs, generalLabel, accountLabel, teamLabel, whatsNewLabel, ariaLabel, defaultTab, className, navClassName, navHeader, contentClassName, enableSearch, searchPlaceholder, searchEntries, generalSearchEntries, value, onValueChange, }: SettingsTabsPageProps): import("react").JSX.Element;
87
89
  export {};
@@ -118,7 +118,7 @@ function isEditableElement(element) {
118
118
  tagName === "select" ||
119
119
  element.isContentEditable);
120
120
  }
121
- export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs = [], generalLabel = "General", accountLabel = "Account", teamLabel = "Team", whatsNewLabel = "What's new", ariaLabel = "Settings sections", defaultTab = "general", className, navClassName, contentClassName, enableSearch = true, searchPlaceholder = "Search settings", searchEntries, generalSearchEntries, value, onValueChange, }) {
121
+ export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs = [], generalLabel = "General", accountLabel = "Account", teamLabel = "Team", whatsNewLabel = "What's new", ariaLabel = "Settings sections", defaultTab = "general", className, navClassName, navHeader, contentClassName, enableSearch = true, searchPlaceholder = "Search settings", searchEntries, generalSearchEntries, value, onValueChange, }) {
122
122
  const rootRef = useRef(null);
123
123
  const searchInputRef = useRef(null);
124
124
  const autoFocusedSearchRef = useRef(false);
@@ -375,7 +375,7 @@ export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs =
375
375
  }
376
376
  };
377
377
  const searching = enableSearch && trimmedQuery.length > 0;
378
- return (_jsxs("div", { ref: rootRef, className: cn("flex h-full min-h-0 w-full flex-col overflow-hidden bg-background sm:flex-row", className), children: [_jsxs("div", { className: cn("flex shrink-0 flex-col gap-2 bg-background p-2 sm:min-h-0 sm:w-60 sm:overflow-y-auto sm:p-4", navClassName), children: [enableSearch ? (_jsxs("div", { className: "relative sm:mb-2", children: [_jsx(IconSearch, { className: "pointer-events-none absolute start-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { ref: searchInputRef, type: "search", value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: (event) => {
378
+ return (_jsxs("div", { ref: rootRef, className: cn("flex h-full min-h-0 w-full flex-col overflow-hidden bg-background sm:flex-row", className), children: [_jsxs("div", { className: cn("flex shrink-0 flex-col gap-2 bg-background p-2 sm:min-h-0 sm:w-60 sm:overflow-y-auto sm:p-4", navClassName), children: [navHeader, enableSearch ? (_jsxs("div", { className: "relative sm:mb-2", children: [_jsx(IconSearch, { className: "pointer-events-none absolute start-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { ref: searchInputRef, type: "search", value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: (event) => {
379
379
  if (event.key === "Escape")
380
380
  setQuery("");
381
381
  if (event.key === "Enter" && results[0]) {
@@ -188,6 +188,14 @@ export interface SSEStreamOptions {
188
188
  * the tight foreground 75s/90s windows.
189
189
  */
190
190
  durableBackgroundRun?: boolean;
191
+ /**
192
+ * Optional reader-local watchdog override. A background follow reader can
193
+ * use a shorter value because a timeout only detaches that read; the follow
194
+ * loop immediately re-checks the server-owned run state.
195
+ */
196
+ noProgressTimeoutMs?: number;
197
+ /** Reader-local counterpart to `noProgressTimeoutMs` for action preparation. */
198
+ actionPreparationStallTimeoutMs?: number;
191
199
  /**
192
200
  * Optional caller-owned preparation watchdog state. Passing the same object
193
201
  * across reconnect reads keeps a stuck action preparation from getting a
@@ -113,15 +113,22 @@ export function sseInFlightWorkDelta(ev) {
113
113
  */
114
114
  export const SSE_DURABLE_NO_PROGRESS_TIMEOUT_MS = 13 * 60_000;
115
115
  export const SSE_DURABLE_ACTION_PREPARATION_STALL_TIMEOUT_MS = 13 * 60_000;
116
+ function sseTimeoutOverrideMs(value) {
117
+ return typeof value === "number" && Number.isFinite(value) && value >= 0
118
+ ? value
119
+ : undefined;
120
+ }
116
121
  export function sseNoProgressTimeoutMs(options) {
117
- return options?.durableBackgroundRun === true
118
- ? SSE_DURABLE_NO_PROGRESS_TIMEOUT_MS
119
- : SSE_NO_PROGRESS_TIMEOUT_MS;
122
+ return (sseTimeoutOverrideMs(options?.noProgressTimeoutMs) ??
123
+ (options?.durableBackgroundRun === true
124
+ ? SSE_DURABLE_NO_PROGRESS_TIMEOUT_MS
125
+ : SSE_NO_PROGRESS_TIMEOUT_MS));
120
126
  }
121
127
  function sseActionPreparationStallTimeoutMs(options) {
122
- return options?.durableBackgroundRun === true
123
- ? SSE_DURABLE_ACTION_PREPARATION_STALL_TIMEOUT_MS
124
- : SSE_ACTION_PREPARATION_STALL_TIMEOUT_MS;
128
+ return (sseTimeoutOverrideMs(options?.actionPreparationStallTimeoutMs) ??
129
+ (options?.durableBackgroundRun === true
130
+ ? SSE_DURABLE_ACTION_PREPARATION_STALL_TIMEOUT_MS
131
+ : SSE_ACTION_PREPARATION_STALL_TIMEOUT_MS));
125
132
  }
126
133
  function activityProgressBytes(ev) {
127
134
  return typeof ev.progressBytes === "number" &&
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- ok?: undefined;
17
16
  error: string;
17
+ ok?: undefined;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -103,12 +103,9 @@ export declare function isIntegrationDurableDispatchDeployEnabled(): boolean;
103
103
  export declare const NETLIFY_RECURRING_JOBS_FUNCTION_NAME = "agent-native-recurring-jobs";
104
104
  export declare function isRecurringJobsDeployEnabled(): boolean;
105
105
  /**
106
- * Keep-warm is ON by default, and on a provisioned database that is the right
107
- * default. It is the wrong default on a metered scale-to-zero tier: a
108
- * once-a-minute wake means autosuspend NEVER fires, so the endpoint bills (and
109
- * quota-limits) as if it were awake 100% of the time with zero users online. On
110
- * a free-tier Neon that exhausts the compute quota and the database then
111
- * hard-blocks every read mid-session — the symptom is a dead app, not a slow one.
106
+ * Keep-warm is opt-in because a once-a-minute wake prevents autosuspend on
107
+ * metered scale-to-zero databases. `AGENT_NATIVE_DISABLE_KEEP_WARM` remains a
108
+ * compatibility kill switch and wins when both flags are set.
112
109
  */
113
110
  export declare function isKeepWarmDeployEnabled(): boolean;
114
111
  /**
@@ -138,7 +135,8 @@ export declare function resolveKeepWarmSchedule(): string;
138
135
  * delayed by tens of minutes, which is longer than a scale-to-zero database's
139
136
  * autosuspend window and leaves the next visitor to pay the cold-start cost.
140
137
  *
141
- * Both halves are opt-out and the cadence is configurable — see
138
+ * The feature is opt-in, the background half has its own kill switch, and the
139
+ * cadence is configurable — see
142
140
  * `isKeepWarmDeployEnabled`, `isKeepWarmBackgroundDeployEnabled`, and
143
141
  * `resolveKeepWarmSchedule`. The tradeoff this function encodes is next-visitor
144
142
  * latency against database awake-time, and only the deployment knows which of
@@ -2256,24 +2256,26 @@ export function isIntegrationDurableDispatchDeployEnabled() {
2256
2256
  }
2257
2257
  const NETLIFY_KEEP_WARM_FUNCTION_NAME = "agent-native-keep-warm";
2258
2258
  export const NETLIFY_RECURRING_JOBS_FUNCTION_NAME = "agent-native-recurring-jobs";
2259
- /** Shared shape for the `AGENT_NATIVE_DISABLE_*` build kill switches. */
2260
- function isDisabledByEnv(name) {
2259
+ /** Shared parser for truthy build environment flags. */
2260
+ function isTruthyEnv(name) {
2261
2261
  const value = process.env[name]?.trim();
2262
2262
  return !!value && ["1", "true", "yes", "on"].includes(value.toLowerCase());
2263
2263
  }
2264
+ /** Shared shape for the `AGENT_NATIVE_DISABLE_*` build kill switches. */
2265
+ function isDisabledByEnv(name) {
2266
+ return isTruthyEnv(name);
2267
+ }
2264
2268
  export function isRecurringJobsDeployEnabled() {
2265
2269
  return !isDisabledByEnv("AGENT_NATIVE_DISABLE_RECURRING_JOBS");
2266
2270
  }
2267
2271
  /**
2268
- * Keep-warm is ON by default, and on a provisioned database that is the right
2269
- * default. It is the wrong default on a metered scale-to-zero tier: a
2270
- * once-a-minute wake means autosuspend NEVER fires, so the endpoint bills (and
2271
- * quota-limits) as if it were awake 100% of the time with zero users online. On
2272
- * a free-tier Neon that exhausts the compute quota and the database then
2273
- * hard-blocks every read mid-session — the symptom is a dead app, not a slow one.
2272
+ * Keep-warm is opt-in because a once-a-minute wake prevents autosuspend on
2273
+ * metered scale-to-zero databases. `AGENT_NATIVE_DISABLE_KEEP_WARM` remains a
2274
+ * compatibility kill switch and wins when both flags are set.
2274
2275
  */
2275
2276
  export function isKeepWarmDeployEnabled() {
2276
- return !isDisabledByEnv("AGENT_NATIVE_DISABLE_KEEP_WARM");
2277
+ return (isTruthyEnv("AGENT_NATIVE_ENABLE_KEEP_WARM") &&
2278
+ !isDisabledByEnv("AGENT_NATIVE_DISABLE_KEEP_WARM"));
2277
2279
  }
2278
2280
  /**
2279
2281
  * The background warm is a separate, much more expensive knob than the server
@@ -2286,7 +2288,8 @@ export function isKeepWarmDeployEnabled() {
2286
2288
  * while dropping the probe storm.
2287
2289
  */
2288
2290
  export function isKeepWarmBackgroundDeployEnabled() {
2289
- return !isDisabledByEnv("AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND");
2291
+ return (isKeepWarmDeployEnabled() &&
2292
+ !isDisabledByEnv("AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND"));
2290
2293
  }
2291
2294
  const DEFAULT_KEEP_WARM_SCHEDULE = "* * * * *";
2292
2295
  /**
@@ -2319,7 +2322,8 @@ export function resolveKeepWarmSchedule() {
2319
2322
  * delayed by tens of minutes, which is longer than a scale-to-zero database's
2320
2323
  * autosuspend window and leaves the next visitor to pay the cold-start cost.
2321
2324
  *
2322
- * Both halves are opt-out and the cadence is configurable — see
2325
+ * The feature is opt-in, the background half has its own kill switch, and the
2326
+ * cadence is configurable — see
2323
2327
  * `isKeepWarmDeployEnabled`, `isKeepWarmBackgroundDeployEnabled`, and
2324
2328
  * `resolveKeepWarmSchedule`. The tradeoff this function encodes is next-visitor
2325
2329
  * latency against database awake-time, and only the deployment knows which of
@@ -2327,7 +2331,10 @@ export function resolveKeepWarmSchedule() {
2327
2331
  */
2328
2332
  export function emitSingleTemplateNetlifyKeepWarmFunction(projectCwd) {
2329
2333
  if (!isKeepWarmDeployEnabled()) {
2330
- console.log("[build] Keep-warm emit skipped: AGENT_NATIVE_DISABLE_KEEP_WARM is set. " +
2334
+ const reason = isDisabledByEnv("AGENT_NATIVE_DISABLE_KEEP_WARM")
2335
+ ? "AGENT_NATIVE_DISABLE_KEEP_WARM is set."
2336
+ : "AGENT_NATIVE_ENABLE_KEEP_WARM is not enabled.";
2337
+ console.log(`[build] Keep-warm emit skipped: ${reason} ` +
2331
2338
  "The database is free to autosuspend; the next visitor after an idle " +
2332
2339
  "period pays its cold start.");
2333
2340
  return;
@@ -434,7 +434,7 @@ const messages = {
434
434
  newTerminal: "New terminal",
435
435
  panelOptions: "Agent panel options",
436
436
  collapseSidebar: "Collapse sidebar",
437
- widenChat: "Widen chat to 75%",
437
+ widenChat: "Widen chat",
438
438
  returnChatToLayout: "Return chat to layout",
439
439
  expandSidebar: "Expand sidebar",
440
440
  hideChats: "Hide chats",
@@ -41,16 +41,16 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
- error?: undefined;
45
44
  summary: import("./types.js").TraceSummary;
46
45
  spans: import("./types.js").TraceSpan[];
47
46
  id?: undefined;
47
+ error?: undefined;
48
48
  ok?: undefined;
49
49
  } | {
50
- error?: undefined;
51
50
  summary?: undefined;
52
51
  spans?: undefined;
53
52
  id: string;
53
+ error?: undefined;
54
54
  ok?: undefined;
55
55
  } | {
56
56
  summary?: undefined;
@@ -59,9 +59,9 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
59
59
  error: any;
60
60
  ok?: undefined;
61
61
  } | {
62
- error?: undefined;
63
62
  summary?: undefined;
64
63
  spans?: undefined;
65
64
  id?: undefined;
66
65
  ok: boolean;
66
+ error?: undefined;
67
67
  }>>;
@@ -311,8 +311,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
311
311
  notes?: string | undefined;
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
- id?: undefined;
315
314
  deleted?: undefined;
315
+ id?: undefined;
316
316
  providers: {
317
317
  id: string;
318
318
  label: string;
@@ -329,9 +329,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
329
329
  label?: undefined;
330
330
  message?: undefined;
331
331
  } | {
332
- id?: undefined;
333
332
  deleted?: undefined;
334
333
  count?: undefined;
334
+ id?: undefined;
335
335
  providers?: undefined;
336
336
  found: boolean;
337
337
  provider: import("../custom-registry.js").CustomProviderConfig;
@@ -219,7 +219,7 @@ function formatDownstreamLlmCredentialFailure(agentName, value) {
219
219
  : null;
220
220
  }
221
221
  export const tool = {
222
- description: "Ask a DIFFERENT, separately-deployed app's agent over A2A. Use message by default so the receiving specialist interprets the objective with its own instructions, skills, connected sources, data dictionary, and tools. The receiver owns provider, schema, query, join, and SQL decisions. Use action + input only for an exact, explicitly known, bounded read whose complete input schema is already known; never expose or call a direct action to work around slow or unreliable delegation, and never guess receiver-owned query logic. NEVER use this to call your own app or perform actions you can do with your own tools. Using call-agent on yourself will fail and waste time. " +
222
+ description: "Ask a DIFFERENT, separately-deployed app's agent over A2A. Use message by default so the receiving specialist interprets the objective with its own instructions, skills, connected sources, data dictionary, and tools. The receiver owns provider, schema, query, join, and SQL decisions. Use action + input only for an exact, explicitly known, bounded read whose complete input schema is already known. Never put a create, update, delete, send, save, publish, or any other side effect in action; omit action and send the objective as message instead; never expose or call a direct action to work around slow or unreliable delegation, and never guess receiver-owned query logic. NEVER use this to call your own app or perform actions you can do with your own tools. Using call-agent on yourself will fail and waste time. " +
223
223
  'For brand-consistent generated media, the first-party Assets agent is available as agent="assets"; use it when another app needs generated heroes, diagrams, product shots, thumbnails, videos, or design imagery, unless the current app has its own generation action that already delegates there. ' +
224
224
  "IMPORTANT — handling the response: " +
225
225
  "(a) If it contains a URL or ID, copy it VERBATIM into your reply. Do not 'correct' or pluralize the path (e.g. /deck/ → /decks/), normalize casing, or change the slug — any edit breaks the link. " +
@@ -243,11 +243,11 @@ export const tool = {
243
243
  },
244
244
  action: {
245
245
  type: "string",
246
- description: "Optional exact read-only action for an explicitly known bounded operation. Prefer message when the target agent must interpret data, choose sources or tools, synthesize, mutate, or do multi-step work. Never guess the action schema or send receiver-owned SQL.",
246
+ description: "Optional exact read-only action for an explicitly known bounded operation. This field is never for creates, updates, deletes, sends, saves, publishes, or any other side effect. Prefer message when the target agent must interpret data, choose sources or tools, synthesize, mutate, or do multi-step work. Never guess the action schema or send receiver-owned SQL.",
247
247
  },
248
248
  input: {
249
249
  type: "object",
250
- description: "Complete input object for action. The target app validates it and refuses actions that are not explicitly exposed read-only operations.",
250
+ description: "Complete input object for action. The target app validates it and refuses actions that are not explicitly exposed read-only operations. For a mutating request, omit action and use message.",
251
251
  additionalProperties: true,
252
252
  },
253
253
  approvedActions: {
@@ -22,7 +22,7 @@ export async function run(args, _context, selfAppId) {
22
22
  .join(", ");
23
23
  return `Error: no workspace app "${requested}". Available: ${available || "(none)"}`;
24
24
  }
25
- return formatCapabilityDetail(await loadCapabilities(agent), (appId) => `Ask this specialist with call-agent: agent="${appId}", message="<objective>". Use action + input only for an exact bounded read with a fully known schema.`);
25
+ return formatCapabilityDetail(await loadCapabilities(agent), (appId) => `Ask this specialist with call-agent: agent="${appId}", message="<objective>". Use action + input only for an exact bounded read with a fully known schema; use message for creates, updates, deletes, sends, saves, publishes, or any other side effect.`);
26
26
  }
27
27
  const agents = await discoverAgents(selfAppId);
28
28
  if (agents.length === 0) {