@agent-native/core 0.157.8 → 0.157.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 (154) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +26 -8
  3. package/corpus/templates/analytics/scripts/first-party-analytics-postgres-retention-dry-run.sql +261 -0
  4. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +3 -1
  5. package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +75 -31
  6. package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +4 -1
  7. package/corpus/templates/analytics/server/plugins/db.ts +139 -0
  8. package/corpus/templates/slides/AGENTS.md +14 -10
  9. package/corpus/templates/slides/actions/export-pptx.ts +1311 -121
  10. package/corpus/templates/slides/actions/import-file.ts +28 -1
  11. package/corpus/templates/slides/actions/import-pptx.ts +69 -42
  12. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +222 -29
  13. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +71 -0
  14. package/corpus/templates/slides/app/global.css +19 -1
  15. package/corpus/templates/slides/app/lib/export-google-slides-client.ts +44 -5
  16. package/corpus/templates/slides/app/lib/export-pdf-client.ts +13 -6
  17. package/corpus/templates/slides/app/lib/export-pptx-client.ts +601 -30
  18. package/corpus/templates/slides/app/pages/DeckEditor.tsx +18 -1
  19. package/corpus/templates/slides/server/handlers/import/html-converter.ts +1240 -22
  20. package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +329 -64
  21. package/corpus/templates/slides/server/handlers/import/pptx-assets.ts +24 -0
  22. package/corpus/templates/slides/server/lib/source-import.ts +10 -1
  23. package/corpus/templates/slides/shared/aspect-ratios.ts +13 -1
  24. package/dist/agent/harness/migrations.d.ts +4 -0
  25. package/dist/agent/harness/migrations.js +60 -0
  26. package/dist/agent/production-agent.d.ts +2 -0
  27. package/dist/agent/production-agent.js +15 -0
  28. package/dist/agent/run-loop-with-resume.d.ts +1 -0
  29. package/dist/agent/run-loop-with-resume.js +44 -12
  30. package/dist/agent/run-migrations.d.ts +8 -0
  31. package/dist/agent/run-migrations.js +61 -0
  32. package/dist/automations/service.d.ts +6 -0
  33. package/dist/automations/service.js +35 -0
  34. package/dist/chat-threads/migrations.d.ts +1 -0
  35. package/dist/chat-threads/migrations.js +1 -0
  36. package/dist/chat-threads/schema-migrations.d.ts +8 -0
  37. package/dist/chat-threads/schema-migrations.js +60 -0
  38. package/dist/cli/code-agent-connector.d.ts +4 -1
  39. package/dist/cli/code-agent-connector.js +154 -23
  40. package/dist/cli/code-agent-executor.js +255 -13
  41. package/dist/cli/code-agent-runs.d.ts +1 -0
  42. package/dist/cli/code-agent-runs.js +2 -1
  43. package/dist/cli/portal-workspace.d.ts +49 -0
  44. package/dist/cli/portal-workspace.js +320 -0
  45. package/dist/client/AssistantChat.d.ts +1 -1
  46. package/dist/client/AssistantChat.js +43 -61
  47. package/dist/client/ConnectBuilderCard.js +33 -5
  48. package/dist/client/agent-chat-adapter.js +4 -0
  49. package/dist/client/analytics-platform.d.ts +2 -0
  50. package/dist/client/analytics-platform.js +12 -0
  51. package/dist/client/analytics.d.ts +3 -0
  52. package/dist/client/analytics.js +10 -1
  53. package/dist/client/chat/index.d.ts +1 -0
  54. package/dist/client/chat/index.js +1 -0
  55. package/dist/client/chat/message-components.d.ts +2 -0
  56. package/dist/client/chat/message-components.js +11 -1
  57. package/dist/client/chat/run-recovery.d.ts +2 -1
  58. package/dist/client/chat/run-recovery.js +2 -2
  59. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  60. package/dist/client/chat-first/apps-rail.js +11 -11
  61. package/dist/client/chat-first/surface-tabs.d.ts +1 -1
  62. package/dist/client/chat-first/surface-tabs.js +38 -36
  63. package/dist/client/chat-first/types.d.ts +5 -0
  64. package/dist/client/chat-first.d.ts +1 -1
  65. package/dist/client/chat-first.js +4 -4
  66. package/dist/client/desktop-local-code-change.d.ts +10 -0
  67. package/dist/client/desktop-local-code-change.js +16 -0
  68. package/dist/client/index.d.ts +1 -0
  69. package/dist/client/index.js +1 -0
  70. package/dist/client/observability/ThumbsFeedback.js +53 -26
  71. package/dist/client/observability/useObservability.d.ts +1 -1
  72. package/dist/client/observability/useObservability.js +6 -3
  73. package/dist/client/terminal/AgentTerminal.d.ts +9 -1
  74. package/dist/client/terminal/AgentTerminal.js +38 -3
  75. package/dist/client/terminal/index.d.ts +1 -1
  76. package/dist/client/terminal/index.js +1 -1
  77. package/dist/client/track.js +9 -1
  78. package/dist/client/use-action.js +3 -0
  79. package/dist/collab/awareness.d.ts +2 -2
  80. package/dist/deploy/build.js +1 -1
  81. package/dist/feature-flags/a2a-action-route.js +4 -2
  82. package/dist/feature-flags/permissions.d.ts +2 -0
  83. package/dist/feature-flags/permissions.js +6 -1
  84. package/dist/ingestion/pptx.d.ts +92 -1
  85. package/dist/ingestion/pptx.js +1180 -115
  86. package/dist/integrations/index.d.ts +3 -3
  87. package/dist/integrations/index.js +2 -2
  88. package/dist/integrations/plugin.js +95 -6
  89. package/dist/integrations/remote-commands-store.d.ts +5 -0
  90. package/dist/integrations/remote-commands-store.js +63 -22
  91. package/dist/integrations/remote-devices-store.d.ts +2 -1
  92. package/dist/integrations/remote-devices-store.js +46 -0
  93. package/dist/integrations/remote-types.d.ts +20 -0
  94. package/dist/jobs/actions/list-recurring-jobs.d.ts +3 -0
  95. package/dist/jobs/actions/list-recurring-jobs.js +5 -1
  96. package/dist/jobs/frontmatter.d.ts +16 -0
  97. package/dist/jobs/frontmatter.js +53 -0
  98. package/dist/jobs/remote-execution.d.ts +42 -0
  99. package/dist/jobs/remote-execution.js +287 -0
  100. package/dist/jobs/scheduler.js +102 -3
  101. package/dist/jobs/tools.js +50 -3
  102. package/dist/localization/core-messages/ar-SA.js +4 -0
  103. package/dist/localization/core-messages/de-DE.js +4 -0
  104. package/dist/localization/core-messages/en-US.d.ts +4 -0
  105. package/dist/localization/core-messages/en-US.js +4 -0
  106. package/dist/localization/core-messages/es-ES.js +4 -0
  107. package/dist/localization/core-messages/fr-FR.js +4 -0
  108. package/dist/localization/core-messages/hi-IN.js +4 -0
  109. package/dist/localization/core-messages/ja-JP.js +4 -0
  110. package/dist/localization/core-messages/ko-KR.js +4 -0
  111. package/dist/localization/core-messages/pt-BR.js +4 -0
  112. package/dist/localization/core-messages/zh-CN.js +4 -0
  113. package/dist/localization/core-messages/zh-TW.js +4 -0
  114. package/dist/localization/core-messages.d.ts +4 -0
  115. package/dist/notifications/routes.d.ts +3 -3
  116. package/dist/observability/feedback.js +40 -16
  117. package/dist/observability/routes.d.ts +6 -6
  118. package/dist/observability/routes.js +15 -3
  119. package/dist/observability/store.js +2 -0
  120. package/dist/observability/traces.js +5 -1
  121. package/dist/provider-api/actions/custom-provider-registration.d.ts +16 -16
  122. package/dist/provider-api/actions/provider-api.d.ts +13 -13
  123. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  124. package/dist/resources/handlers.d.ts +1 -1
  125. package/dist/secrets/routes.d.ts +9 -9
  126. package/dist/server/action-routes.d.ts +7 -5
  127. package/dist/server/action-routes.js +13 -6
  128. package/dist/server/agent-chat-plugin.js +6 -0
  129. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  130. package/dist/server/agent-run-context.d.ts +1 -0
  131. package/dist/server/agent-run-context.js +9 -0
  132. package/dist/server/better-auth-instance.js +4 -0
  133. package/dist/server/core-routes-plugin.js +8 -1
  134. package/dist/server/realtime-voice.js +2 -1
  135. package/dist/server/release-migrations.js +16 -0
  136. package/dist/server/request-context.d.ts +2 -0
  137. package/dist/server/transcribe-voice.d.ts +1 -1
  138. package/dist/shared/analytics-platform.d.ts +6 -0
  139. package/dist/shared/analytics-platform.js +14 -0
  140. package/dist/shared/mcp-embed-headers.d.ts +1 -1
  141. package/dist/shared/mcp-embed-headers.js +1 -1
  142. package/dist/styles/agent-native.css +83 -103
  143. package/dist/terminal/cli-registry.js +5 -0
  144. package/dist/tracking/registry.js +9 -1
  145. package/dist/triggers/actions/list-automations.d.ts +3 -0
  146. package/dist/triggers/actions/list-automations.js +3 -0
  147. package/dist/triggers/actions/manage-automation.d.ts +9 -0
  148. package/dist/triggers/actions/manage-automation.js +14 -2
  149. package/dist/triggers/actions.js +82 -7
  150. package/dist/usage/migrations.d.ts +4 -0
  151. package/dist/usage/migrations.js +45 -0
  152. package/docs/content/template-content-developers.mdx +48 -0
  153. package/docs/content/toolkit-ui.mdx +57 -0
  154. package/package.json +3 -3
@@ -5,6 +5,7 @@ import { withBuilderUtmTrackingParams } from "../shared/builder-link-tracking.js
5
5
  import { agentNativePath } from "./api-path.js";
6
6
  import { BuilderBMark } from "./builder-mark.js";
7
7
  import { writeClipboardText } from "./clipboard.js";
8
+ import { requestDesktopLocalCodeChange } from "./desktop-local-code-change.js";
8
9
  import { getCallbackOrigin } from "./frame.js";
9
10
  import { useBuilderConnectFlow } from "./settings/useBuilderStatus.js";
10
11
  import { cn } from "./utils.js";
@@ -18,6 +19,12 @@ function isLocalDevelopment() {
18
19
  const hostname = window.location.hostname;
19
20
  return (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1");
20
21
  }
22
+ function hasElectronShellBridge() {
23
+ if (typeof window === "undefined")
24
+ return false;
25
+ const electronApi = window.electronAPI;
26
+ return Boolean(electronApi?.appConfig);
27
+ }
21
28
  /**
22
29
  * Rich inline card rendered for the `connect-builder` tool call. Shows the
23
30
  * Builder handoff when available, or routes local development requests to the
@@ -48,7 +55,9 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
48
55
  const [sendErr, setSendErr] = useState(null);
49
56
  const [copyErr, setCopyErr] = useState(null);
50
57
  const [promptCopied, setPromptCopied] = useState(false);
58
+ const [localCodeChangeRequested, setLocalCodeChangeRequested] = useState(false);
51
59
  const [localDevelopment] = useState(() => isLocalDevelopment());
60
+ const [electronShell] = useState(() => hasElectronShellBridge());
52
61
  const mountedRef = useRef(true);
53
62
  const copyResetRef = useRef(null);
54
63
  // Tracks whether the user clicked "Connect Builder" *this session*. When
@@ -154,6 +163,12 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
154
163
  const err = sendErr ?? waitlistErr ?? copyErr ?? flow.error;
155
164
  const hasPrompt = prompt.trim().length > 0;
156
165
  const canSend = configured && builderEnabled && hasPrompt;
166
+ const showDesktopLocalHandoff = electronShell && hasPrompt;
167
+ const handleDoLocally = useCallback((event) => {
168
+ if (requestDesktopLocalCodeChange(prompt, event.currentTarget)) {
169
+ setLocalCodeChangeRequested(true);
170
+ }
171
+ }, [prompt]);
157
172
  // Auto-send the user's pending prompt the moment connecting finishes
158
173
  // successfully. Without this, the connect popup closing leaves the user
159
174
  // staring at a "Send to Builder" button — feels like they have to
@@ -181,14 +196,19 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
181
196
  : `AI credits are ready to use. ${CODE_CHANGE_FALLBACK_TEXT}`;
182
197
  let title;
183
198
  let subtitle;
184
- if (runResult) {
199
+ if (localCodeChangeRequested) {
200
+ title = "Preparing a local copy";
201
+ subtitle = "Desktop is cloning this app and applying this request.";
202
+ }
203
+ else if (runResult) {
185
204
  title = "Builder is working on it";
186
205
  subtitle = (_jsxs(_Fragment, { children: ["Working on branch", " ", _jsx("span", { className: "font-mono text-foreground", children: runResult.branchName }), ". Click through to watch progress in the Visual Editor."] }));
187
206
  }
188
207
  else if (showExternalAgentHandoff) {
189
208
  title = "This requires a code change";
190
- subtitle =
191
- "Open your coding agent in this project, then paste this request.";
209
+ subtitle = showDesktopLocalHandoff
210
+ ? "Make the change in a local copy of this app, or copy the request to another coding agent."
211
+ : "Open your coding agent in this project, then paste this request.";
192
212
  }
193
213
  else if (showWaitlist) {
194
214
  title = "This requires a code change";
@@ -206,8 +226,16 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
206
226
  title = "Connect Builder.io";
207
227
  subtitle = _jsx(_Fragment, { children: "Builder.io's free tier includes AI credits." });
208
228
  }
209
- return (_jsx("div", { className: cn("my-2 rounded-lg border border-border overflow-hidden"), children: _jsxs("div", { className: "flex items-start gap-3 px-4 py-3.5 bg-gradient-to-br from-teal-500/5 via-transparent to-transparent", children: [_jsx("div", { className: cn("flex h-9 w-9 shrink-0 items-center justify-center rounded-lg", "bg-foreground text-background"), children: runResult ? (_jsx(IconLoader2, { className: "h-5 w-5 animate-spin" })) : showExternalAgentHandoff ? (_jsx(IconCode, { className: "h-5 w-5" })) : (_jsx(BuilderBMark, { className: "h-5 w-5" })) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "flex items-center gap-2 flex-wrap", children: _jsx("span", { className: "text-sm font-semibold text-foreground", children: title }) }), _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground leading-relaxed", children: subtitle }), showWaitlist && (_jsxs("a", { href: DESKTOP_DOWNLOAD_URL, target: "_blank", rel: "noopener noreferrer", className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground no-underline hover:text-foreground", children: ["Download desktop app", _jsx(IconExternalLink, { className: "h-3 w-3" })] })), err && _jsx("div", { className: "mt-2 text-xs text-destructive", children: err }), _jsx("div", { className: "mt-3", children: runResult ? (_jsxs("a", { href: withBuilderUtmTrackingParams(runResult.url, {
229
+ return (_jsx("div", { className: cn("my-2 rounded-lg border border-border overflow-hidden"), children: _jsxs("div", { className: "flex items-start gap-3 px-4 py-3.5 bg-gradient-to-br from-teal-500/5 via-transparent to-transparent", children: [_jsx("div", { className: cn("flex h-9 w-9 shrink-0 items-center justify-center rounded-lg", "bg-foreground text-background"), children: runResult ? (_jsx(IconLoader2, { className: "h-5 w-5 animate-spin" })) : showExternalAgentHandoff ? (_jsx(IconCode, { className: "h-5 w-5" })) : (_jsx(BuilderBMark, { className: "h-5 w-5" })) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "flex items-center gap-2 flex-wrap", children: _jsx("span", { className: "text-sm font-semibold text-foreground", children: title }) }), _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground leading-relaxed", children: subtitle }), showWaitlist && !showDesktopLocalHandoff && (_jsxs("a", { href: DESKTOP_DOWNLOAD_URL, target: "_blank", rel: "noopener noreferrer", className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground no-underline hover:text-foreground", children: ["Download desktop app", _jsx(IconExternalLink, { className: "h-3 w-3" })] })), err && _jsx("div", { className: "mt-2 text-xs text-destructive", children: err }), _jsx("div", { className: "mt-3", children: runResult ? (_jsxs("a", { href: withBuilderUtmTrackingParams(runResult.url, {
210
230
  campaign: "product",
211
231
  content: "connect_builder_card_branch",
212
- }), target: "_blank", rel: "noopener noreferrer", className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: ["Open branch in Builder", _jsx(IconExternalLink, { className: "h-3.5 w-3.5" })] })) : canSend ? (_jsx("button", { type: "button", onClick: handleSend, disabled: sending, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", sending && "opacity-70 cursor-wait"), children: sending ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Sending to Builder\u2026"] })) : (_jsx(_Fragment, { children: "Send to Builder" })) })) : showExternalAgentHandoff ? (_jsx("button", { type: "button", onClick: () => void handleCopyPrompt(), className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: promptCopied ? (_jsxs(_Fragment, { children: [_jsx(IconCheck, { className: "h-3.5 w-3.5" }), "Prompt copied"] })) : (_jsxs(_Fragment, { children: [_jsx(IconCopy, { className: "h-3.5 w-3.5" }), "Copy prompt"] })) })) : showWaitlist && !waitlistJoined ? (_jsx("button", { type: "button", onClick: handleJoinWaitlist, disabled: joiningWaitlist, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", joiningWaitlist && "opacity-70 cursor-wait"), children: joiningWaitlist ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Joining\u2026"] })) : (_jsx(_Fragment, { children: "Join the waitlist" })) })) : !configured ? (_jsx("button", { type: "button", onClick: () => flow.start(), disabled: connecting, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", connecting && "opacity-70 cursor-wait"), children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Waiting for Builder\u2026"] })) : ("Connect Builder") })) : null })] })] }) }));
232
+ }), target: "_blank", rel: "noopener noreferrer", className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: ["Open branch in Builder", _jsx(IconExternalLink, { className: "h-3.5 w-3.5" })] })) : canSend ? (_jsxs("div", { className: "flex flex-wrap gap-2", children: [showDesktopLocalHandoff && (_jsxs("button", { type: "button", "data-desktop-local-code-change": true, onClick: handleDoLocally, disabled: localCodeChangeRequested, className: cn("inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-xs font-medium text-foreground transition-colors hover:bg-accent", localCodeChangeRequested && "cursor-wait opacity-70"), children: [localCodeChangeRequested ? (_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(IconCode, { className: "h-3.5 w-3.5" })), localCodeChangeRequested
233
+ ? "Preparing locally…"
234
+ : "Do locally"] })), _jsx("button", { type: "button", onClick: handleSend, disabled: sending, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", sending && "opacity-70 cursor-wait"), children: sending ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Sending to Builder\u2026"] })) : (_jsx(_Fragment, { children: "Send to Builder" })) })] })) : showExternalAgentHandoff ? (_jsxs("div", { className: "flex flex-wrap gap-2", children: [showDesktopLocalHandoff && (_jsxs("button", { type: "button", "data-desktop-local-code-change": true, onClick: handleDoLocally, disabled: localCodeChangeRequested, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", localCodeChangeRequested && "cursor-wait opacity-70"), children: [localCodeChangeRequested ? (_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(IconCode, { className: "h-3.5 w-3.5" })), localCodeChangeRequested
235
+ ? "Preparing locally…"
236
+ : "Do locally"] })), _jsx("button", { type: "button", onClick: () => void handleCopyPrompt(), className: cn("inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-xs font-medium text-foreground transition-colors hover:bg-accent"), children: promptCopied ? (_jsxs(_Fragment, { children: [_jsx(IconCheck, { className: "h-3.5 w-3.5" }), "Prompt copied"] })) : (_jsxs(_Fragment, { children: [_jsx(IconCopy, { className: "h-3.5 w-3.5" }), "Copy prompt"] })) })] })) : showWaitlist && !waitlistJoined ? (_jsxs("div", { className: "flex flex-wrap gap-2", children: [showDesktopLocalHandoff && (_jsxs("button", { type: "button", "data-desktop-local-code-change": true, onClick: handleDoLocally, disabled: localCodeChangeRequested, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", localCodeChangeRequested && "cursor-wait opacity-70"), children: [localCodeChangeRequested ? (_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(IconCode, { className: "h-3.5 w-3.5" })), localCodeChangeRequested
237
+ ? "Preparing locally…"
238
+ : "Do locally"] })), _jsx("button", { type: "button", onClick: handleJoinWaitlist, disabled: joiningWaitlist, className: cn("inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-xs font-medium text-foreground transition-colors hover:bg-accent", joiningWaitlist && "opacity-70 cursor-wait"), children: joiningWaitlist ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Joining\u2026"] })) : (_jsx(_Fragment, { children: "Join the waitlist" })) })] })) : !configured ? (_jsxs("div", { className: "flex flex-wrap gap-2", children: [showDesktopLocalHandoff && (_jsxs("button", { type: "button", "data-desktop-local-code-change": true, onClick: handleDoLocally, disabled: localCodeChangeRequested, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", localCodeChangeRequested && "cursor-wait opacity-70"), children: [localCodeChangeRequested ? (_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(IconCode, { className: "h-3.5 w-3.5" })), localCodeChangeRequested
239
+ ? "Preparing locally…"
240
+ : "Do locally"] })), _jsx("button", { type: "button", onClick: () => flow.start(), disabled: connecting, className: cn("inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-xs font-medium text-foreground transition-colors hover:bg-accent", connecting && "opacity-70 cursor-wait"), children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Waiting for Builder\u2026"] })) : ("Connect Builder") })] })) : showDesktopLocalHandoff ? (_jsxs("button", { type: "button", "data-desktop-local-code-change": true, onClick: handleDoLocally, disabled: localCodeChangeRequested, className: cn("inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-xs font-medium text-foreground transition-colors hover:bg-accent", localCodeChangeRequested && "cursor-wait opacity-70"), children: [localCodeChangeRequested ? (_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(IconCode, { className: "h-3.5 w-3.5" })), localCodeChangeRequested ? "Preparing locally…" : "Do locally"] })) : null })] })] }) }));
213
241
  }
@@ -1,7 +1,9 @@
1
1
  import { unwrapAttachmentEnvelope } from "@agent-native/toolkit/composer/pasted-text";
2
2
  import { actionPreparationContinuationNote } from "../agent/action-continuation-guidance.js";
3
3
  import { LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, } from "../agent/engine/credential-errors.js";
4
+ import { ANALYTICS_CLIENT_PLATFORM_HEADER } from "../shared/analytics-platform.js";
4
5
  import { clearPendingTurnIfMatches, getActiveRun, setActiveRun, updateActiveRunSeq, clearActiveRun, clearActiveRunIfMatches, setPendingTurn, } from "./active-run-state.js";
6
+ import { getAnalyticsClientPlatform } from "./analytics-platform.js";
5
7
  import { getOrCreateAnalyticsSessionId } from "./analytics-session.js";
6
8
  import { captureError } from "./analytics.js";
7
9
  import { agentNativePath } from "./api-path.js";
@@ -2000,6 +2002,8 @@ export function createAgentChatAdapter(options) {
2000
2002
  catch {
2001
2003
  // Analytics session unavailable — traces just lose replay linkage.
2002
2004
  }
2005
+ headers[ANALYTICS_CLIENT_PLATFORM_HEADER] =
2006
+ getAnalyticsClientPlatform();
2003
2007
  // Surface hint — the server uses this to keep code-editing dev tools
2004
2008
  // out of the app-rendered sidebar. The outer dev frame passes
2005
2009
  // "dev-frame" explicitly; the reusable in-product chat defaults to
@@ -0,0 +1,2 @@
1
+ import { type AnalyticsClientPlatform } from "../shared/analytics-platform.js";
2
+ export declare function getAnalyticsClientPlatform(configured?: AnalyticsClientPlatform): AnalyticsClientPlatform;
@@ -0,0 +1,12 @@
1
+ import { normalizeAnalyticsClientPlatform, } from "../shared/analytics-platform.js";
2
+ export function getAnalyticsClientPlatform(configured) {
3
+ if (configured)
4
+ return configured;
5
+ if (typeof window === "undefined")
6
+ return "web";
7
+ const platformWindow = window;
8
+ return (normalizeAnalyticsClientPlatform(platformWindow.agentNativeDesktop?.analytics?.clientPlatform) ??
9
+ normalizeAnalyticsClientPlatform(platformWindow.__AGENT_NATIVE_HOST_PLATFORM__) ??
10
+ normalizeAnalyticsClientPlatform(platformWindow.__AGENT_NATIVE_CONFIG__?.clientPlatform) ??
11
+ "web");
12
+ }
@@ -1,3 +1,4 @@
1
+ import { type AnalyticsClientPlatform } from "../shared/analytics-platform.js";
1
2
  export { clearAnalyticsSessionId, setAnalyticsSessionId, } from "./analytics-session.js";
2
3
  import type { SessionReplayOptions, SessionReplayStartResult } from "./session-replay.js";
3
4
  export { scrubUrl } from "./url-scrub.js";
@@ -51,6 +52,8 @@ export type ErrorCaptureConfigOptions = {
51
52
  maxBreadcrumbs?: number;
52
53
  };
53
54
  export type ConfigureTrackingOptions = {
55
+ /** Platform attribution attached to every event emitted by this client. */
56
+ clientPlatform?: AnalyticsClientPlatform;
54
57
  /**
55
58
  * Agent Native first-party analytics public key. This mirrors hosted
56
59
  * analytics SDKs where consumers pass the key at setup time instead of
@@ -1,5 +1,7 @@
1
+ import { ANALYTICS_CLIENT_PLATFORM_PROPERTY, } from "../shared/analytics-platform.js";
1
2
  import { llmConnectionTrackingProperties, } from "../shared/llm-connection.js";
2
3
  import { toPostHogExceptionProperties } from "../tracking/posthog-exception.js";
4
+ import { getAnalyticsClientPlatform } from "./analytics-platform.js";
3
5
  import { getOrCreateAnalyticsAnonymousId, getOrCreateAnalyticsSessionId, } from "./analytics-session.js";
4
6
  export { clearAnalyticsSessionId, setAnalyticsSessionId, } from "./analytics-session.js";
5
7
  import { fetchAgentEngineStatus, fetchAuthSessionStatus, } from "./client-status-requests.js";
@@ -10,6 +12,7 @@ export { scrubUrl } from "./url-scrub.js";
10
12
  export { addErrorBreadcrumb, captureException, captureMessage, isErrorCaptureInstalled, } from "./error-capture.js";
11
13
  export { getSessionReplayContext, getSessionReplayUrl, } from "./session-replay.js";
12
14
  let _getDefaultProps = null;
15
+ let _configuredAnalyticsClientPlatform = null;
13
16
  let _agentNativeAnalyticsPublicKey = null;
14
17
  let _agentNativeAnalyticsEndpoint = null;
15
18
  let _amplitudeInitialized = false;
@@ -934,6 +937,9 @@ export function trackAgentChatLifecycle(input) {
934
937
  })();
935
938
  }
936
939
  export function configureTracking(options) {
940
+ if (options.clientPlatform) {
941
+ _configuredAnalyticsClientPlatform = options.clientPlatform;
942
+ }
937
943
  const publicKey = options.key || options.publicKey;
938
944
  if (publicKey) {
939
945
  _agentNativeAnalyticsPublicKey = publicKey;
@@ -1413,7 +1419,10 @@ function resolveProps(name, params) {
1413
1419
  if (enriched[key] === undefined)
1414
1420
  enriched[key] = value;
1415
1421
  }
1416
- return applyTrackingIdentity(enriched);
1422
+ return {
1423
+ ...applyTrackingIdentity(enriched),
1424
+ [ANALYTICS_CLIENT_PLATFORM_PROPERTY]: getAnalyticsClientPlatform(_configuredAnalyticsClientPlatform ?? undefined),
1425
+ };
1417
1426
  }
1418
1427
  function sessionReplayTrackingProperties() {
1419
1428
  const module = _sessionReplayModuleForCapture;
@@ -11,6 +11,7 @@ export * from "./runtime.js";
11
11
  export { sendToAgentChat, sendToAgentChatAndConfirm, reportAgentChatSubmitResult, AGENT_CHAT_SUBMIT_RESULT_EVENT, type AgentChatMessage, type AgentChatSubmitResult, type SendToAgentChatAndConfirmResult, } from "../agent-chat.js";
12
12
  export { useAgentChatGenerating } from "../use-agent-chat.js";
13
13
  export { useSendToAgentChat } from "../use-send-to-agent-chat.js";
14
+ export { DESKTOP_LOCAL_CODE_CHANGE_EVENT, requestDesktopLocalCodeChange, type DesktopLocalCodeChangeDetail, } from "../desktop-local-code-change.js";
14
15
  export { AGENT_SIDEBAR_DEFAULT_MAX_WIDTH, AGENT_SIDEBAR_MIN_WIDTH, AGENT_SIDEBAR_WIDE_WIDTH_RATIO, clampAgentSidebarWidth, getAgentSidebarMaxWidth, getAgentSidebarWideWidth, requestAgentSidebarOpen, SIDEBAR_STATE_CHANGE_EVENT, setAgentSidebarOpenPreference, type AgentSidebarStateChangeDetail, type AgentSidebarStateMode, type AgentSidebarStateSource, } from "../agent-sidebar-state.js";
15
16
  export { clearReservedToolRenderersForTests, clearToolRenderersForTests, registerActionChatRenderer, registerFallbackToolRenderer, registerReservedActionChatRenderer, registerReservedFallbackToolRenderer, registerReservedToolRenderer, registerToolRenderer, resolveToolRenderer, type ActionChatRendererRegistration, type ToolRendererComponent, type ToolRendererContext, type ToolRendererMatch, type ToolRendererProps, type ToolRendererRegistration, } from "./tool-render-registry.js";
16
17
  export { ACTION_CHAT_UI_DATA_CHART_RENDERER, ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER, ACTION_CHAT_UI_DATA_TABLE_RENDERER, ACTION_CHAT_UI_DATA_WIDGET_RENDERER, ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER, ACTION_CHAT_UI_WORKSPACE_FILE_RENDERER, type ActionChatUIConfig, } from "../../action-ui.js";
@@ -11,6 +11,7 @@ export * from "./runtime.js";
11
11
  export { sendToAgentChat, sendToAgentChatAndConfirm, reportAgentChatSubmitResult, AGENT_CHAT_SUBMIT_RESULT_EVENT, } from "../agent-chat.js";
12
12
  export { useAgentChatGenerating } from "../use-agent-chat.js";
13
13
  export { useSendToAgentChat } from "../use-send-to-agent-chat.js";
14
+ export { DESKTOP_LOCAL_CODE_CHANGE_EVENT, requestDesktopLocalCodeChange, } from "../desktop-local-code-change.js";
14
15
  export { AGENT_SIDEBAR_DEFAULT_MAX_WIDTH, AGENT_SIDEBAR_MIN_WIDTH, AGENT_SIDEBAR_WIDE_WIDTH_RATIO, clampAgentSidebarWidth, getAgentSidebarMaxWidth, getAgentSidebarWideWidth, requestAgentSidebarOpen, SIDEBAR_STATE_CHANGE_EVENT, setAgentSidebarOpenPreference, } from "../agent-sidebar-state.js";
15
16
  export { clearReservedToolRenderersForTests, clearToolRenderersForTests, registerActionChatRenderer, registerFallbackToolRenderer, registerReservedActionChatRenderer, registerReservedFallbackToolRenderer, registerReservedToolRenderer, registerToolRenderer, resolveToolRenderer, } from "./tool-render-registry.js";
16
17
  export { ACTION_CHAT_UI_DATA_CHART_RENDERER, ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER, ACTION_CHAT_UI_DATA_TABLE_RENDERER, ACTION_CHAT_UI_DATA_WIDGET_RENDERER, ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER, ACTION_CHAT_UI_WORKSPACE_FILE_RENDERER, } from "../../action-ui.js";
@@ -31,6 +31,7 @@ export declare const MessageActionsContext: React.Context<{
31
31
  */
32
32
  bannerRunErrorKey?: string | null;
33
33
  } | null>;
34
+ export declare function isLocalDevelopmentHost(hostname: string): boolean;
34
35
  /**
35
36
  * Restore rewrites the working tree, so only offer it when the server actually
36
37
  * has a checkpoint for this turn. Auto-checkpointing skips turns that started
@@ -43,6 +44,7 @@ export declare function shouldOfferRestore(args: {
43
44
  isLast: boolean;
44
45
  runId: string | undefined;
45
46
  checkpointRunIds: ReadonlySet<string> | undefined;
47
+ hostname: string;
46
48
  }): boolean;
47
49
  /**
48
50
  * Live yields put the run id at `metadata.custom.runId`; server-persisted
@@ -156,6 +156,14 @@ export function SelectionAttachedPill() {
156
156
  // ─── CheckpointContext / MessageActionsContext ────────────────────────────────
157
157
  export const CheckpointContext = React.createContext(null);
158
158
  export const MessageActionsContext = React.createContext(null);
159
+ export function isLocalDevelopmentHost(hostname) {
160
+ const normalizedHostname = hostname.trim().toLowerCase();
161
+ return (normalizedHostname === "localhost" ||
162
+ normalizedHostname === "127.0.0.1" ||
163
+ normalizedHostname === "0.0.0.0" ||
164
+ normalizedHostname === "::1" ||
165
+ normalizedHostname === "[::1]");
166
+ }
159
167
  /**
160
168
  * Restore rewrites the working tree, so only offer it when the server actually
161
169
  * has a checkpoint for this turn. Auto-checkpointing skips turns that started
@@ -163,7 +171,8 @@ export const MessageActionsContext = React.createContext(null);
163
171
  * "Revert to here" item on turns where clicking it could do nothing.
164
172
  */
165
173
  export function shouldOfferRestore(args) {
166
- return Boolean(args.devMode &&
174
+ return Boolean(isLocalDevelopmentHost(args.hostname) &&
175
+ args.devMode &&
167
176
  args.isComplete &&
168
177
  !args.isLast &&
169
178
  args.runId &&
@@ -1018,6 +1027,7 @@ export function AssistantMessage() {
1018
1027
  isLast,
1019
1028
  runId: messageRunId,
1020
1029
  checkpointRunIds: cpCtx?.checkpointRunIds,
1030
+ hostname: window.location.hostname,
1021
1031
  });
1022
1032
  // Collect parts for the files-changed summary (code-agent turns only).
1023
1033
  const msgContent = msg.content;
@@ -33,9 +33,10 @@ export declare function BuilderSetupContent({ onConnected, layout, }: {
33
33
  onConnected?: () => void;
34
34
  layout?: BuilderSetupCardLayout;
35
35
  }): import("react").JSX.Element;
36
- export declare function BuilderSetupCard({ onConnected, bouncePulse, fullWidth, layout, }: {
36
+ export declare function BuilderSetupCard({ onConnected, bouncePulse, attached, fullWidth, layout, }: {
37
37
  onConnected?: () => void;
38
38
  bouncePulse?: number;
39
+ attached?: boolean;
39
40
  fullWidth?: boolean;
40
41
  layout?: BuilderSetupCardLayout;
41
42
  }): import("react").JSX.Element;
@@ -165,7 +165,7 @@ export function BuilderSetupContent({ onConnected, layout = "default", }) {
165
165
  defaultValue: "Custom keys",
166
166
  }) })] })] }), keyOpen ? (_jsx("div", { className: "mt-3", children: _jsx(ApiKeyConnect, { onConnected: onConnected }) })) : null] }));
167
167
  }
168
- export function BuilderSetupCard({ onConnected, bouncePulse, fullWidth, layout = "default", }) {
168
+ export function BuilderSetupCard({ onConnected, bouncePulse, attached = false, fullWidth, layout = "default", }) {
169
169
  const sidebarLayout = layout === "sidebar";
170
170
  const cardRef = useRef(null);
171
171
  // Replay the bounce keyframe each time bouncePulse increments. Toggling the
@@ -181,7 +181,7 @@ export function BuilderSetupCard({ onConnected, bouncePulse, fullWidth, layout =
181
181
  void el.offsetWidth;
182
182
  el.classList.add("animate-bounce-once");
183
183
  }, [bouncePulse]);
184
- return (_jsx("div", { ref: cardRef, className: cn("agent-builder-setup-card", sidebarLayout && "agent-builder-setup-card--sidebar", fullWidth
184
+ return (_jsx("div", { ref: cardRef, className: cn("agent-builder-setup-card", sidebarLayout && "agent-builder-setup-card--sidebar", attached && "agent-builder-setup-card--attached", fullWidth
185
185
  ? "w-full px-3 pb-2"
186
186
  : sidebarLayout
187
187
  ? "mx-auto w-full max-w-[42rem] px-3 pb-2"
@@ -1,2 +1,2 @@
1
1
  import type { ChatFirstAppRailProps } from "./types.js";
2
- export declare const ChatFirstAppsRail: import("react").MemoExoticComponent<({ apps, activeAppId, loading, error, collapsed, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onRemoveApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy, }: ChatFirstAppRailProps) => import("react").JSX.Element>;
2
+ export declare const ChatFirstAppsRail: import("react").MemoExoticComponent<({ apps, defaultAppIds, activeAppId, loading, error, collapsed, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onRemoveApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy, }: ChatFirstAppRailProps) => import("react").JSX.Element>;
@@ -2,7 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
2
2
  import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger, Skeleton, Tooltip, TooltipContent, TooltipTrigger, } from "@agent-native/toolkit/ui";
3
3
  import { IconApps, IconChevronDown, IconChevronUp, IconPin, IconPlus, IconTrash, } from "@tabler/icons-react";
4
4
  import { memo, useMemo, useState } from "react";
5
- import { orderChatFirstAppIds, readChatFirstAppLayout, writeChatFirstAppLayout, } from "../chat-first.js";
5
+ import { CHAT_FIRST_DEFAULT_APP_IDS, orderChatFirstAppIds, readChatFirstAppLayout, writeChatFirstAppLayout, } from "../chat-first.js";
6
6
  import { cn } from "../utils.js";
7
7
  import { defaultChatFirstCopy } from "./copy.js";
8
8
  function ChatFirstRailAppIcon({ app, activeAppId, renderIcon, }) {
@@ -10,8 +10,8 @@ function ChatFirstRailAppIcon({ app, activeAppId, renderIcon, }) {
10
10
  const isInactive = activeAppId !== undefined && !isActive;
11
11
  return (_jsx("span", { "data-chat-first-app-icon": true, className: cn("transition-[filter]", isInactive && "grayscale"), children: renderIcon(app, { isActive, isInactive }) }));
12
12
  }
13
- function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, onOpenApp, onRemoveApp, onTogglePinned, onMove, renderIcon, copy, }) {
14
- const orderedIds = orderChatFirstAppIds(apps.map((app) => app.id), layout);
13
+ function AppRows({ apps, defaultAppIds, activeAppId, layout, onDragStart, onDrop, onDragEnd, onOpenApp, onRemoveApp, onTogglePinned, onMove, renderIcon, copy, }) {
14
+ const orderedIds = orderChatFirstAppIds(apps.map((app) => app.id), layout, defaultAppIds);
15
15
  const appsById = new Map(apps.map((app) => [app.id, app]));
16
16
  const orderedApps = orderedIds
17
17
  .map((id) => appsById.get(id))
@@ -41,28 +41,28 @@ function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, on
41
41
  }), "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")] }), onRemoveApp ? (_jsxs(_Fragment, { children: [_jsx(ContextMenuSeparator, {}), _jsxs(ContextMenuItem, { onSelect: () => onRemoveApp(app), className: "text-destructive focus:text-destructive", children: [_jsx(IconTrash, { size: 14, "aria-hidden": "true" }), copy("removeApp")] })] })) : null] })] }, app.id));
42
42
  }) }));
43
43
  }
44
- export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, collapsed = false, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onRemoveApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy = defaultChatFirstCopy, }) {
44
+ export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, defaultAppIds, activeAppId, loading = false, error, collapsed = false, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onRemoveApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy = defaultChatFirstCopy, }) {
45
45
  const [localLayout, setLocalLayout] = useState(() => readChatFirstAppLayout());
46
46
  const layout = controlledLayout ?? localLayout;
47
47
  const [draggedAppId, setDraggedAppId] = useState(null);
48
48
  const [showAllApps, setShowAllApps] = useState(false);
49
49
  const orderedApps = useMemo(() => {
50
- const orderedIds = orderChatFirstAppIds(apps.map((app) => app.id), layout);
50
+ const orderedIds = orderChatFirstAppIds(apps.map((app) => app.id), layout, defaultAppIds);
51
51
  const appsById = new Map(apps.map((app) => [app.id, app]));
52
52
  return orderedIds
53
53
  .map((id) => appsById.get(id))
54
54
  .filter((app) => Boolean(app));
55
- }, [apps, layout]);
55
+ }, [apps, defaultAppIds, layout]);
56
56
  const visibleApps = useMemo(() => {
57
57
  if (showAllApps)
58
58
  return orderedApps;
59
- const defaultApps = orderedApps.slice(0, 5);
59
+ const defaultApps = orderedApps.slice(0, defaultAppIds?.length ?? CHAT_FIRST_DEFAULT_APP_IDS.length);
60
60
  if (!activeAppId || defaultApps.some((app) => app.id === activeAppId)) {
61
61
  return defaultApps;
62
62
  }
63
63
  const activeApp = orderedApps.find((app) => app.id === activeAppId);
64
64
  return activeApp ? [...defaultApps, activeApp] : defaultApps;
65
- }, [activeAppId, orderedApps, showAllApps]);
65
+ }, [activeAppId, defaultAppIds, orderedApps, showAllApps]);
66
66
  const hasMoreApps = orderedApps.length > visibleApps.length;
67
67
  const createTrigger = createAppTrigger ??
68
68
  (onCreateApp ? (_jsx("button", { type: "button", className: "flex size-6 items-center justify-center rounded text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, "aria-label": copy("createWorkspaceApp"), title: copy("createWorkspaceApp"), children: _jsx(IconPlus, { size: 14, "aria-hidden": "true" }) })) : null);
@@ -82,7 +82,7 @@ export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, activeA
82
82
  function reorderApps(targetId) {
83
83
  if (!draggedAppId || draggedAppId === targetId)
84
84
  return;
85
- const currentOrder = orderChatFirstAppIds(apps.map((app) => app.id), layout);
85
+ const currentOrder = orderChatFirstAppIds(apps.map((app) => app.id), layout, defaultAppIds);
86
86
  const fromIndex = currentOrder.indexOf(draggedAppId);
87
87
  const toIndex = currentOrder.indexOf(targetId);
88
88
  if (fromIndex < 0 || toIndex < 0)
@@ -94,7 +94,7 @@ export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, activeA
94
94
  setDraggedAppId(null);
95
95
  }
96
96
  function moveApp(appId, direction) {
97
- const currentOrder = orderChatFirstAppIds(apps.map((app) => app.id), layout);
97
+ const currentOrder = orderChatFirstAppIds(apps.map((app) => app.id), layout, defaultAppIds);
98
98
  const index = currentOrder.indexOf(appId);
99
99
  const nextIndex = index + direction;
100
100
  if (index < 0 || nextIndex < 0 || nextIndex >= currentOrder.length)
@@ -113,5 +113,5 @@ export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, activeA
113
113
  ? "text-sidebar-foreground"
114
114
  : "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: _jsx(ChatFirstRailAppIcon, { app: app, activeAppId: activeAppId, renderIcon: renderIcon }) }, 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] }));
115
115
  }
116
- 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-2 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-2", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-8 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-2", 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, onRemoveApp: onRemoveApp, 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-2 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] }));
116
+ 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-2 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-2", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-8 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-2", 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, defaultAppIds: defaultAppIds, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onRemoveApp: onRemoveApp, 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-2 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] }));
117
117
  });
@@ -1,7 +1,7 @@
1
1
  import type { ReactNode } from "react";
2
2
  import { type ChatFirstSurfaceTab } from "../chat-first.js";
3
3
  import type { ChatFirstSurfaceTabsProps } from "./types.js";
4
- export declare function ChatFirstSurfaceTabs({ tabs, activeTabId, onActivate, onClose, onCloseOthers, onCloseToRight, onCloseAll, onOpenSurface, apps, onOpenApp, renderAppIcon, copy, }: ChatFirstSurfaceTabsProps): import("react").JSX.Element;
4
+ export declare function ChatFirstSurfaceTabs({ tabs, activeTabId, onActivate, onClose, onCloseOthers, onCloseToRight, onCloseAll, onOpenSurface, hiddenSurfaceKinds, apps, onOpenApp, renderAppIcon, onAddTab, addTabLabel, copy, }: ChatFirstSurfaceTabsProps): import("react").JSX.Element;
5
5
  export declare function ChatFirstSurfaceContent({ tabs, activeTabId, renderTab, }: {
6
6
  tabs: readonly ChatFirstSurfaceTab[];
7
7
  activeTabId: string | null;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@agent-native/toolkit/ui";
3
- import { IconFiles, IconGitCompare, IconMessageCircle, IconTerminal2, IconUsersGroup, IconWorld, IconX, } from "@tabler/icons-react";
3
+ import { IconFiles, IconGitCompare, IconMessageCircle, IconPlus, IconTerminal2, IconUsersGroup, IconWorld, IconX, } from "@tabler/icons-react";
4
4
  import { CHAT_FIRST_SURFACE_CATALOG, } from "../chat-first.js";
5
5
  import { cn } from "../utils.js";
6
6
  import { defaultChatFirstCopy } from "./copy.js";
@@ -17,44 +17,44 @@ function SurfaceIcon({ kind }) {
17
17
  const Icon = ICONS[kind];
18
18
  return _jsx(Icon, { size: 14, "aria-hidden": "true" });
19
19
  }
20
- export function ChatFirstSurfaceTabs({ tabs, activeTabId, onActivate, onClose, onCloseOthers, onCloseToRight, onCloseAll, onOpenSurface, apps = [], onOpenApp, renderAppIcon, copy = defaultChatFirstCopy, }) {
20
+ export function ChatFirstSurfaceTabs({ tabs, activeTabId, onActivate, onClose, onCloseOthers, onCloseToRight, onCloseAll, onOpenSurface, hiddenSurfaceKinds = [], apps = [], onOpenApp, renderAppIcon, onAddTab, addTabLabel = "New tab", copy = defaultChatFirstCopy, }) {
21
21
  return (_jsx(TooltipProvider, { delayDuration: 400, children: _jsx("div", { "data-chat-first-surface-tabs": true, className: cn("min-w-0 border-b border-border bg-card", tabs.length > 0
22
22
  ? "shrink-0"
23
- : "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden"), children: tabs.length > 0 ? (_jsx("div", { className: "flex min-w-0 items-center gap-1 overflow-x-auto px-2 py-1.5", role: "tablist", "aria-label": copy("openSideSurfaces"), children: tabs.map((tab, index) => (_jsxs(ContextMenu, { children: [_jsx(ContextMenuTrigger, { asChild: true, children: _jsxs("div", { "data-surface-tab-id": tab.id, "data-active": activeTabId === tab.id ? "true" : "false", className: cn("group flex h-8 min-w-0 max-w-48 shrink-0 items-center rounded-md text-xs transition-colors", activeTabId === tab.id
24
- ? "bg-accent text-foreground"
25
- : "text-muted-foreground hover:bg-accent hover:text-foreground"), onMouseDown: (event) => {
26
- if (event.button === 1) {
27
- event.preventDefault();
28
- onClose(tab);
29
- }
30
- }, title: tab.title, children: [_jsxs("button", { type: "button", role: "tab", tabIndex: activeTabId === tab.id ? 0 : -1, "aria-selected": activeTabId === tab.id, "data-chat-first-surface-tab-id": tab.id, className: "flex min-w-0 flex-1 items-center gap-1.5 truncate rounded-md px-2 text-start focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onKeyDown: (event) => {
31
- if (event.key === "Enter" || event.key === " ") {
32
- event.preventDefault();
33
- onActivate(tab);
34
- return;
35
- }
36
- const nextIndex = event.key === "ArrowRight"
37
- ? (index + 1) % tabs.length
38
- : event.key === "ArrowLeft"
39
- ? (index - 1 + tabs.length) % tabs.length
40
- : event.key === "Home"
41
- ? 0
42
- : event.key === "End"
43
- ? tabs.length - 1
44
- : -1;
45
- if (nextIndex < 0)
46
- return;
23
+ : "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden"), children: tabs.length > 0 ? (_jsxs("div", { className: "flex min-w-0 items-center gap-1 overflow-x-auto px-2 py-1.5", role: "tablist", "aria-label": copy("openSideSurfaces"), children: [tabs.map((tab, index) => (_jsxs(ContextMenu, { children: [_jsx(ContextMenuTrigger, { asChild: true, children: _jsxs("div", { "data-surface-tab-id": tab.id, "data-active": activeTabId === tab.id ? "true" : "false", className: cn("group flex h-8 min-w-0 max-w-48 shrink-0 items-center rounded-md text-xs transition-colors", activeTabId === tab.id
24
+ ? "bg-accent text-foreground"
25
+ : "text-muted-foreground hover:bg-accent hover:text-foreground"), onMouseDown: (event) => {
26
+ if (event.button === 1) {
47
27
  event.preventDefault();
48
- onActivate(tabs[nextIndex]);
49
- requestAnimationFrame(() => {
50
- document
51
- .querySelector(`[data-chat-first-surface-tab-id="${CSS.escape(tabs[nextIndex].id)}"]`)
52
- ?.focus();
53
- });
54
- }, onClick: () => onActivate(tab), children: [_jsx(SurfaceIcon, { kind: tab.kind }), _jsx("span", { className: "truncate", children: tab.title })] }), _jsx("button", { type: "button", className: "mr-1 flex size-5 shrink-0 items-center justify-center rounded text-muted-foreground/65 opacity-0 transition-opacity hover:bg-background/70 hover:text-foreground group-hover:opacity-100 focus-visible:opacity-100", "aria-label": copy("closeTab", { name: tab.title }), title: copy("closeTab", { name: tab.title }), onClick: (event) => {
55
- event.stopPropagation();
56
28
  onClose(tab);
57
- }, children: _jsx(IconX, { size: 12, "aria-hidden": "true" }) })] }) }), _jsxs(ContextMenuContent, { children: [_jsx(ContextMenuItem, { onSelect: () => onClose(tab), children: copy("close") }), _jsx(ContextMenuItem, { disabled: tabs.length < 2, onSelect: () => onCloseOthers(tab), children: copy("closeOthers") }), _jsx(ContextMenuItem, { disabled: tabs[tabs.length - 1]?.id === tab.id, onSelect: () => onCloseToRight(tab), children: copy("closeToRight") }), _jsx(ContextMenuSeparator, {}), _jsx(ContextMenuItem, { onSelect: onCloseAll, children: copy("closeAll") })] })] }, tab.id))) })) : (_jsxs("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto px-2 py-1", "data-surface-empty-state": true, "aria-label": copy("openSideSurfaces"), children: [CHAT_FIRST_SURFACE_CATALOG.map((surface) => {
29
+ }
30
+ }, title: tab.title, children: [_jsxs("button", { type: "button", role: "tab", tabIndex: activeTabId === tab.id ? 0 : -1, "aria-selected": activeTabId === tab.id, "data-chat-first-surface-tab-id": tab.id, className: "flex min-w-0 flex-1 items-center gap-1.5 truncate rounded-md px-2 text-start focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onKeyDown: (event) => {
31
+ if (event.key === "Enter" || event.key === " ") {
32
+ event.preventDefault();
33
+ onActivate(tab);
34
+ return;
35
+ }
36
+ const nextIndex = event.key === "ArrowRight"
37
+ ? (index + 1) % tabs.length
38
+ : event.key === "ArrowLeft"
39
+ ? (index - 1 + tabs.length) % tabs.length
40
+ : event.key === "Home"
41
+ ? 0
42
+ : event.key === "End"
43
+ ? tabs.length - 1
44
+ : -1;
45
+ if (nextIndex < 0)
46
+ return;
47
+ event.preventDefault();
48
+ onActivate(tabs[nextIndex]);
49
+ requestAnimationFrame(() => {
50
+ document
51
+ .querySelector(`[data-chat-first-surface-tab-id="${CSS.escape(tabs[nextIndex].id)}"]`)
52
+ ?.focus();
53
+ });
54
+ }, onClick: () => onActivate(tab), children: [_jsx(SurfaceIcon, { kind: tab.kind }), _jsx("span", { className: "truncate", children: tab.title })] }), _jsx("button", { type: "button", className: "mr-1 flex size-5 shrink-0 items-center justify-center rounded text-muted-foreground/65 opacity-0 transition-opacity hover:bg-background/70 hover:text-foreground group-hover:opacity-100 focus-visible:opacity-100", "aria-label": copy("closeTab", { name: tab.title }), title: copy("closeTab", { name: tab.title }), onClick: (event) => {
55
+ event.stopPropagation();
56
+ onClose(tab);
57
+ }, children: _jsx(IconX, { size: 12, "aria-hidden": "true" }) })] }) }), _jsxs(ContextMenuContent, { children: [_jsx(ContextMenuItem, { onSelect: () => onClose(tab), children: copy("close") }), _jsx(ContextMenuItem, { disabled: tabs.length < 2, onSelect: () => onCloseOthers(tab), children: copy("closeOthers") }), _jsx(ContextMenuItem, { disabled: tabs[tabs.length - 1]?.id === tab.id, onSelect: () => onCloseToRight(tab), children: copy("closeToRight") }), _jsx(ContextMenuSeparator, {}), _jsx(ContextMenuItem, { onSelect: onCloseAll, children: copy("closeAll") })] })] }, tab.id))), onAddTab ? (_jsx("button", { type: "button", className: "flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground/65 transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", "aria-label": addTabLabel, title: addTabLabel, onClick: onAddTab, children: _jsx(IconPlus, { size: 14, "aria-hidden": "true" }) })) : null] })) : (_jsxs("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto px-2 py-1", "data-surface-empty-state": true, "aria-label": copy("openSideSurfaces"), children: [CHAT_FIRST_SURFACE_CATALOG.filter((surface) => !hiddenSurfaceKinds.includes(surface.kind)).map((surface) => {
58
58
  const label = copy(`surface.${surface.kind}.label`);
59
59
  const reason = surface.availability === "deferred"
60
60
  ? copy(`surface.${surface.kind}.reason`)
@@ -62,6 +62,8 @@ export function ChatFirstSurfaceTabs({ tabs, activeTabId, onActivate, onClose, o
62
62
  const isDeferred = surface.availability === "deferred";
63
63
  const canOpenSurface = onOpenSurface &&
64
64
  (surface.kind === "browser" ||
65
+ (surface.kind === "terminal" &&
66
+ surface.availability === "desktop") ||
65
67
  surface.kind === "side-chat" ||
66
68
  surface.kind === "agents");
67
69
  const row = (_jsxs("div", { className: cn("flex h-7 min-w-0 items-center gap-2 rounded px-2 text-xs transition-colors", canOpenSurface
@@ -78,7 +80,7 @@ export function ChatFirstSurfaceTabs({ tabs, activeTabId, onActivate, onClose, o
78
80
  }), apps.length > 0 && onOpenApp ? (_jsxs("div", { className: "mt-1 border-t border-border pt-1", children: [_jsx("p", { className: "px-2 py-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: copy("workspaceApps") }), _jsx("div", { className: "space-y-0.5", children: apps.map((app) => (_jsxs("button", { type: "button", "data-chat-first-surface-app": app.id, className: "flex h-8 w-full min-w-0 items-center gap-2 rounded px-2 text-start text-xs text-foreground transition-colors hover:bg-accent", title: copy("openApp", { name: app.name }), "aria-label": copy("openApp", { name: app.name }), onClick: () => onOpenApp(app), children: [renderAppIcon?.(app) ?? _jsx(SurfaceIcon, { kind: "app" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: app.name })] }, app.id))) })] })) : null] })) }) }));
79
81
  }
80
82
  export function ChatFirstSurfaceContent({ tabs, activeTabId, renderTab, }) {
81
- return (_jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: tabs.map((tab) => {
83
+ return (_jsx("div", { className: "flex h-full min-h-0 flex-1 flex-col overflow-hidden", children: tabs.map((tab) => {
82
84
  const active = tab.id === activeTabId;
83
85
  return (_jsx("div", { "data-chat-first-surface-content": tab.id, "aria-hidden": !active, className: cn("h-full min-h-0", !active && "hidden"), children: renderTab(tab) }, tab.id));
84
86
  }) }));
@@ -35,6 +35,9 @@ export interface ChatFirstSurfaceTabsProps {
35
35
  onCloseToRight: (tab: ChatFirstSurfaceTab) => void;
36
36
  onCloseAll: () => void;
37
37
  onOpenSurface?: (kind: ChatFirstSurfaceKind) => void;
38
+ hiddenSurfaceKinds?: readonly ChatFirstSurfaceKind[];
39
+ onAddTab?: () => void;
40
+ addTabLabel?: string;
38
41
  apps?: readonly ChatFirstAppItem[];
39
42
  onOpenApp?: (app: ChatFirstAppItem) => void;
40
43
  renderAppIcon?: (app: ChatFirstAppItem) => ReactNode;
@@ -42,6 +45,8 @@ export interface ChatFirstSurfaceTabsProps {
42
45
  }
43
46
  export interface ChatFirstAppRailProps {
44
47
  apps: readonly ChatFirstAppItem[];
48
+ /** Optional host-specific fallback order for apps without a saved layout. */
49
+ defaultAppIds?: readonly string[];
45
50
  activeAppId?: string;
46
51
  loading?: boolean;
47
52
  error?: string | null;
@@ -188,7 +188,7 @@ export declare function writeChatFirstAppLayout(layout: ChatFirstAppLayoutPrefer
188
188
  ok: false;
189
189
  reason: "unavailable" | "write-failed";
190
190
  };
191
- export declare function orderChatFirstAppIds(appIds: readonly string[], layout: ChatFirstAppLayoutPreference): string[];
191
+ export declare function orderChatFirstAppIds(appIds: readonly string[], layout: ChatFirstAppLayoutPreference, preferredAppIds?: readonly string[]): string[];
192
192
  export declare function resolveChatFirstBrowserTarget(detail: ChatFirstOpenBrowserDetail): ChatFirstBrowserResolution;
193
193
  type ChatFirstOpenAppListener = (detail: ChatFirstOpenAppDetail) => void;
194
194
  type ChatFirstOpenBrowserListener = (detail: ChatFirstOpenBrowserDetail) => void;
@@ -19,8 +19,8 @@ export const CHAT_FIRST_SURFACE_CATALOG = [
19
19
  kind: "terminal",
20
20
  label: "Terminal",
21
21
  description: "Run commands beside the conversation.",
22
- availability: "deferred",
23
- disabledReason: "Deferred in this release: the existing terminal is a standalone host surface; a shared PTY lifecycle is needed for an embedded pane.",
22
+ availability: "desktop",
23
+ disabledReason: "Open a local coding-agent terminal beside the conversation.",
24
24
  },
25
25
  {
26
26
  kind: "files",
@@ -304,9 +304,9 @@ export function writeChatFirstAppLayout(layout, storage = browserStorage()) {
304
304
  return { ok: false, reason: "write-failed" };
305
305
  }
306
306
  }
307
- export function orderChatFirstAppIds(appIds, layout) {
307
+ export function orderChatFirstAppIds(appIds, layout, preferredAppIds = CHAT_FIRST_DEFAULT_APP_IDS) {
308
308
  const available = new Set(appIds);
309
- const preferredDefaults = CHAT_FIRST_DEFAULT_APP_IDS.filter((id) => available.has(id));
309
+ const preferredDefaults = preferredAppIds.filter((id) => available.has(id));
310
310
  const preferredDefaultSet = new Set(preferredDefaults);
311
311
  const fallbackOrder = [
312
312
  ...preferredDefaults,
@@ -0,0 +1,10 @@
1
+ export declare const DESKTOP_LOCAL_CODE_CHANGE_EVENT = "agent-native:desktop-local-code-change";
2
+ export interface DesktopLocalCodeChangeDetail {
3
+ prompt: string;
4
+ }
5
+ /**
6
+ * Ask an embedding desktop shell to prepare a local code-change workspace.
7
+ * The optional target keeps bubbling events scoped to the shell that owns the
8
+ * chat card while preserving a window fallback for standalone consumers.
9
+ */
10
+ export declare function requestDesktopLocalCodeChange(prompt: string, target?: EventTarget | null): boolean;