@agent-native/core 0.157.9 → 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 (107) hide show
  1. package/corpus/templates/analytics/scripts/first-party-analytics-postgres-retention-dry-run.sql +5 -2
  2. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +3 -1
  3. package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +18 -6
  4. package/corpus/templates/analytics/server/plugins/db.ts +139 -0
  5. package/corpus/templates/slides/AGENTS.md +14 -10
  6. package/corpus/templates/slides/actions/export-pptx.ts +1094 -94
  7. package/corpus/templates/slides/actions/import-file.ts +16 -1
  8. package/corpus/templates/slides/actions/import-pptx.ts +61 -43
  9. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +222 -29
  10. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +71 -0
  11. package/corpus/templates/slides/app/global.css +19 -1
  12. package/corpus/templates/slides/app/lib/export-google-slides-client.ts +44 -5
  13. package/corpus/templates/slides/app/lib/export-pdf-client.ts +13 -6
  14. package/corpus/templates/slides/app/lib/export-pptx-client.ts +556 -25
  15. package/corpus/templates/slides/app/pages/DeckEditor.tsx +18 -1
  16. package/corpus/templates/slides/server/handlers/import/html-converter.ts +1176 -26
  17. package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +13 -0
  18. package/corpus/templates/slides/server/handlers/import/pptx-assets.ts +24 -0
  19. package/corpus/templates/slides/shared/aspect-ratios.ts +13 -1
  20. package/dist/agent/harness/migrations.js +24 -3
  21. package/dist/agent/production-agent.js +11 -0
  22. package/dist/agent/run-loop-with-resume.js +21 -4
  23. package/dist/automations/service.d.ts +6 -0
  24. package/dist/automations/service.js +35 -0
  25. package/dist/cli/code-agent-connector.d.ts +4 -1
  26. package/dist/cli/code-agent-connector.js +154 -23
  27. package/dist/cli/code-agent-executor.js +255 -13
  28. package/dist/cli/code-agent-runs.d.ts +1 -0
  29. package/dist/cli/code-agent-runs.js +2 -1
  30. package/dist/cli/portal-workspace.d.ts +49 -0
  31. package/dist/cli/portal-workspace.js +320 -0
  32. package/dist/client/AssistantChat.d.ts +1 -1
  33. package/dist/client/AssistantChat.js +39 -61
  34. package/dist/client/ConnectBuilderCard.js +33 -5
  35. package/dist/client/agent-chat-adapter.js +4 -0
  36. package/dist/client/analytics-platform.d.ts +2 -0
  37. package/dist/client/analytics-platform.js +12 -0
  38. package/dist/client/analytics.d.ts +3 -0
  39. package/dist/client/analytics.js +10 -1
  40. package/dist/client/chat/index.d.ts +1 -0
  41. package/dist/client/chat/index.js +1 -0
  42. package/dist/client/chat/run-recovery.d.ts +2 -1
  43. package/dist/client/chat/run-recovery.js +2 -2
  44. package/dist/client/chat-first/apps-rail.d.ts +1 -1
  45. package/dist/client/chat-first/apps-rail.js +11 -11
  46. package/dist/client/chat-first/surface-tabs.d.ts +1 -1
  47. package/dist/client/chat-first/surface-tabs.js +38 -36
  48. package/dist/client/chat-first/types.d.ts +5 -0
  49. package/dist/client/chat-first.d.ts +1 -1
  50. package/dist/client/chat-first.js +4 -4
  51. package/dist/client/desktop-local-code-change.d.ts +10 -0
  52. package/dist/client/desktop-local-code-change.js +16 -0
  53. package/dist/client/index.d.ts +1 -0
  54. package/dist/client/index.js +1 -0
  55. package/dist/client/observability/ThumbsFeedback.js +21 -7
  56. package/dist/client/terminal/AgentTerminal.d.ts +9 -1
  57. package/dist/client/terminal/AgentTerminal.js +38 -3
  58. package/dist/client/terminal/index.d.ts +1 -1
  59. package/dist/client/terminal/index.js +1 -1
  60. package/dist/client/track.js +9 -1
  61. package/dist/client/use-action.js +3 -0
  62. package/dist/collab/struct-routes.d.ts +1 -1
  63. package/dist/deploy/build.js +1 -1
  64. package/dist/ingestion/pptx.d.ts +78 -0
  65. package/dist/ingestion/pptx.js +991 -107
  66. package/dist/integrations/index.d.ts +3 -3
  67. package/dist/integrations/index.js +2 -2
  68. package/dist/integrations/plugin.js +95 -6
  69. package/dist/integrations/remote-commands-store.d.ts +5 -0
  70. package/dist/integrations/remote-commands-store.js +63 -22
  71. package/dist/integrations/remote-devices-store.d.ts +2 -1
  72. package/dist/integrations/remote-devices-store.js +46 -0
  73. package/dist/integrations/remote-types.d.ts +20 -0
  74. package/dist/jobs/actions/list-recurring-jobs.d.ts +3 -0
  75. package/dist/jobs/actions/list-recurring-jobs.js +5 -1
  76. package/dist/jobs/frontmatter.d.ts +16 -0
  77. package/dist/jobs/frontmatter.js +53 -0
  78. package/dist/jobs/remote-execution.d.ts +42 -0
  79. package/dist/jobs/remote-execution.js +287 -0
  80. package/dist/jobs/scheduler.js +102 -3
  81. package/dist/jobs/tools.js +50 -3
  82. package/dist/notifications/routes.d.ts +4 -4
  83. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  84. package/dist/provider-api/actions/provider-api.d.ts +14 -14
  85. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  86. package/dist/resources/handlers.d.ts +1 -1
  87. package/dist/server/action-routes.js +4 -2
  88. package/dist/server/agent-chat-plugin.js +6 -0
  89. package/dist/server/agent-run-context.d.ts +1 -0
  90. package/dist/server/agent-run-context.js +9 -0
  91. package/dist/server/core-routes-plugin.js +8 -1
  92. package/dist/server/request-context.d.ts +2 -0
  93. package/dist/shared/analytics-platform.d.ts +6 -0
  94. package/dist/shared/analytics-platform.js +14 -0
  95. package/dist/shared/mcp-embed-headers.d.ts +1 -1
  96. package/dist/shared/mcp-embed-headers.js +1 -1
  97. package/dist/styles/agent-native.css +83 -103
  98. package/dist/terminal/cli-registry.js +5 -0
  99. package/dist/tracking/registry.js +9 -1
  100. package/dist/triggers/actions/list-automations.d.ts +3 -0
  101. package/dist/triggers/actions/list-automations.js +3 -0
  102. package/dist/triggers/actions/manage-automation.d.ts +9 -0
  103. package/dist/triggers/actions/manage-automation.js +14 -2
  104. package/dist/triggers/actions.js +82 -7
  105. package/docs/content/template-content-developers.mdx +48 -0
  106. package/docs/content/toolkit-ui.mdx +57 -0
  107. package/package.json +2 -2
@@ -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";
@@ -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;
@@ -0,0 +1,16 @@
1
+ export const DESKTOP_LOCAL_CODE_CHANGE_EVENT = "agent-native:desktop-local-code-change";
2
+ /**
3
+ * Ask an embedding desktop shell to prepare a local code-change workspace.
4
+ * The optional target keeps bubbling events scoped to the shell that owns the
5
+ * chat card while preserving a window fallback for standalone consumers.
6
+ */
7
+ export function requestDesktopLocalCodeChange(prompt, target = typeof window === "undefined" ? null : window) {
8
+ const trimmed = prompt.trim();
9
+ if (!trimmed || !target)
10
+ return false;
11
+ target.dispatchEvent(new CustomEvent(DESKTOP_LOCAL_CODE_CHANGE_EVENT, {
12
+ bubbles: true,
13
+ detail: { prompt: trimmed },
14
+ }));
15
+ return true;
16
+ }
@@ -3,6 +3,7 @@
3
3
  * Compatibility aggregation only; new code must not import this barrel.
4
4
  */
5
5
  export * from "./agent-chat/index.js";
6
+ export * from "./desktop-local-code-change.js";
6
7
  export * from "./hooks/index.js";
7
8
  export * from "./navigation/index.js";
8
9
  export * from "./host/index.js";
@@ -3,6 +3,7 @@
3
3
  * Compatibility aggregation only; new code must not import this barrel.
4
4
  */
5
5
  export * from "./agent-chat/index.js";
6
+ export * from "./desktop-local-code-change.js";
6
7
  export * from "./hooks/index.js";
7
8
  export * from "./navigation/index.js";
8
9
  export * from "./host/index.js";
@@ -16,7 +16,7 @@ export function ThumbsFeedback({ threadId, runId, messageSeq, className, }) {
16
16
  const feedbackInputRef = useRef(null);
17
17
  const sendFeedback = useCallback(async (feedbackType, value) => {
18
18
  try {
19
- await fetch(agentNativePath("/_agent-native/observability/feedback"), {
19
+ const response = await fetch(agentNativePath("/_agent-native/observability/feedback"), {
20
20
  method: "POST",
21
21
  headers: { "Content-Type": "application/json" },
22
22
  body: JSON.stringify({
@@ -27,9 +27,14 @@ export function ThumbsFeedback({ threadId, runId, messageSeq, className, }) {
27
27
  value: value ?? "",
28
28
  }),
29
29
  });
30
+ if (!response.ok) {
31
+ throw new Error(`Feedback submission failed (${response.status})`);
32
+ }
33
+ return true;
30
34
  }
31
35
  catch {
32
- // Fire-and-forget; don't block the UI on feedback submission failures
36
+ // coercion-ok: callers receive false and restore the retryable UI state.
37
+ return false;
33
38
  }
34
39
  }, [threadId, runId, messageSeq]);
35
40
  const handleThumbsUp = useCallback(() => {
@@ -37,7 +42,10 @@ export function ThumbsFeedback({ threadId, runId, messageSeq, className, }) {
37
42
  return;
38
43
  setSelection("up");
39
44
  setPopoverOpen(false);
40
- sendFeedback("thumbs_up");
45
+ void sendFeedback("thumbs_up").then((submitted) => {
46
+ if (!submitted)
47
+ setSelection(null);
48
+ });
41
49
  }, [selection, sendFeedback]);
42
50
  const handleThumbsDown = useCallback(() => {
43
51
  if (selection === "down") {
@@ -46,15 +54,21 @@ export function ThumbsFeedback({ threadId, runId, messageSeq, className, }) {
46
54
  }
47
55
  setSelection("down");
48
56
  setPopoverOpen(true);
49
- sendFeedback("thumbs_down");
57
+ void sendFeedback("thumbs_down").then((submitted) => {
58
+ if (!submitted)
59
+ setSelection(null);
60
+ });
50
61
  }, [selection, sendFeedback]);
51
62
  const handleTextFeedback = useCallback(() => {
52
63
  const value = textFeedback.trim();
53
64
  if (!value)
54
65
  return;
55
- setTextFeedback("");
56
- setPopoverOpen(false);
57
- sendFeedback("text", value);
66
+ void sendFeedback("text", value).then((submitted) => {
67
+ if (!submitted)
68
+ return;
69
+ setTextFeedback("");
70
+ setPopoverOpen(false);
71
+ });
58
72
  }, [sendFeedback, textFeedback]);
59
73
  return (_jsxs("div", { className: cn("inline-flex items-center gap-0.5", className), children: [_jsx("button", { type: "button", "aria-label": t("agentChat.feedback.thumbsUp"), onClick: handleThumbsUp, className: cn("flex h-6 w-6 items-center justify-center rounded", selection === "up"
60
74
  ? "text-foreground"
@@ -30,6 +30,14 @@ export interface AgentTerminalProps {
30
30
  onConnectionChange?: (connected: boolean) => void;
31
31
  /** Callback when agent running state changes */
32
32
  onAgentRunningChange?: (running: boolean) => void;
33
+ /** Queue a prompt for the CLI once the PTY WebSocket is ready. */
34
+ submitRequest?: AgentTerminalSubmitRequest;
35
+ /** Called after a queued prompt is written to the PTY. */
36
+ onPromptSubmitted?: (request: AgentTerminalSubmitRequest) => void;
37
+ }
38
+ export interface AgentTerminalSubmitRequest {
39
+ id: string | number;
40
+ text: string;
33
41
  }
34
42
  export declare function formatWebSocketHostname(hostname: string): string;
35
- export declare function AgentTerminal({ command, flags, wsUrl: wsUrlProp, hideInFrame, theme, fontSize, className, style, onConnectionChange, onAgentRunningChange, }: AgentTerminalProps): import("react").JSX.Element | null;
43
+ export declare function AgentTerminal({ command, flags, wsUrl: wsUrlProp, hideInFrame, theme, fontSize, className, style, onConnectionChange, onAgentRunningChange, submitRequest, onPromptSubmitted, }: AgentTerminalProps): import("react").JSX.Element | null;