@av-pi-studio/web-client 0.0.8
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.
- package/dist/.tsbuildinfo +1 -0
- package/dist/web/assets/BinaryFallbackViewer-BVnOJSh_.js +2 -0
- package/dist/web/assets/BinaryFallbackViewer-BVnOJSh_.js.map +1 -0
- package/dist/web/assets/BinaryFallbackViewer-CLiNWU_S.css +1 -0
- package/dist/web/assets/ChatPanel-BOR3MYdN.js +3 -0
- package/dist/web/assets/ChatPanel-BOR3MYdN.js.map +1 -0
- package/dist/web/assets/ChatPanel-DW5U4HRf.css +1 -0
- package/dist/web/assets/CodeView-BJb8T6q4.js +2 -0
- package/dist/web/assets/CodeView-BJb8T6q4.js.map +1 -0
- package/dist/web/assets/CodeView-CLRODhB8.css +1 -0
- package/dist/web/assets/DiffView-B4H-yHuX.js +3 -0
- package/dist/web/assets/DiffView-B4H-yHuX.js.map +1 -0
- package/dist/web/assets/DiffView-CRbeRztO.css +1 -0
- package/dist/web/assets/FilePanel-DIInFr3h.css +1 -0
- package/dist/web/assets/FilePanel-lERDJldp.js +3 -0
- package/dist/web/assets/FilePanel-lERDJldp.js.map +1 -0
- package/dist/web/assets/ImageViewer-Cnr0mhRu.js +2 -0
- package/dist/web/assets/ImageViewer-Cnr0mhRu.js.map +1 -0
- package/dist/web/assets/ImageViewer-ijzMsq8H.css +1 -0
- package/dist/web/assets/MarkdownFileViewer-BD5Ri_Fb.css +1 -0
- package/dist/web/assets/MarkdownFileViewer-CDHJBxdH.js +2 -0
- package/dist/web/assets/MarkdownFileViewer-CDHJBxdH.js.map +1 -0
- package/dist/web/assets/TerminalPanel-BQj62G6N.css +1 -0
- package/dist/web/assets/TerminalPanel-cnkK_YF5.js +2 -0
- package/dist/web/assets/TerminalPanel-cnkK_YF5.js.map +1 -0
- package/dist/web/assets/TextViewer-DWYO-J8z.js +2 -0
- package/dist/web/assets/TextViewer-DWYO-J8z.js.map +1 -0
- package/dist/web/assets/VideoViewer-BBuZ4wou.css +1 -0
- package/dist/web/assets/VideoViewer-D2ZnEQoA.js +2 -0
- package/dist/web/assets/VideoViewer-D2ZnEQoA.js.map +1 -0
- package/dist/web/assets/index-DnTTpAX5.js +3 -0
- package/dist/web/assets/index-DnTTpAX5.js.map +1 -0
- package/dist/web/assets/index-DrGTLQo0.css +1 -0
- package/dist/web/assets/markdown-BQwXm0MF.css +1 -0
- package/dist/web/assets/markdown-Biwdm3dP.js +2 -0
- package/dist/web/assets/markdown-Biwdm3dP.js.map +1 -0
- package/dist/web/assets/use-file-download-BpyWNVxA.js +2 -0
- package/dist/web/assets/use-file-download-BpyWNVxA.js.map +1 -0
- package/dist/web/assets/vendor-BFXtRv5b.js +83 -0
- package/dist/web/assets/vendor-BFXtRv5b.js.map +1 -0
- package/dist/web/assets/vendor-dnd-D9kA7FgH.js +6 -0
- package/dist/web/assets/vendor-dnd-D9kA7FgH.js.map +1 -0
- package/dist/web/assets/vendor-highlight-DaTzDdJW.js +5 -0
- package/dist/web/assets/vendor-highlight-DaTzDdJW.js.map +1 -0
- package/dist/web/assets/vendor-icons-DUloQbkI.js +182 -0
- package/dist/web/assets/vendor-icons-DUloQbkI.js.map +1 -0
- package/dist/web/assets/vendor-markdown-CpaoCv-a.js +36 -0
- package/dist/web/assets/vendor-markdown-CpaoCv-a.js.map +1 -0
- package/dist/web/assets/vendor-overlays-Bo99M0pM.js +2 -0
- package/dist/web/assets/vendor-overlays-Bo99M0pM.js.map +1 -0
- package/dist/web/assets/vendor-query-CAh-Aqig.js +2 -0
- package/dist/web/assets/vendor-query-CAh-Aqig.js.map +1 -0
- package/dist/web/assets/vendor-react-BoIQBqDW.js +50 -0
- package/dist/web/assets/vendor-react-BoIQBqDW.js.map +1 -0
- package/dist/web/assets/vendor-terminal-BhzX2_ts.js +10 -0
- package/dist/web/assets/vendor-terminal-BhzX2_ts.js.map +1 -0
- package/dist/web/assets/vendor-terminal-DYP7pi_n.css +32 -0
- package/dist/web/index.html +21 -0
- package/package.json +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":";+pDAIO,MAAMA,GAAc,IAAIC,GCCxB,SAASC,GAASC,EAAkB,CACzC,MAAMC,EAAID,EAAI,OAAO,QAAQ,KAAM,EAAE,EAC/BE,EACJD,EAAE,SAAW,EACTA,EACG,MAAM,EAAE,EACR,IAAKE,GAAMA,EAAIA,CAAC,EAChB,KAAK,EAAE,EACVF,EACN,GAAIC,EAAK,SAAW,GAAK,eAAe,KAAKA,CAAI,EAC/C,MAAM,IAAI,MAAM,sBAAsBF,CAAG,EAAE,EAE7C,MAAO,CACL,EAAG,SAASE,EAAK,MAAM,EAAG,CAAC,EAAG,EAAE,EAChC,EAAG,SAASA,EAAK,MAAM,EAAG,CAAC,EAAG,EAAE,EAChC,EAAG,SAASA,EAAK,MAAM,EAAG,CAAC,EAAG,EAAE,EAEpC,CAEA,SAASE,GAASC,EAAmB,CACnC,OAAO,KAAK,IAAI,EAAG,KAAK,IAAI,IAAK,KAAK,MAAMA,CAAC,CAAC,CAAC,CACjD,CAEO,SAASC,GAAS,CAAE,EAAAC,EAAG,EAAAC,EAAG,EAAAC,GAAkB,CACjD,MAAMC,EAAML,GAAcD,GAASC,CAAC,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,EAClE,MAAO,IAAIK,EAAGH,CAAC,CAAC,GAAGG,EAAGF,CAAC,CAAC,GAAGE,EAAGD,CAAC,CAAC,EAClC,CAGO,SAASE,GAAkBX,EAAqB,CACrD,KAAM,CAAE,EAAAO,EAAG,EAAAC,EAAG,EAAAC,CAAA,EAAMV,GAASC,CAAG,EAC1BY,EAAWC,GAAc,CAC7B,MAAMC,EAAID,EAAI,IACd,OAAOC,GAAK,OAAUA,EAAI,QAAUA,EAAI,MAAS,QAAU,GAC7D,EACA,MAAO,OAASF,EAAQL,CAAC,EAAI,MAASK,EAAQJ,CAAC,EAAI,MAASI,EAAQH,CAAC,CACvE,CAGO,SAASM,GAAmBf,EAAagB,EAAO,UAAWC,EAAQ,UAAmB,CAC3F,OAAON,GAAkBX,CAAG,EAAI,IAAOgB,EAAOC,CAChD,CAGO,SAASC,GAAQlB,EAAamB,EAAwB,CAC3D,KAAM,CAAE,EAAAZ,EAAG,EAAAC,EAAG,EAAAC,CAAA,EAAMV,GAASC,CAAG,EAC1BoB,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,EAAGD,CAAM,CAAC,EACzC,OAAOb,GAAS,CACd,EAAGC,GAAK,IAAMA,GAAKa,EACnB,EAAGZ,GAAK,IAAMA,GAAKY,EACnB,EAAGX,GAAK,IAAMA,GAAKW,CAAA,CACpB,CACH,CASO,SAASC,GAAWC,EAAwB,CACjD,MAAMrB,EAAIqB,EAAM,OAAO,QAAQ,KAAM,EAAE,EACvC,OAAQrB,EAAE,SAAW,GAAKA,EAAE,SAAW,IAAM,CAAC,eAAe,KAAKA,CAAC,CACrE,CClDA,MAAMsB,EAAQ,CACZC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,KACkB,CAClB,GAAMV,EACN,IAAOC,EACP,IAAOC,EACP,IAAOC,EACP,IAAOC,EACP,IAAOC,EACP,IAAOC,EACP,IAAOC,EACP,IAAOC,EACP,IAAOC,EACP,IAAOC,CACT,GAEaC,EAAU,CACrB,KAAMZ,EACJ,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEF,KAAMA,EACJ,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEF,MAAOA,EACL,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEF,KAAMA,EACJ,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEF,MAAOA,EACL,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEF,IAAKA,EACH,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEF,KAAMA,EACJ,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEF,MAAOA,EACL,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEF,OAAQA,EACN,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEF,OAAQA,EACN,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,UACA,WAEF,OAAQA,EACN,U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","names":["queryClient","QueryClient","hexToRgb","hex","h","full","c","clamp255","n","rgbToHex","r","g","b","to","relativeLuminance","channel","v","s","contrastForeground","dark","light","lighten","amount","a","isHexColor","value","scale","s50","s100","s200","s300","s400","s500","s600","s700","s800","s900","s950","palette","DARK_STATUS","LIGHT_STATUS","darkSyntax","lightSyntax","terminalFrom","surface0","foreground","accent","buildDarkColors","tint","accentForeground","buildLightColors","THEME_SWATCHES","THEME_NAMES","buildVariantColors","name","_variants","colorScheme","THEME_VARIANTS","spacing","baseFontSize","fontWeight","borderRadius","borderWidth","opacity","iconSize","baseLineHeight","DEFAULT_UI_FONT","DEFAULT_MONO_FONT","buildShadows","color","buildTheme","variant","FONT_SIZE_BASE","FONT_SIZE_MIN","FONT_SIZE_MAX","applyAppearance","base","settings","themeName","baseTheme","uiFont","monoFont","fontSize","key","k","getTheme","DEFAULT_THEME_NAME","flattenThemeToVars","theme","vars","flattenColors","val","colors","_palette","_syntax","_terminal","semanticColors","flattenSyntax","syntax","camelToKebab","str","m","applyVarsToRoot","style","prop","scheme","applyThemeToDOM","AssetRefSchema","z.string","BrandColorsSchema","z.object","BrandAssetsSchema","BrandLinksSchema","BrandLegalSchema","resolveAccentColors","accentBright","swatch","injectBrandAccent","resolved","STORAGE_KEY","createAppearanceController","store","brandConfig","state","loadInitial","mediaCleanup","resolveThemeName","mode","buildResolved","persist","serialized","raw","parsed","resolvedTheme","update","controller","patch","mq","handler","ThemeBoundary","children","controllerRef","useRef","useEffect","localKvStore","_a","AppProviders","jsx","QueryClientProvider","BUTTON_MIN_HEIGHT","BUTTON_ICON_SIZE","BUTTON_PADDING_H","BUTTON_FONT_SIZE","resolveButtonState","opts","disabled","loading","Button","size","leftIcon","trailing","iconOnly","className","rest","isDisabled","iconPx","jsxs","clsx","styles","Loader2","TextInput","forwardRef","ref","TextArea","isoTimestampSchema","wireTimestampSchema","z.union","z.number","clientTypeSchema","z.enum","helloSchema","z.literal","z.record","z.boolean","serverInfoPayloadSchema","z.unknown","statusSchema","pingSchema","pongSchema","agentStatusEnum","imageAttachmentSchema","agentAttachmentsSchema","z.array","agentSessionConfigSchema","createAgentRequestSchema","createAgentResponseSchema","agentUpdateSchema","agentStatusMessageSchema","agentListSchema","agentDeletedSchema","agentArchivedSchema","toolCallDetailSchema","z.discriminatedUnion","agentStreamEventSchema","agentStreamSchema","timelineDirectionEnum","sourceSeqRangeSchema","fetchAgentTimelineRequestSchema","fetchAgentTimelineResponseSchema","agentPermissionRequestSchema","agentPermissionResolvedSchema","respondToPermissionRequestSchema","respondToPermissionResponseSchema","legacyRespondToPermissionSchema","rewindModeSchema","agentRewindRequestSchema","agentRewindResponseSchema","rpcErrorSchema","sessionMessageSchema","sessionMessageBaseSchema","sessionEnvelopeSchema","TerminalOpcode","OPCODE_BY_VALUE","terminalResizeSchema","TerminalFrameError","message","textEncoder","textDecoder","assertSlot","slot","frameWithPayload","opcodeValue","payload","out","encodeTerminalFrame","frame","json","decodeTerminalFrame","bytes","opcode","result","FileTransferOpcode","fileTransferBeginSchema","FileTransferFrameError","decodeFileTransferFrame","stream","tryDecodeFileTransferFrame","providerIdSchema","colorTierSchema","agentCapabilityFlagsSchema","providerModeSchema","reasonString","reason","createWebSocketTransport","factory","ws","self","url","resolve","reject","create","err","ev","data","_b","_c","_d","code","PROTOCOL_VERSION","newRequestId","RpcError","requestId","RpcTimeoutError","DaemonClient","options","__publicField","flag","handshake","info","type","params","timeoutMs","timeout","timer","next","_code","pending","buf","opcodeByte","envelope","msg","PiStudioClient","daemon","req","agentId","AgentHandle","workspaceId","WorkspaceHandle","ProviderHandle","TimelineHandle","prompt","provider","ReconnectionManager","cb","ms","attempt","jitterAmount","delay","error","generateClientId","useConnectionStore","set","get","transport","client","reconnection","MIN_SIDEBAR_WIDTH","MAX_SIDEBAR_WIDTH","DEFAULT_LEFT_SIDEBAR_WIDTH","DEFAULT_RIGHT_SIDEBAR_WIDTH","clampSidebarWidth","width","useUiStore","host","password","cwd","sessionId","x","y","tab","deltaX","LABEL","ConnectionStatus","status","Toolbar","connect","disconnect","setHost","setPassword","leftSidebarCollapsed","toggleLeftSidebar","rightSidebarCollapsed","toggleRightSidebar","connected","e","PanelLeftOpen","PanelLeftClose","PanelRightOpen","PanelRightClose","EMPTY_TIMELINE","rowSeq","nextRowId","toolDetailText","tool","toolCallKey","callId","onTurnStarted","onAssistantMessage","text","rows","index","row","onReasoning","mergeTool","prev","merged","onToolCall","event","existingIndex","onTurnCompleted","onTurnFailed","finalized","onTurnCanceled","onError","onUserMessage","images","applyStreamEvent","sessionSeq","newSessionId","useSessionStore","id","entry","title","timeline","applyStreamEventToTimeline","lastAssistant","_removed","order","activeSessionId","tabsInWorkspace","tabs","t","useTabStore","existing","closed","idx","activeTabId","lastActiveTabByWorkspace","siblings","siblingIdx","stillOpen","remembered","fromId","toId","fromIdx","toIdx","moved","label","tabIds","path","staged","slotOrToken","terminalCount","openNewTerminal","workspaceCwd","useExplorerStore","currentPath","rootPath","parent","clamped","cachedHomeDir","homeDirListeners","onHomeDirResolved","listener","resolveHome","response","first","peekCachedHomeDir","resolveTildePath","useHomeDir","homeDir","setHomeDir","useState","statusDotColor","input","requiresAttention","attentionReason","showInactive","STATUS_DOT_SIZE","COLOR_TOKEN_VAR","StatusDot","colorToken","MAP","toDotStatus","SessionItem","session","active","onSelect","onOpenMenu","rect","MoreVertical","SessionContextMenu","menu","closeSessionMenu","triggerRef","open","setOpen","canStop","handleOpenChange","rename","stop","removeLocal","archive","remove","DropdownMenu.Root","DropdownMenu.Trigger","DropdownMenu.Portal","DropdownMenu.Content","DropdownMenu.Item","DropdownMenu.Separator","WorkspaceGroupHeader","sessionCount","collapsed","onToggleCollapsed","onNewSession","ChevronRight","ChevronDown","FolderClosed","Plus","normalizeCwd","groupSessionsByWorkspace","sessions","groups","bucket","sessionsInGroup","workspaceLabel","SessionList","activate","createSession","openTab","openSessionMenu","setCwd","openCwdPicker","collapsedWorkspaces","toggleWorkspaceCollapsed","handleNewSession","targetWorkspaceCwd","targetCwd","handleSelect","FolderOpen","group","ICON_BY_KIND","MessageSquare","FileText","GitCompare","TerminalSquare","TabItem","close","Icon","attributes","listeners","setNodeRef","transform","transition","isDragging","useSortable","CSS","X","TabStrip","activeWorkspaceCwd","reorder","visibleTabs","sensors","useSensors","useSensor","PointerSensor","handleDragEnd","over","DndContext","closestCenter","SortableContext","horizontalListSortingStrategy","SPINNER_SIZE_PX","SPINNER_BORDER_PX","Spinner","ariaLabel","px","borderPx","ChatPanel","lazy","__vitePreload","FilePanel","TerminalPanel","PANEL_BY_KIND","TabPanelHost","Panel","Suspense","rpcKeys","normalizeEntry","kind","sortEntries","entries","useExplorer","enabled","useQuery","FileExplorer","setPath","setRoot","goUp","lastSeededCwd","target","isLoading","isError","handleDirClick","handleFileClick","fullPath","ArrowUp","Folder","FileIcon","useGitStore","projection","changes","f","isCheckoutStatusUpdate","useCheckoutStatus","applyProjection","setSubscribedCwd","clientRef","cancelled","unsubscribeMessages","current","BADGE_LABEL","ChangesPanel","handleClick","change","useTerminalStore","terminals","isTerminalsUpdate","useTerminals","setTerminals","res","TerminalsPanel","activateTab","closeTab","findTabForSlot","handleReattach","handleKill","newTerminalButton","isOpen","TerminalIcon","RightSidebar","rightSidebarTab","setRightSidebarTab","useQueryClient","handleRefresh","RotateCw","checked","onChange","Check","placeholder","opt","DialogClose","RadixDialog.Close","Dialog","onOpenChange","footer","bare","RadixDialog.Root","RadixDialog.Portal","RadixDialog.Overlay","RadixDialog.Content","RadixDialog.Title","ResizeHandle","side","onResize","startXRef","handlePointerMove","useCallback","handlePointerUp","handlePointerDown","openWorkspace","OpenWorkspaceDialog","closeDialog","setPathState","inputValue","setInputValue","seed","dirEntries","navigate","handleUp","handleGo","handleOpen","Fragment","WorkspacePage","leftSidebarWidth","rightSidebarWidth","resizeLeftSidebar","resizeRightSidebar","useConnectionBoot","ranRef","qs","shouldConnect","ui","pw","isTimelineItem","flattenTimelineItems","items","events","useSessionRestore","restoredRef","agents","sessionStore","agent","page","useShortcuts","onKeyDown","mod","Boot","App","root","createRoot","StrictMode"],"ignoreList":[],"sources":["../../../src/lib/connection/query-client.ts","../../../src/theme/color-utils.ts","../../../src/theme/palette.ts","../../../src/theme/colors.ts","../../../src/theme/variants.ts","../../../src/theme/tokens.ts","../../../src/theme/theme.ts","../../../src/theme/css-bridge.ts","../../../src/brand/config.ts","../../../src/brand/theme-injection.ts","../../../src/theme/appearance-store.ts","../../../src/theme/ThemeBoundary.tsx","../../../src/providers/kv-store.ts","../../../src/providers/AppProviders.tsx","../../../src/ui/button.ts","../../../src/components/primitives/Button.tsx","../../../src/components/primitives/TextInput.tsx","../../../../protocol/src/validation.ts","../../../../protocol/src/messages.ts","../../../../protocol/src/binary-frames/terminal-stream-protocol.ts","../../../../protocol/src/binary-frames/file-transfer-protocol.ts","../../../../protocol/src/provider-manifest.ts","../../../../client/src/transport.ts","../../../../client/src/daemon-client.ts","../../../../client/src/pistudio-client.ts","../../../../client/src/reconnect.ts","../../../src/lib/connection/connection-store.ts","../../../src/stores/ui-store.ts","../../../src/features/connection/ConnectionStatus.tsx","../../../src/features/connection/Toolbar.tsx","../../../src/timeline/row-model.ts","../../../src/timeline/reducer.ts","../../../src/stores/session-store.ts","../../../src/stores/tab-store.ts","../../../src/stores/explorer-store.ts","../../../src/hooks/use-home-dir.ts","../../../src/ui/status-dot.ts","../../../src/components/primitives/StatusDot.tsx","../../../src/features/sessions/status-map.ts","../../../src/features/sessions/SessionItem.tsx","../../../src/features/sessions/SessionContextMenu.tsx","../../../src/features/sessions/WorkspaceGroupHeader.tsx","../../../src/features/sessions/workspace-grouping.ts","../../../src/features/sessions/SessionList.tsx","../../../src/features/workspace/TabStrip.tsx","../../../src/components/primitives/Spinner.tsx","../../../src/features/workspace/panel-registry.ts","../../../src/features/workspace/TabPanelHost.tsx","../../../src/lib/connection/rpc-keys.ts","../../../src/hooks/use-explorer.ts","../../../src/features/files/FileExplorer.tsx","../../../src/stores/git-store.ts","../../../src/hooks/use-checkout-status.ts","../../../src/features/git/ChangesPanel.tsx","../../../src/stores/terminal-store.ts","../../../src/hooks/use-terminals.ts","../../../src/features/terminal/TerminalsPanel.tsx","../../../src/features/files/RightSidebar.tsx","../../../src/components/primitives/Checkbox.tsx","../../../src/components/primitives/Select.tsx","../../../src/components/primitives/Dialog.tsx","../../../src/components/primitives/ResizeHandle.tsx","../../../src/features/sessions/open-workspace.ts","../../../src/features/workspace-picker/OpenWorkspaceDialog.tsx","../../../src/routes/WorkspacePage.tsx","../../../src/hooks/use-connection.ts","../../../src/lib/protocol/events.ts","../../../src/hooks/use-session-restore.ts","../../../src/hooks/use-shortcuts.ts","../../../src/app.tsx","../../../src/main.tsx"],"sourcesContent":["import { QueryClient } from \"@tanstack/react-query\";\n\n// Shared TanStack Query cache. Tuning (staleTime, retry, invalidation on the\n// `filesChanged` signal) is refined as data-fetching hooks land.\nexport const queryClient = new QueryClient();\n","// Small pure color helpers used by the theme builder and (later) brand accent derivation.\n// No external deps; operates on `#rrggbb` / `#rgb` hex strings.\n\nexport type Rgb = { r: number; g: number; b: number };\n\nexport function hexToRgb(hex: string): Rgb {\n const h = hex.trim().replace(/^#/, \"\");\n const full =\n h.length === 3\n ? h\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : h;\n if (full.length !== 6 || /[^0-9a-fA-F]/.test(full)) {\n throw new Error(`Invalid hex color: ${hex}`);\n }\n return {\n r: parseInt(full.slice(0, 2), 16),\n g: parseInt(full.slice(2, 4), 16),\n b: parseInt(full.slice(4, 6), 16),\n };\n}\n\nfunction clamp255(n: number): number {\n return Math.max(0, Math.min(255, Math.round(n)));\n}\n\nexport function rgbToHex({ r, g, b }: Rgb): string {\n const to = (n: number) => clamp255(n).toString(16).padStart(2, \"0\");\n return `#${to(r)}${to(g)}${to(b)}`;\n}\n\n// Relative luminance per WCAG (0 = black, 1 = white).\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = hexToRgb(hex);\n const channel = (v: number) => {\n const s = v / 255;\n return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n };\n return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);\n}\n\n// Contrast-safe text color to place ON TOP of `hex`.\nexport function contrastForeground(hex: string, dark = \"#18181b\", light = \"#ffffff\"): string {\n return relativeLuminance(hex) > 0.45 ? dark : light;\n}\n\n// Mix `hex` toward white by `amount` (0..1). Used to derive an accentBright when omitted.\nexport function lighten(hex: string, amount: number): string {\n const { r, g, b } = hexToRgb(hex);\n const a = Math.max(0, Math.min(1, amount));\n return rgbToHex({\n r: r + (255 - r) * a,\n g: g + (255 - g) * a,\n b: b + (255 - b) * a,\n });\n}\n\n// Mix `hex` toward black by `amount` (0..1).\nexport function darken(hex: string, amount: number): string {\n const { r, g, b } = hexToRgb(hex);\n const a = Math.max(0, Math.min(1, amount));\n return rgbToHex({ r: r * (1 - a), g: g * (1 - a), b: b * (1 - a) });\n}\n\nexport function isHexColor(value: string): boolean {\n const h = value.trim().replace(/^#/, \"\");\n return (h.length === 3 || h.length === 6) && !/[^0-9a-fA-F]/.test(h);\n}\n","// Design-system raw color palette.\n// Tailwind-style numeric scales referenced by design-system.md § Colors (`colors.palette`).\n// Components reach into these only for FIXED signal colors that must not shift per theme.\n\nexport type PaletteShade =\n | \"50\"\n | \"100\"\n | \"200\"\n | \"300\"\n | \"400\"\n | \"500\"\n | \"600\"\n | \"700\"\n | \"800\"\n | \"900\"\n | \"950\";\n\nexport type PaletteScale = Readonly<Record<PaletteShade, string>>;\n\nconst scale = (\n s50: string,\n s100: string,\n s200: string,\n s300: string,\n s400: string,\n s500: string,\n s600: string,\n s700: string,\n s800: string,\n s900: string,\n s950: string,\n): PaletteScale => ({\n \"50\": s50,\n \"100\": s100,\n \"200\": s200,\n \"300\": s300,\n \"400\": s400,\n \"500\": s500,\n \"600\": s600,\n \"700\": s700,\n \"800\": s800,\n \"900\": s900,\n \"950\": s950,\n});\n\nexport const palette = {\n zinc: scale(\n \"#fafafa\",\n \"#f4f4f5\",\n \"#e4e4e7\",\n \"#d4d4d8\",\n \"#a1a1aa\",\n \"#71717a\",\n \"#52525b\",\n \"#3f3f46\",\n \"#27272a\",\n \"#18181b\",\n \"#09090b\",\n ),\n gray: scale(\n \"#f9fafb\",\n \"#f3f4f6\",\n \"#e5e7eb\",\n \"#d1d5db\",\n \"#9ca3af\",\n \"#6b7280\",\n \"#4b5563\",\n \"#374151\",\n \"#1f2937\",\n \"#111827\",\n \"#030712\",\n ),\n slate: scale(\n \"#f8fafc\",\n \"#f1f5f9\",\n \"#e2e8f0\",\n \"#cbd5e1\",\n \"#94a3b8\",\n \"#64748b\",\n \"#475569\",\n \"#334155\",\n \"#1e293b\",\n \"#0f172a\",\n \"#020617\",\n ),\n blue: scale(\n \"#eff6ff\",\n \"#dbeafe\",\n \"#bfdbfe\",\n \"#93c5fd\",\n \"#60a5fa\",\n \"#3b82f6\",\n \"#2563eb\",\n \"#1d4ed8\",\n \"#1e40af\",\n \"#1e3a8a\",\n \"#172554\",\n ),\n green: scale(\n \"#f0fdf4\",\n \"#dcfce7\",\n \"#bbf7d0\",\n \"#86efac\",\n \"#4ade80\",\n \"#22c55e\",\n \"#16a34a\",\n \"#15803d\",\n \"#166534\",\n \"#14532d\",\n \"#052e16\",\n ),\n red: scale(\n \"#fef2f2\",\n \"#fee2e2\",\n \"#fecaca\",\n \"#fca5a5\",\n \"#f87171\",\n \"#ef4444\",\n \"#dc2626\",\n \"#b91c1c\",\n \"#991b1b\",\n \"#7f1d1d\",\n \"#450a0a\",\n ),\n teal: scale(\n \"#f0fdfa\",\n \"#ccfbf1\",\n \"#99f6e4\",\n \"#5eead4\",\n \"#2dd4bf\",\n \"#14b8a6\",\n \"#0d9488\",\n \"#0f766e\",\n \"#115e59\",\n \"#134e4a\",\n \"#042f2e\",\n ),\n amber: scale(\n \"#fffbeb\",\n \"#fef3c7\",\n \"#fde68a\",\n \"#fcd34d\",\n \"#fbbf24\",\n \"#f59e0b\",\n \"#d97706\",\n \"#b45309\",\n \"#92400e\",\n \"#78350f\",\n \"#451a03\",\n ),\n yellow: scale(\n \"#fefce8\",\n \"#fef9c3\",\n \"#fef08a\",\n \"#fde047\",\n \"#facc15\",\n \"#eab308\",\n \"#ca8a04\",\n \"#a16207\",\n \"#854d0e\",\n \"#713f12\",\n \"#422006\",\n ),\n purple: scale(\n \"#faf5ff\",\n \"#f3e8ff\",\n \"#e9d5ff\",\n \"#d8b4fe\",\n \"#c084fc\",\n \"#a855f7\",\n \"#9333ea\",\n \"#7e22ce\",\n \"#6b21a8\",\n \"#581c87\",\n \"#3b0764\",\n ),\n orange: scale(\n \"#fff7ed\",\n \"#ffedd5\",\n \"#fed7aa\",\n \"#fdba74\",\n \"#fb923c\",\n \"#f97316\",\n \"#ea580c\",\n \"#c2410c\",\n \"#9a3412\",\n \"#7c2d12\",\n \"#431407\",\n ),\n white: \"#ffffff\",\n black: \"#000000\",\n} as const;\n\nexport type Palette = typeof palette;\n","// Layer-based semantic color system + syntax/terminal maps per design-system.md § Colors.\n\nimport { contrastForeground, lighten } from \"./color-utils.js\";\nimport { palette } from \"./palette.js\";\n\n// Highlight token → color map (see timeline-rendering.md § Syntax highlighting).\nexport type SyntaxColors = {\n keyword: string;\n string: string;\n number: string;\n boolean: string;\n comment: string;\n function: string;\n variable: string;\n type: string;\n class: string;\n constant: string;\n operator: string;\n punctuation: string;\n tag: string;\n attribute: string;\n property: string;\n regexp: string;\n escape: string;\n heading: string;\n link: string;\n deleted: string;\n inserted: string;\n};\n\n// Full xterm theme: bg/fg/cursor/selection + 16 ANSI colors.\nexport type TerminalTheme = {\n background: string;\n foreground: string;\n cursor: string;\n cursorAccent: string;\n selectionBackground: string;\n black: string;\n red: string;\n green: string;\n yellow: string;\n blue: string;\n magenta: string;\n cyan: string;\n white: string;\n brightBlack: string;\n brightRed: string;\n brightGreen: string;\n brightYellow: string;\n brightBlue: string;\n brightMagenta: string;\n brightCyan: string;\n brightWhite: string;\n};\n\nexport type ThemeColors = {\n // Surfaces (elevation ramp)\n surface0: string;\n surface1: string;\n surface2: string;\n surface3: string;\n surface4: string;\n // Surfaces (special)\n surfaceDiffEmpty: string;\n surfaceSidebar: string;\n surfaceSidebarHover: string;\n surfaceWorkspace: string;\n // Text\n foreground: string;\n foregroundMuted: string;\n // Brand\n accent: string;\n accentBright: string;\n accentForeground: string;\n // Semantic\n destructive: string;\n destructiveForeground: string;\n success: string;\n successForeground: string;\n // Borders\n border: string;\n borderAccent: string;\n // Status\n statusSuccess: string;\n statusDanger: string;\n statusWarning: string;\n statusMerged: string;\n // Diff\n diffAddition: string;\n diffDeletion: string;\n // Controls\n scrollbarHandle: string;\n // Legacy aliases (migration only; new code uses semantic tokens above)\n background: string;\n popover: string;\n popoverForeground: string;\n primary: string;\n secondary: string;\n muted: string;\n mutedForeground: string;\n input: string;\n ring: string;\n // Nested raw scales\n palette: typeof palette;\n syntax: SyntaxColors;\n terminal: TerminalTheme;\n};\n\n// Signal status colors — semantic success/danger/warning/merged. Kept a step\n// darker than the raw palette so they read as signals, not neon. Per Paseo\n// (`~/DEV/paseo/packages/app/src/styles/theme.ts`) light and dark differ.\nconst DARK_STATUS = {\n statusSuccess: \"#16a34a\", // green-600\n statusDanger: \"#dc2626\", // red-600\n statusWarning: \"#f59e0b\", // amber-500\n statusMerged: \"#9333ea\", // purple-600\n} as const;\nconst LIGHT_STATUS = {\n statusSuccess: \"#15803d\", // green-700\n statusDanger: \"#b91c1c\", // red-700\n statusWarning: \"#d97706\", // amber-600\n statusMerged: \"#7c3aed\", // purple-600\n} as const;\n\nfunction darkSyntax(): SyntaxColors {\n return {\n keyword: palette.purple[\"300\"],\n string: palette.green[\"300\"],\n number: palette.orange[\"300\"],\n boolean: palette.orange[\"300\"],\n comment: palette.zinc[\"500\"],\n function: palette.blue[\"300\"],\n variable: palette.zinc[\"200\"],\n type: palette.teal[\"300\"],\n class: palette.amber[\"300\"],\n constant: palette.orange[\"300\"],\n operator: palette.zinc[\"400\"],\n punctuation: palette.zinc[\"400\"],\n tag: palette.red[\"300\"],\n attribute: palette.amber[\"300\"],\n property: palette.blue[\"200\"],\n regexp: palette.teal[\"300\"],\n escape: palette.orange[\"200\"],\n heading: palette.blue[\"300\"],\n link: palette.blue[\"400\"],\n deleted: palette.red[\"400\"],\n inserted: palette.green[\"400\"],\n };\n}\n\nfunction lightSyntax(): SyntaxColors {\n return {\n keyword: palette.purple[\"700\"],\n string: palette.green[\"700\"],\n number: palette.orange[\"700\"],\n boolean: palette.orange[\"700\"],\n comment: palette.zinc[\"500\"],\n function: palette.blue[\"700\"],\n variable: palette.zinc[\"800\"],\n type: palette.teal[\"700\"],\n class: palette.amber[\"700\"],\n constant: palette.orange[\"700\"],\n operator: palette.zinc[\"600\"],\n punctuation: palette.zinc[\"600\"],\n tag: palette.red[\"700\"],\n attribute: palette.amber[\"700\"],\n property: palette.blue[\"700\"],\n regexp: palette.teal[\"700\"],\n escape: palette.orange[\"700\"],\n heading: palette.blue[\"700\"],\n link: palette.blue[\"600\"],\n deleted: palette.red[\"700\"],\n inserted: palette.green[\"700\"],\n };\n}\n\n// Tint config consumed by the dark-theme builder.\nexport type DarkTintConfig = {\n surface0: string;\n surface1: string;\n surface2: string;\n surface3: string;\n surface4: string;\n surfaceDiffEmpty: string;\n surfaceSidebar: string;\n surfaceSidebarHover: string;\n mutedForeground: string;\n scrollbarHandle: string;\n border: string;\n borderAccent: string;\n accent: string;\n accentBright: string;\n destructive: string;\n};\n\nfunction terminalFrom(surface0: string, foreground: string, accent: string): TerminalTheme {\n return {\n background: surface0,\n foreground,\n cursor: accent,\n cursorAccent: surface0,\n selectionBackground: \"rgba(255,255,255,0.18)\",\n black: palette.zinc[\"900\"],\n red: palette.red[\"500\"],\n green: palette.green[\"500\"],\n yellow: palette.amber[\"500\"],\n blue: palette.blue[\"500\"],\n magenta: palette.purple[\"500\"],\n cyan: palette.teal[\"500\"],\n white: palette.zinc[\"300\"],\n brightBlack: palette.zinc[\"600\"],\n brightRed: palette.red[\"400\"],\n brightGreen: palette.green[\"400\"],\n brightYellow: palette.amber[\"400\"],\n brightBlue: palette.blue[\"400\"],\n brightMagenta: palette.purple[\"400\"],\n brightCyan: palette.teal[\"400\"],\n brightWhite: palette.zinc[\"50\"],\n };\n}\n\n// Build a full dark theme color set from a tint config.\nexport function buildDarkColors(tint: DarkTintConfig): ThemeColors {\n // Paseo dark foreground is zinc-50 (#fafafa), not zinc-100.\n const foreground = palette.zinc[\"50\"];\n const accentForeground = contrastForeground(tint.accent);\n return {\n surface0: tint.surface0,\n surface1: tint.surface1,\n surface2: tint.surface2,\n surface3: tint.surface3,\n surface4: tint.surface4,\n surfaceDiffEmpty: tint.surfaceDiffEmpty,\n surfaceSidebar: tint.surfaceSidebar,\n surfaceSidebarHover: tint.surfaceSidebarHover,\n // Paseo dark: the workspace surface is surface1 (not the app background surface0).\n surfaceWorkspace: tint.surface1,\n foreground,\n foregroundMuted: tint.mutedForeground,\n accent: tint.accent,\n accentBright: tint.accentBright,\n accentForeground,\n destructive: tint.destructive,\n destructiveForeground: palette.white,\n // Paseo: success uses the theme accent in dark.\n success: tint.accent,\n successForeground: palette.white,\n border: tint.border,\n borderAccent: tint.borderAccent,\n ...DARK_STATUS,\n diffAddition: palette.green[\"400\"], // #4ade80\n diffDeletion: palette.red[\"500\"], // #ef4444\n scrollbarHandle: tint.scrollbarHandle,\n // Legacy aliases → nearest semantic token\n background: tint.surface0,\n popover: tint.surface2,\n popoverForeground: foreground,\n primary: tint.accent,\n secondary: tint.surface2,\n muted: tint.surface2,\n mutedForeground: tint.mutedForeground,\n input: tint.surface2,\n ring: tint.accent,\n palette,\n syntax: darkSyntax(),\n terminal: terminalFrom(tint.surface0, foreground, tint.accent),\n };\n}\n\n// The single light theme (built directly, not via the dark builder).\n// Values mirror Paseo's `lightSemanticColors` (`~/DEV/paseo/.../styles/theme.ts`).\nexport function buildLightColors(): ThemeColors {\n const foreground = \"#1a1a1e\";\n const accent = \"#253e6f\";\n return {\n surface0: \"#ffffff\",\n surface1: \"#fafafa\",\n surface2: \"#f4f4f5\",\n surface3: \"#e4e4e7\",\n surface4: \"#d4d4d8\",\n surfaceDiffEmpty: \"#f6f6f6\",\n surfaceSidebar: \"#f4f4f5\",\n surfaceSidebarHover: \"#e9e9ec\",\n surfaceWorkspace: \"#ffffff\",\n foreground,\n foregroundMuted: \"#71717a\",\n accent,\n accentBright: \"#2f4f8e\",\n accentForeground: contrastForeground(accent),\n destructive: \"#b04138\",\n destructiveForeground: \"#ffffff\",\n success: accent,\n successForeground: \"#ffffff\",\n border: \"#e4e4e7\",\n borderAccent: \"#ececf1\",\n ...LIGHT_STATUS,\n diffAddition: \"#15803d\", // green-700\n diffDeletion: \"#b91c1c\", // red-700\n scrollbarHandle: \"#3f3f46\", // zinc-700\n background: \"#ffffff\",\n popover: \"#ffffff\",\n popoverForeground: foreground,\n primary: accent,\n secondary: \"#f4f4f5\",\n muted: \"#f4f4f5\",\n mutedForeground: \"#71717a\",\n input: \"#f4f4f5\",\n ring: accent,\n palette,\n syntax: lightSyntax(),\n terminal: {\n ...terminalFrom(\"#ffffff\", foreground, accent),\n selectionBackground: \"rgba(0,0,0,0.15)\",\n white: \"#ffffff\",\n brightWhite: \"#fafafa\",\n },\n };\n}\n\nexport { lighten };\n","// Six named theme variants per design-system.md § Theme variants.\n// One light + five dark tints sharing the buildDarkColors builder.\n\nimport { buildDarkColors, buildLightColors, type ThemeColors } from \"./colors.js\";\n\n// theme-name → swatch color map (for Appearance picker).\nexport const THEME_SWATCHES: Readonly<Record<ThemeName, string>> = {\n light: \"#ffffff\",\n dark: \"#3b62b0\",\n zinc: \"#808080\",\n midnight: \"#4A6BA8\",\n claude: \"#D97757\",\n ghostty: \"#8caaee\",\n};\n\nexport type ThemeName = \"light\" | \"dark\" | \"zinc\" | \"midnight\" | \"claude\" | \"ghostty\";\nexport const THEME_NAMES: readonly ThemeName[] = [\n \"light\",\n \"dark\",\n \"zinc\",\n \"midnight\",\n \"claude\",\n \"ghostty\",\n];\n\nfunction buildVariantColors(name: ThemeName): ThemeColors {\n if (name === \"light\") return buildLightColors();\n\n switch (name) {\n // Paseo — subtle teal-green tint (default). Exact values from Paseo `paseoDarkColors`.\n case \"dark\":\n return buildDarkColors({\n surface0: \"#181B1A\",\n surface1: \"#1E2120\",\n surface2: \"#272A29\",\n surface3: \"#434645\",\n surface4: \"#595B5B\",\n surfaceDiffEmpty: \"#252827\",\n surfaceSidebar: \"#141716\",\n surfaceSidebarHover: \"#1c1f1e\",\n mutedForeground: \"#A1A5A4\",\n scrollbarHandle: \"#717574\",\n border: \"#252B2A\",\n borderAccent: \"#2f3135\",\n accent: \"#2e5cb8\",\n accentBright: \"#a2b4d7\",\n destructive: \"#c64f43\",\n });\n\n // Zinc — neutral gray, no tint. Exact values from Paseo `zincDarkColors`.\n case \"zinc\":\n return buildDarkColors({\n surface0: \"#18181b\",\n surface1: \"#1f1f22\",\n surface2: \"#27272a\",\n surface3: \"#3f3f46\",\n surface4: \"#52525b\",\n surfaceDiffEmpty: \"#242427\",\n surfaceSidebar: \"#131316\",\n surfaceSidebarHover: \"#1b1b1e\",\n mutedForeground: \"#a1a1aa\",\n scrollbarHandle: \"#71717a\",\n border: \"#27272a\",\n borderAccent: \"#303036\",\n accent: \"#e4e4e7\",\n accentBright: \"#fafafa\",\n destructive: \"#c44a4a\",\n });\n\n // Midnight — subtle blue tint. Exact values from Paseo `midnightDarkColors`.\n case \"midnight\":\n return buildDarkColors({\n surface0: \"#161820\",\n surface1: \"#1c1e27\",\n surface2: \"#252731\",\n surface3: \"#3c3e4c\",\n surface4: \"#535564\",\n surfaceDiffEmpty: \"#222430\",\n surfaceSidebar: \"#121420\",\n surfaceSidebarHover: \"#1a1c28\",\n mutedForeground: \"#9a9db0\",\n scrollbarHandle: \"#6b6e82\",\n border: \"#242636\",\n borderAccent: \"#2e3040\",\n accent: \"#3b6fcf\",\n accentBright: \"#7eaaeb\",\n destructive: \"#c44a52\",\n });\n\n // Claude — warm neutral with subtle orange undertone. From Paseo `claudeDarkColors`.\n case \"claude\":\n return buildDarkColors({\n surface0: \"#1f1f1e\",\n surface1: \"#262523\",\n surface2: \"#2f2d2b\",\n surface3: \"#4a4745\",\n surface4: \"#605d5b\",\n surfaceDiffEmpty: \"#2a2826\",\n surfaceSidebar: \"#1a1918\",\n surfaceSidebarHover: \"#222120\",\n mutedForeground: \"#ada9a5\",\n scrollbarHandle: \"#78746f\",\n border: \"#2c2a27\",\n borderAccent: \"#36332f\",\n accent: \"#d97757\",\n accentBright: \"#e89a7f\",\n destructive: \"#cf513e\",\n });\n\n // Ghostty — blue-tinted dark. Exact values from Paseo `ghosttyDarkColors`.\n case \"ghostty\":\n return buildDarkColors({\n surface0: \"#282c34\",\n surface1: \"#2f333d\",\n surface2: \"#383c48\",\n surface3: \"#4a4f5e\",\n surface4: \"#5b6175\",\n surfaceDiffEmpty: \"#323643\",\n surfaceSidebar: \"#21252d\",\n surfaceSidebarHover: \"#292d36\",\n mutedForeground: \"#c8ccd8\",\n scrollbarHandle: \"#a0a4b2\",\n border: \"#353a47\",\n borderAccent: \"#3f4454\",\n accent: \"#89b4fa\",\n accentBright: \"#b4d0fc\",\n destructive: \"#c44a55\",\n });\n }\n}\n\nexport type ThemeVariant = {\n name: ThemeName;\n colorScheme: \"light\" | \"dark\";\n colors: ThemeColors;\n swatch: string;\n};\n\nconst _variants: Record<ThemeName, ThemeVariant> = {} as Record<ThemeName, ThemeVariant>;\nfor (const name of THEME_NAMES) {\n const colorScheme: \"light\" | \"dark\" = name === \"light\" ? \"light\" : \"dark\";\n _variants[name] = {\n name,\n colorScheme,\n colors: buildVariantColors(name),\n swatch: THEME_SWATCHES[name],\n };\n}\nexport const THEME_VARIANTS: Readonly<Record<ThemeName, ThemeVariant>> = _variants;\n","// Theme scale tokens (theme-invariant unless patched by Appearance settings).\n// Exact values per design-system.md § Scales.\n\n// spacing: 4px base grid.\nexport const spacing = {\n \"0\": 0,\n \"1\": 4,\n \"1.5\": 6,\n \"2\": 8,\n \"3\": 12,\n \"4\": 16,\n \"6\": 24,\n \"8\": 32,\n \"12\": 48,\n \"16\": 64,\n \"20\": 80,\n \"24\": 96,\n \"32\": 128,\n} as const;\nexport type Spacing = typeof spacing;\nexport type SpacingKey = keyof Spacing;\n\n// fontSize keys — values deliberately typed as `number` (not literals) so the appearance\n// updater can patch them at runtime. `baseFontSize` holds the documented defaults.\nexport type FontSizeKey =\n | \"xs\"\n | \"code\"\n | \"sm\"\n | \"base\"\n | \"lg\"\n | \"xl\"\n | \"2xl\"\n | \"3xl\"\n | \"4xl\";\n\nexport const baseFontSize: Readonly<Record<FontSizeKey, number>> = {\n xs: 12,\n code: 12,\n sm: 14,\n base: 16,\n lg: 18,\n xl: 20,\n \"2xl\": 22,\n \"3xl\": 26,\n \"4xl\": 34,\n};\n\n// fontWeight — RN-style string weights.\nexport const fontWeight = {\n normal: \"400\",\n medium: \"500\",\n semibold: \"600\",\n bold: \"700\",\n} as const;\nexport type FontWeight = typeof fontWeight;\n\nexport const borderRadius = {\n none: 0,\n sm: 2,\n base: 4,\n md: 6,\n lg: 8,\n xl: 12,\n \"2xl\": 16,\n full: 9999,\n} as const;\nexport type BorderRadius = typeof borderRadius;\n\nexport const borderWidth = {\n \"0\": 0,\n \"1\": 1,\n \"2\": 2,\n} as const;\nexport type BorderWidth = typeof borderWidth;\n\nexport const opacity = {\n \"0\": 0,\n \"50\": 0.5,\n \"100\": 1,\n} as const;\nexport type Opacity = typeof opacity;\n\nexport const iconSize = {\n xs: 12,\n sm: 14,\n md: 16,\n lg: 20,\n} as const;\nexport type IconSize = typeof iconSize;\n\n// lineHeight.diff is reserved for code/diff; prose uses fontSize.base * ~1.4 at render time.\nexport const baseLineHeight = { diff: 22 } as const;\n\n// Default platform font stacks (seeds; appearance can override the `ui` stack).\nexport const DEFAULT_UI_FONT =\n 'system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif';\nexport const DEFAULT_MONO_FONT =\n 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace';\n\nexport type Shadow = {\n color: string;\n offsetX: number;\n offsetY: number;\n radius: number;\n elevation: number;\n};\n\nexport type ShadowSet = { sm: Shadow; md: Shadow; lg: Shadow };\n\n// Shadow builder — dark themes use a stronger alpha-tinted color.\nexport function buildShadows(colorScheme: \"light\" | \"dark\"): ShadowSet {\n const color = colorScheme === \"dark\" ? \"rgba(0,0,0,0.55)\" : \"rgba(24,24,27,0.12)\";\n return {\n sm: { color, offsetX: 0, offsetY: 1, radius: 2, elevation: 1 },\n md: { color, offsetX: 0, offsetY: 3, radius: 8, elevation: 4 },\n lg: { color, offsetX: 0, offsetY: 10, radius: 24, elevation: 12 },\n };\n}\n","// Full Theme type + runtime appearance application.\n// design-system.md § Theme token shape, § Behavior (appearance application)\n\nimport {\n baseFontSize,\n baseLineHeight,\n borderRadius,\n borderWidth,\n buildShadows,\n DEFAULT_MONO_FONT,\n DEFAULT_UI_FONT,\n fontWeight,\n iconSize,\n opacity,\n spacing,\n type FontSizeKey,\n type ShadowSet,\n} from \"./tokens.js\";\nimport { type ThemeColors } from \"./colors.js\";\nimport { type ThemeName, THEME_VARIANTS } from \"./variants.js\";\n\nexport type Theme = {\n name: ThemeName;\n colorScheme: \"light\" | \"dark\";\n colors: ThemeColors;\n spacing: typeof spacing;\n // fontSize is Record<FontSizeKey, number> (not readonly narrowed literals) so appearance\n // patch can replace values without a type error.\n fontSize: Record<FontSizeKey, number>;\n fontFamily: {\n // Both are plain strings so custom fonts can be spliced in at runtime.\n ui: string;\n mono: string;\n };\n lineHeight: Record<\"diff\", number>;\n iconSize: typeof iconSize;\n fontWeight: typeof fontWeight;\n borderRadius: typeof borderRadius;\n borderWidth: typeof borderWidth;\n opacity: typeof opacity;\n shadow: ShadowSet;\n swatch: string;\n};\n\nfunction buildTheme(name: ThemeName): Theme {\n const variant = THEME_VARIANTS[name];\n return {\n name,\n colorScheme: variant.colorScheme,\n colors: variant.colors,\n spacing,\n fontSize: { ...baseFontSize },\n fontFamily: {\n ui: DEFAULT_UI_FONT,\n mono: DEFAULT_MONO_FONT,\n },\n lineHeight: { ...baseLineHeight },\n iconSize,\n fontWeight,\n borderRadius,\n borderWidth,\n opacity,\n shadow: buildShadows(variant.colorScheme),\n swatch: variant.swatch,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Appearance settings + runtime patch\n// ---------------------------------------------------------------------------\n\nexport type AppearanceSettings = {\n /** One of the six named theme variants. */\n themeName?: ThemeName;\n /** Custom UI font family string; empty/undefined → platform default. */\n uiFont?: string;\n /** Custom mono font family string; empty/undefined → platform default. */\n monoFont?: string;\n /**\n * Desired base font size (px). All fontSize tokens are scaled relative to the\n * documented base of 16px. Clamped 10–24.\n */\n fontSize?: number;\n};\n\nconst FONT_SIZE_BASE = 16;\nconst FONT_SIZE_MIN = 10;\nconst FONT_SIZE_MAX = 24;\n\n/**\n * Apply appearance settings to a base theme and return a new patched theme.\n * The base theme object is never mutated.\n * Code/mono surfaces keep the mono font because they reference `fontFamily.mono`\n * independently — a custom `uiFont` only patches `fontFamily.ui`.\n */\nexport function applyAppearance(base: Theme, settings: AppearanceSettings): Theme {\n const themeName: ThemeName = settings.themeName ?? base.name;\n const baseTheme = buildTheme(themeName);\n\n const uiFont =\n settings.uiFont && settings.uiFont.trim() !== \"\" ? settings.uiFont : DEFAULT_UI_FONT;\n const monoFont =\n settings.monoFont && settings.monoFont.trim() !== \"\" ? settings.monoFont : DEFAULT_MONO_FONT;\n\n let fontSize = { ...baseTheme.fontSize };\n if (settings.fontSize != null) {\n const clamped = Math.max(FONT_SIZE_MIN, Math.min(FONT_SIZE_MAX, settings.fontSize));\n const scale = clamped / FONT_SIZE_BASE;\n const key: FontSizeKey[] = [\"xs\", \"code\", \"sm\", \"base\", \"lg\", \"xl\", \"2xl\", \"3xl\", \"4xl\"];\n for (const k of key) {\n const base = baseFontSize[k];\n // `code` always tracks the xs/code document base, not the prose base, to avoid\n // code surfaces growing disproportionately.\n fontSize[k] = Math.round(base * scale);\n }\n }\n\n return {\n ...baseTheme,\n fontSize,\n fontFamily: { ui: uiFont, mono: monoFont },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Theme registry / accessor\n// ---------------------------------------------------------------------------\n\n/** Build a fresh Theme for the given name with default appearance settings. */\nexport function getTheme(name: ThemeName): Theme {\n return buildTheme(name);\n}\n\nexport const DEFAULT_THEME_NAME: ThemeName = \"dark\";\nexport { type ThemeName };\n","/**\n * Theme → CSS custom properties bridge.\n * Flattens a Theme object into `--pi-*` CSS variables on :root.\n * design-system.md § Purpose (render-stack decision)\n */\n\nimport { type Theme } from \"./theme.js\";\nimport { type ThemeColors, type SyntaxColors, type TerminalTheme } from \"./colors.js\";\n\n// ---------------------------------------------------------------------------\n// Token → variable-name map\n// ---------------------------------------------------------------------------\n\n/**\n * Given a Theme, produces a flat Record<`--pi-${path}`, value> map\n * suitable for setting on `document.documentElement.style`.\n */\nexport function flattenThemeToVars(theme: Theme): Record<string, string> {\n const vars: Record<string, string> = {};\n\n // Colors (flat semantic tokens)\n flattenColors(theme.colors, vars);\n\n // Spacing\n for (const [key, val] of Object.entries(theme.spacing)) {\n vars[`--pi-spacing-${key}`] = `${val}px`;\n }\n\n // Font size\n for (const [key, val] of Object.entries(theme.fontSize)) {\n vars[`--pi-font-size-${key}`] = `${val}px`;\n }\n\n // Font family\n vars[\"--pi-font-ui\"] = theme.fontFamily.ui;\n vars[\"--pi-font-mono\"] = theme.fontFamily.mono;\n\n // Line height\n vars[\"--pi-line-height-diff\"] = `${theme.lineHeight.diff}px`;\n\n // Icon size\n for (const [key, val] of Object.entries(theme.iconSize)) {\n vars[`--pi-icon-size-${key}`] = `${val}px`;\n }\n\n // Font weight\n for (const [key, val] of Object.entries(theme.fontWeight)) {\n vars[`--pi-font-weight-${key}`] = String(val);\n }\n\n // Border radius\n for (const [key, val] of Object.entries(theme.borderRadius)) {\n vars[`--pi-radius-${key}`] = val === 9999 ? \"9999px\" : `${val}px`;\n }\n\n // Border width\n for (const [key, val] of Object.entries(theme.borderWidth)) {\n vars[`--pi-border-width-${key}`] = `${val}px`;\n }\n\n // Opacity\n for (const [key, val] of Object.entries(theme.opacity)) {\n vars[`--pi-opacity-${key}`] = String(val);\n }\n\n // Shadow (convert { offsetX, offsetY, radius, color } → CSS box-shadow string)\n for (const [key, val] of Object.entries(theme.shadow)) {\n if (typeof val === \"string\") {\n vars[`--pi-shadow-${key}`] = val;\n } else if (val && typeof val === \"object\") {\n const s = val as { offsetX: number; offsetY: number; radius: number; color: string };\n vars[`--pi-shadow-${key}`] = `${s.offsetX}px ${s.offsetY}px ${s.radius}px ${s.color}`;\n }\n }\n\n // Color scheme marker (for prefers-color-scheme media query override)\n vars[\"--pi-color-scheme\"] = theme.colorScheme;\n\n return vars;\n}\n\nfunction flattenColors(colors: ThemeColors, vars: Record<string, string>): void {\n // Top-level semantic color tokens\n const {\n palette: _palette,\n syntax: _syntax,\n terminal: _terminal,\n ...semanticColors\n } = colors;\n\n // Emit color tokens VERBATIM (camelCase), e.g. `--pi-color-surfaceSidebar`,\n // `--pi-color-foregroundMuted`, `--pi-color-statusDanger`. This matches how\n // every component references them; kebab-casing here silently broke all\n // multi-word tokens (they fell back to defaults).\n for (const [key, val] of Object.entries(semanticColors)) {\n if (typeof val === \"string\") {\n vars[`--pi-color-${key}`] = val;\n }\n }\n\n // Syntax colors → --syntax-*\n if (_syntax) {\n flattenSyntax(_syntax, vars);\n }\n\n // Terminal colors are not CSS variables (consumed by xterm config directly);\n // but we expose a few key ones for CSS terminal-wrapper styling.\n if (_terminal) {\n vars[\"--pi-terminal-bg\"] = _terminal.background;\n vars[\"--pi-terminal-fg\"] = _terminal.foreground;\n }\n}\n\nfunction flattenSyntax(syntax: SyntaxColors, vars: Record<string, string>): void {\n for (const [key, val] of Object.entries(syntax)) {\n if (typeof val === \"string\") {\n vars[`--syntax-${camelToKebab(key)}`] = val;\n }\n }\n}\n\n/** Convert camelCase to kebab-case. */\nfunction camelToKebab(str: string): string {\n return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);\n}\n\n// ---------------------------------------------------------------------------\n// DOM application\n// ---------------------------------------------------------------------------\n\n/**\n * Apply a flattened variable map to the document root.\n * Idempotent — re-applying the same map is a no-op (DOM diffing).\n */\nexport function applyVarsToRoot(vars: Record<string, string>): void {\n if (typeof document === \"undefined\") return;\n const style = document.documentElement.style;\n for (const [prop, val] of Object.entries(vars)) {\n style.setProperty(prop, val);\n }\n // Set color-scheme for native form control theming\n const scheme = vars[\"--pi-color-scheme\"];\n if (scheme) {\n document.documentElement.style.setProperty(\"color-scheme\", scheme);\n }\n}\n\n/**\n * Full pipeline: flatten a Theme and apply to the DOM.\n */\nexport function applyThemeToDOM(theme: Theme): void {\n const vars = flattenThemeToVars(theme);\n applyVarsToRoot(vars);\n}\n","// Brand config schema + resolver.\n// features/white-label-branding.md § Public Contract, § Resolution\n\nimport { z } from \"zod\";\nimport { contrastForeground, isHexColor, lighten } from \"@pi-studio-ui/theme/color-utils.js\";\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst AssetRefSchema = z.string().min(1, \"Asset path must not be empty\");\n\nconst BrandColorsSchema = z.object({\n accent: z.string().refine(isHexColor, { message: \"accent must be a valid #rrggbb hex color\" }),\n accentBright: z\n .string()\n .refine(isHexColor, { message: \"accentBright must be a valid #rrggbb hex color\" })\n .optional(),\n accentForeground: z\n .string()\n .refine(isHexColor, { message: \"accentForeground must be a valid #rrggbb hex color\" })\n .optional(),\n swatch: z\n .string()\n .refine(isHexColor, { message: \"swatch must be a valid #rrggbb hex color\" })\n .optional(),\n});\n\nconst BrandAssetsSchema = z.object({\n logoLight: AssetRefSchema,\n logoDark: AssetRefSchema,\n logoMark: AssetRefSchema.optional(),\n icon: AssetRefSchema,\n splash: AssetRefSchema.optional(),\n});\n\nconst BrandLinksSchema = z\n .object({\n docs: z.string().url().optional(),\n support: z.string().url().optional(),\n issues: z.string().url().optional(),\n website: z.string().url().optional(),\n })\n .optional();\n\nconst BrandLegalSchema = z\n .object({\n copyright: z.string().optional(),\n companyName: z.string().optional(),\n })\n .optional();\n\nexport const BrandConfigSchema = z.object({\n productName: z.string().min(1, \"productName is required\"),\n shortName: z.string().optional(),\n tagline: z.string().optional(),\n colors: BrandColorsSchema,\n assets: BrandAssetsSchema,\n links: BrandLinksSchema,\n legal: BrandLegalSchema,\n});\n\nexport type BrandConfig = z.infer<typeof BrandConfigSchema>;\n\n// ---------------------------------------------------------------------------\n// Derived brand colors (fill in optional accent fields if omitted)\n// ---------------------------------------------------------------------------\n\nexport type ResolvedBrandColors = Required<\n Pick<BrandConfig[\"colors\"], \"accent\" | \"accentBright\" | \"accentForeground\" | \"swatch\">\n>;\n\nexport function resolveAccentColors(colors: BrandConfig[\"colors\"]): ResolvedBrandColors {\n const accent = colors.accent;\n const accentBright = colors.accentBright ?? lighten(accent, 0.35);\n const accentForeground = colors.accentForeground ?? contrastForeground(accent);\n const swatch = colors.swatch ?? accent;\n return { accent, accentBright, accentForeground, swatch };\n}\n\n// ---------------------------------------------------------------------------\n// Default Pi-Studio brand\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_BRAND: BrandConfig = {\n productName: \"Pi-Studio\",\n shortName: \"Pi-Studio\",\n tagline: \"Your local-first AI coding agent\",\n colors: {\n accent: \"#2e5cb8\",\n accentBright: \"#a2b4d7\",\n accentForeground: \"#ffffff\",\n swatch: \"#3b62b0\",\n },\n assets: {\n logoLight: \"brand/logo-light.svg\",\n logoDark: \"brand/logo-dark.svg\",\n logoMark: \"brand/logo-mark.svg\",\n icon: \"brand/icon.png\",\n splash: \"brand/splash.png\",\n },\n links: {\n docs: \"https://pi-studio.dev/docs\",\n issues: \"https://github.com/av-pi-studio/pi-studio/issues\",\n website: \"https://pi-studio.dev\",\n },\n legal: {\n companyName: \"Pi-Studio Contributors\",\n },\n};\n\n// ---------------------------------------------------------------------------\n// Resolver\n// ---------------------------------------------------------------------------\n\nexport type BrandResolution =\n | { ok: true; brand: BrandConfig; source: \"default\" | \"env\" }\n | { ok: false; errors: string[] };\n\n/**\n * Resolve the active brand config.\n *\n * At build time: reads PI_STUDIO_BRAND_DIR from the environment, loads and\n * validates <dir>/brand.config.json, fails fast with field-level errors.\n * When unset: returns the default Pi-Studio brand.\n *\n * In Node unit tests `rawConfig` is injected directly to skip file I/O.\n */\nexport function resolveBrandConfig(rawConfig?: unknown): BrandResolution {\n if (rawConfig === undefined) {\n // No injection → use built-in default.\n return { ok: true, brand: DEFAULT_BRAND, source: \"default\" };\n }\n\n const result = BrandConfigSchema.safeParse(rawConfig);\n if (!result.success) {\n const errors = result.error.errors.map(\n (e) => `${e.path.join(\".\")}: ${e.message}`,\n );\n return { ok: false, errors };\n }\n\n // Fill optional shortName default.\n const brand = result.data;\n if (!brand.shortName) brand.shortName = brand.productName;\n\n return { ok: true, brand, source: \"env\" };\n}\n\n// ---------------------------------------------------------------------------\n// Runtime accessor (frozen after first resolution)\n// ---------------------------------------------------------------------------\n\nlet _resolved: BrandConfig | null = null;\n\nexport function getActiveBrand(): BrandConfig {\n if (!_resolved) _resolved = DEFAULT_BRAND;\n return _resolved;\n}\n\n/** Inject a validated brand (used in tests and by the build-time bootstrap). */\nexport function _setActiveBrand(brand: BrandConfig): void {\n _resolved = brand;\n}\n\nexport function _resetActiveBrand(): void {\n _resolved = null;\n}\n","// Brand accent injection into theme variants.\n// design-system.md § Theme variants (Brand injection)\n// features/white-label-branding.md § Behavior & Algorithms\n\nimport { type ResolvedBrandColors, resolveAccentColors, type BrandConfig } from \"./config.js\";\nimport { type Theme } from \"@pi-studio-ui/theme/theme.js\";\nimport { type ThemeName, THEME_NAMES } from \"@pi-studio-ui/theme/variants.js\";\nimport { getTheme } from \"@pi-studio-ui/theme/theme.js\";\n\n/**\n * Build a branded copy of a theme by replacing only the accent family tokens\n * and swatch. All other tokens (surfaces, semantic status, syntax, terminal)\n * remain unchanged — only the accent family is brand-overridable in this version.\n */\nexport function injectBrandAccent(theme: Theme, resolved: ResolvedBrandColors): Theme {\n return {\n ...theme,\n colors: {\n ...theme.colors,\n accent: resolved.accent,\n accentBright: resolved.accentBright,\n accentForeground: resolved.accentForeground,\n // Legacy aliases that mirror accent.\n primary: resolved.accent,\n ring: resolved.accent,\n },\n swatch: resolved.swatch,\n };\n}\n\n/**\n * Build all six branded themes from a brand config.\n * Returns a map from theme name → patched Theme.\n */\nexport function buildBrandedThemes(\n brand: BrandConfig,\n): Readonly<Record<ThemeName, Theme>> {\n const resolved = resolveAccentColors(brand.colors);\n const result = {} as Record<ThemeName, Theme>;\n for (const name of THEME_NAMES) {\n result[name] = injectBrandAccent(getTheme(name), resolved);\n }\n return result;\n}\n","/**\n * Appearance store: persisted theme + appearance settings, system dark-mode following.\n * design-system.md § Theme variants, § Behavior (Appearance)\n */\n\nimport { type ThemeName, THEME_NAMES } from \"./variants.js\";\nimport { type AppearanceSettings, getTheme, applyAppearance, DEFAULT_THEME_NAME } from \"./theme.js\";\nimport { type Theme } from \"./theme.js\";\nimport { applyThemeToDOM } from \"./css-bridge.js\";\nimport { injectBrandAccent } from \"@pi-studio-ui/brand/theme-injection.js\";\nimport { resolveAccentColors, type BrandConfig } from \"@pi-studio-ui/brand/config.js\";\n\n// ---------------------------------------------------------------------------\n// KeyValueStore interface (localStorage on web, Electron bridge on desktop)\n// ---------------------------------------------------------------------------\n\nexport interface KeyValueStore {\n get(key: string): string | null;\n set(key: string, value: string): void;\n}\n\nconst STORAGE_KEY = \"pi-studio-appearance\";\n\n// ---------------------------------------------------------------------------\n// Appearance state\n// ---------------------------------------------------------------------------\n\nexport type AppearanceMode = ThemeName | \"system\";\n\nexport interface AppearanceState {\n mode: AppearanceMode;\n settings: AppearanceSettings;\n resolvedTheme: Theme;\n}\n\n// ---------------------------------------------------------------------------\n// Appearance controller\n// ---------------------------------------------------------------------------\n\nexport interface AppearanceController {\n getState(): AppearanceState;\n setMode(mode: AppearanceMode): void;\n updateSettings(patch: Partial<AppearanceSettings>): void;\n /** Call to apply current theme to DOM (call on init + changes). */\n apply(): void;\n /** Start system-follow listener; returns cleanup. */\n listen(): () => void;\n}\n\nexport function createAppearanceController(\n store: KeyValueStore,\n brandConfig?: BrandConfig,\n): AppearanceController {\n let state: AppearanceState = loadInitial(store);\n let mediaCleanup: (() => void) | null = null;\n\n function resolveThemeName(mode: AppearanceMode): ThemeName {\n if (mode === \"system\") {\n const prefersDark =\n typeof window !== \"undefined\" &&\n window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n return prefersDark ? \"dark\" : \"light\";\n }\n return mode;\n }\n\n function buildResolved(mode: AppearanceMode, settings: AppearanceSettings): Theme {\n const themeName = resolveThemeName(mode);\n const base = getTheme(themeName);\n let theme = applyAppearance(base, { ...settings, themeName });\n if (brandConfig?.colors) {\n const resolved = resolveAccentColors(brandConfig.colors);\n theme = injectBrandAccent(theme, resolved);\n }\n return theme;\n }\n\n function persist(): void {\n const serialized = JSON.stringify({ mode: state.mode, settings: state.settings });\n store.set(STORAGE_KEY, serialized);\n }\n\n function loadInitial(store: KeyValueStore): AppearanceState {\n const raw = store.get(STORAGE_KEY);\n let mode: AppearanceMode = DEFAULT_THEME_NAME;\n let settings: AppearanceSettings = {};\n if (raw) {\n try {\n const parsed = JSON.parse(raw);\n if (parsed.mode && (THEME_NAMES.includes(parsed.mode) || parsed.mode === \"system\")) {\n mode = parsed.mode;\n }\n if (parsed.settings && typeof parsed.settings === \"object\") {\n settings = parsed.settings;\n }\n } catch {\n // ignore corrupt data\n }\n }\n const resolvedTheme = buildResolved(mode, settings);\n return { mode, settings, resolvedTheme };\n }\n\n function update(): void {\n state = { ...state, resolvedTheme: buildResolved(state.mode, state.settings) };\n persist();\n }\n\n const controller: AppearanceController = {\n getState() {\n return state;\n },\n setMode(mode) {\n state = { ...state, mode };\n update();\n controller.apply();\n },\n updateSettings(patch) {\n state = { ...state, settings: { ...state.settings, ...patch } };\n update();\n controller.apply();\n },\n apply() {\n applyThemeToDOM(state.resolvedTheme);\n },\n listen() {\n if (typeof window === \"undefined\") return () => {};\n const mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n const handler = () => {\n if (state.mode === \"system\") {\n update();\n controller.apply();\n }\n };\n mq.addEventListener(\"change\", handler);\n mediaCleanup = () => mq.removeEventListener(\"change\", handler);\n return mediaCleanup;\n },\n };\n\n return controller;\n}\n","/**\n * ThemeBoundary — applies theme CSS variables before first paint.\n * Wraps the app so no flash of wrong theme occurs.\n */\n\nimport { useEffect, useRef, type ReactNode } from \"react\";\nimport {\n createAppearanceController,\n type AppearanceController,\n type KeyValueStore,\n} from \"./appearance-store.js\";\nimport { type BrandConfig } from \"@pi-studio-ui/brand/config.js\";\n\nexport interface ThemeBoundaryProps {\n store: KeyValueStore;\n brandConfig?: BrandConfig;\n children: ReactNode;\n}\n\n/**\n * Synchronously applies theme on first render, then listens for system changes.\n */\nexport function ThemeBoundary({ store, brandConfig, children }: ThemeBoundaryProps) {\n const controllerRef = useRef<AppearanceController | null>(null);\n\n if (!controllerRef.current) {\n controllerRef.current = createAppearanceController(store, brandConfig);\n // Apply synchronously before first paint\n controllerRef.current.apply();\n }\n\n useEffect(() => {\n const cleanup = controllerRef.current!.listen();\n return cleanup;\n }, []);\n\n return <>{children}</>;\n}\n","import { type KeyValueStore } from \"@pi-studio-ui/theme/appearance-store.js\";\n\n// Web KeyValueStore backed by localStorage. In Electron a bridge-backed store\n// can be substituted at the same DI boundary (ThemeBoundary `store` prop).\nexport const localKvStore: KeyValueStore = {\n get(key) {\n try {\n return globalThis.localStorage?.getItem(key) ?? null;\n } catch {\n return null;\n }\n },\n set(key, value) {\n try {\n globalThis.localStorage?.setItem(key, value);\n } catch {\n // Ignore quota / unavailable storage.\n }\n },\n};\n","import { type ReactNode } from \"react\";\nimport { QueryClientProvider } from \"@tanstack/react-query\";\nimport { queryClient } from \"@pi-studio-ui/lib/connection/query-client.js\";\nimport { ThemeBoundary } from \"@pi-studio-ui/theme/ThemeBoundary.js\";\nimport { localKvStore } from \"./kv-store.js\";\n\n// Top-level provider composition. ThemeBoundary applies `--pi-*` CSS variables\n// before first paint; TanStack Query owns server-cache state. Router and\n// connection context are added as their phases land.\nexport function AppProviders({ children }: { children: ReactNode }) {\n return (\n <ThemeBoundary store={localKvStore}>\n <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>\n </ThemeBoundary>\n );\n}\n","// Button primitive — variant/size/state contracts.\n// ui-components.md § Pressables\n\nexport type ButtonVariant = \"default\" | \"secondary\" | \"outline\" | \"ghost\" | \"destructive\";\nexport type ButtonSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\n// Min-height per size (px) per ui-components.md: \"min-heights from 28\".\nexport const BUTTON_MIN_HEIGHT: Record<ButtonSize, number> = {\n xs: 28,\n sm: 32,\n md: 36,\n lg: 40,\n};\n\n// Icon size per button size (maps to iconSize token keys).\nexport const BUTTON_ICON_SIZE: Record<ButtonSize, number> = {\n xs: 12,\n sm: 14,\n md: 16,\n lg: 20,\n};\n\n// Horizontal padding per size.\nexport const BUTTON_PADDING_H: Record<ButtonSize, number> = {\n xs: 8,\n sm: 10,\n md: 12,\n lg: 16,\n};\n\n// Font size per size (px).\nexport const BUTTON_FONT_SIZE: Record<ButtonSize, number> = {\n xs: 12,\n sm: 13,\n md: 14,\n lg: 15,\n};\n\nexport type ButtonStateStyle = {\n opacity: number;\n /** Token key for background color. null = no bg (transparent). */\n bgToken: string | null;\n};\n\n/**\n * Resolve the opacity and background token for a button given its state.\n * - pressed → 0.85 opacity\n * - disabled or loading → 0.5 opacity\n * - ghost hover → special token swap (handled by caller reading `isGhostHover`)\n */\nexport function resolveButtonState(opts: {\n variant: ButtonVariant;\n pressed: boolean;\n disabled: boolean;\n loading: boolean;\n}): { opacity: number; isGhostHover: false } {\n const { pressed, disabled, loading } = opts;\n if (disabled || loading) return { opacity: 0.5, isGhostHover: false };\n if (pressed) return { opacity: 0.85, isGhostHover: false };\n return { opacity: 1, isGhostHover: false };\n}\n\n/**\n * Returns the correct text/icon color token key for each variant.\n * This is what callers pass to the theme-prop binder for icon color.\n */\nexport function buttonIconColorToken(variant: ButtonVariant): string {\n switch (variant) {\n case \"default\":\n return \"accentForeground\";\n case \"secondary\":\n return \"foreground\";\n case \"outline\":\n return \"accent\";\n case \"ghost\":\n return \"foregroundMuted\"; // swaps to \"foreground\" on hover\n case \"destructive\":\n return \"destructiveForeground\";\n }\n}\n\n/** Returns \"foreground\" when ghost is hovered, else the default ghost color. */\nexport function ghostHoverIconToken(isHovered: boolean): string {\n return isHovered ? \"foreground\" : \"foregroundMuted\";\n}\n","/**\n * Button — pressable with variant/size/state, styled from --pi-* CSS variables.\n * ui-components.md § Pressables\n */\n\nimport { type ReactNode, type ButtonHTMLAttributes } from \"react\";\nimport { clsx } from \"clsx\";\nimport { Loader2 } from \"lucide-react\";\nimport styles from \"./Button.module.css\";\nimport {\n type ButtonVariant,\n type ButtonSize,\n BUTTON_MIN_HEIGHT,\n BUTTON_PADDING_H,\n BUTTON_FONT_SIZE,\n BUTTON_ICON_SIZE,\n resolveButtonState,\n} from \"@pi-studio-ui/ui/button.js\";\n\nexport interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"children\"> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n loading?: boolean;\n leftIcon?: ReactNode;\n trailing?: ReactNode;\n iconOnly?: boolean;\n children?: ReactNode;\n}\n\nexport function Button({\n variant = \"default\",\n size = \"md\",\n loading = false,\n disabled = false,\n leftIcon,\n trailing,\n iconOnly = false,\n children,\n style,\n className,\n ...rest\n}: ButtonProps) {\n const { opacity } = resolveButtonState({\n variant,\n pressed: false,\n disabled: disabled ?? false,\n loading,\n });\n const isDisabled = disabled || loading;\n\n const iconPx = BUTTON_ICON_SIZE[size];\n\n return (\n <button\n className={clsx(\n styles.btn,\n styles[variant],\n iconOnly && styles.iconOnly,\n className,\n )}\n disabled={isDisabled}\n aria-busy={loading || undefined}\n style={{\n minHeight: BUTTON_MIN_HEIGHT[size],\n paddingLeft: iconOnly ? undefined : BUTTON_PADDING_H[size],\n paddingRight: iconOnly ? undefined : BUTTON_PADDING_H[size],\n fontSize: BUTTON_FONT_SIZE[size],\n opacity,\n ...(iconOnly ? { width: BUTTON_MIN_HEIGHT[size] } : {}),\n ...style,\n }}\n {...rest}\n >\n {loading ? (\n <Loader2 className={styles.loadingSpinner} size={iconPx} aria-hidden />\n ) : leftIcon ? (\n <span aria-hidden style={{ display: \"flex\", alignItems: \"center\", flexShrink: 0 }}>\n {leftIcon}\n </span>\n ) : null}\n {children}\n {trailing && !loading && (\n <span aria-hidden style={{ display: \"flex\", alignItems: \"center\", flexShrink: 0 }}>\n {trailing}\n </span>\n )}\n </button>\n );\n}\n","/**\n * TextInput / TextArea — form input controls.\n * ui-components.md § Inputs & form controls (AdaptiveTextInput)\n */\n\nimport {\n forwardRef,\n type InputHTMLAttributes,\n type TextareaHTMLAttributes,\n} from \"react\";\nimport { clsx } from \"clsx\";\nimport styles from \"./TextInput.module.css\";\n\n// ---- TextInput ----\n\nexport interface TextInputProps extends InputHTMLAttributes<HTMLInputElement> {\n className?: string;\n}\n\nexport const TextInput = forwardRef<HTMLInputElement, TextInputProps>(\n function TextInput({ className, ...rest }, ref) {\n return (\n <input\n ref={ref}\n className={clsx(styles.input, className)}\n {...rest}\n />\n );\n },\n);\n\n// ---- TextArea ----\n\nexport interface TextAreaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {\n className?: string;\n}\n\nexport const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(\n function TextArea({ className, ...rest }, ref) {\n return (\n <textarea\n ref={ref}\n className={clsx(styles.input, styles.textarea, className)}\n {...rest}\n />\n );\n },\n);\n","import { z } from \"zod\";\n\n/**\n * Shared validation conventions for Pi-Studio.\n *\n * Every wire frame and every on-disk JSON document is parsed through Zod before it is trusted\n * (MAIN-SCOPE.md §9). These helpers are the backbone of \"validate at every boundary\" and are\n * reused by the protocol message schemas and the file-based persistence stores.\n *\n * ## Append-only compatibility rules (always)\n *\n * Schemas are **append-only**. A 6-month-old client must still parse new-daemon messages and\n * vice-versa. Concretely:\n *\n * - **New fields are optional with a default or transform.** Use {@link optionalWithDefault} so a\n * missing field deserializes to a known value instead of `undefined`.\n * - **Never flip `optional` → `required`.** Once a field is optional it stays optional.\n * - **Never remove a field.** Stop *sending* it if you must, but keep *reading* it.\n * - **Never narrow a type** (e.g. `string` → enum, `nullable` → non-null). New enum values are\n * gated at serialization behind capability flags so old clients only ever receive values they\n * advertised support for.\n *\n * ## COMPAT(name) convention\n *\n * Back-compat shims are tagged so a single grep (`COMPAT(`) lists all pending cleanup work. Each\n * tag records the version the shim was added in and the date it may be removed. Use {@link COMPAT}\n * at the call site of a shim, e.g.:\n *\n * ```ts\n * // COMPAT(legacy-provider-key): added 1.2.0, remove by 2026-12-31\n * const normalized = COMPAT({ name: \"legacy-provider-key\", addedIn: \"1.2.0\", removeBy: \"2026-12-31\" });\n * ```\n */\n\n/** Metadata describing a back-compat shim. Grep `COMPAT(` to enumerate cleanup work. */\nexport interface CompatTag {\n /** Stable identifier for the shim (kebab-case). */\n readonly name: string;\n /** Version the shim was introduced in (semver). */\n readonly addedIn: string;\n /** ISO date (YYYY-MM-DD) after which the shim may be removed. */\n readonly removeBy: string;\n}\n\n/**\n * Marks a back-compat shim. Returns the tag unchanged so it can be used inline; its real value is\n * the grep-able `COMPAT(` token and the structured metadata it carries.\n */\nexport function COMPAT(tag: CompatTag): CompatTag {\n return tag;\n}\n\n/**\n * Append-only field helper: declares a schema field that is optional on the wire/disk but always\n * resolves to a concrete default once parsed. This is the sanctioned way to add new fields without\n * breaking older producers that omit them.\n */\nexport function optionalWithDefault<S extends z.ZodTypeAny>(\n schema: S,\n defaultValue: z.infer<S>,\n): z.ZodDefault<z.ZodOptional<S>> {\n return schema.optional().default(defaultValue);\n}\n\n/**\n * Parse `value` with `schema`, returning the parsed value on success or `defaults` on any failure.\n * Used by the file-based stores so corrupt/partial JSON falls back to defaults instead of crashing\n * the daemon (architecture/persistence.md — \"Corrupt/partial JSON → fall back to defaults\").\n */\nexport function safeParseOrDefault<S extends z.ZodTypeAny>(\n schema: S,\n value: unknown,\n defaults: z.infer<S>,\n): z.infer<S> {\n const result = schema.safeParse(value);\n return result.success ? (result.data as z.infer<S>) : defaults;\n}\n\n// ---------------------------------------------------------------------------\n// Shared primitive schemas\n// ---------------------------------------------------------------------------\n\n/**\n * ISO-8601 timestamp string (UTC `Z` or explicit offset), e.g. `2026-06-11T15:00:00.000Z`.\n * Timeline row timestamps are canonical, daemon-owned strings (MAIN-SCOPE.md §9).\n */\nexport const isoTimestampSchema = z.string().datetime({ offset: true });\n\n/** RFC-4122 UUID string (the primary key shape for agents, chat rooms, messages, etc.). */\nexport const uuidSchema = z.string().uuid();\n\n/**\n * base64url character set with no padding (`A–Z a–z 0–9 - _`). The encoding used for stable ids\n * like the daemon `server-id` (`srv_<base64url>`).\n */\nexport const base64UrlSchema = z.string().regex(/^[A-Za-z0-9_-]+$/, \"expected base64url\");\n\n/** Returns true if `value` is a non-empty base64url string. */\nexport function isBase64Url(value: string): boolean {\n return base64UrlSchema.safeParse(value).success;\n}\n\n/**\n * Schema for a prefixed base64url id of the form `${prefix}_<base64url>` (e.g. `srv_AbC-123`).\n */\nexport function prefixedIdSchema(prefix: string): z.ZodString {\n const escaped = prefix.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return z\n .string()\n .regex(new RegExp(`^${escaped}_[A-Za-z0-9_-]+$`), `expected ${prefix}_<base64url> id`);\n}\n","import { z } from \"zod\";\n\nimport { isoTimestampSchema } from \"./validation.js\";\n\n/**\n * WebSocket wire schemas (architecture/websocket-protocol.md).\n *\n * The protocol mixes JSON text frames (these schemas) with a small binary framing for\n * terminal/file streams (see `binary-frames/`). All schemas are append-only: new fields are\n * optional with defaults, types are never narrowed, fields are never removed.\n *\n * Built up across sprint-002:\n * - task-001: top-level envelopes + handshake.\n * - task-003: the session message family union (agent / timeline / permission core).\n */\n\n// ===========================================================================\n// Shared wire primitives\n// ===========================================================================\n\n/**\n * A wire timestamp. Accepts either an epoch-millis number or an ISO-8601 string so the type is\n * never narrowed across versions (append-only rule).\n */\nexport const wireTimestampSchema = z.union([z.number(), isoTimestampSchema]);\nexport type WireTimestamp = z.infer<typeof wireTimestampSchema>;\n\n// ===========================================================================\n// Handshake\n// ===========================================================================\n\n/** Client kinds that may connect. Desktop connects as `browser` (TODO(verify) MAIN-SCOPE). */\nexport const clientTypeSchema = z.enum([\"mobile\", \"browser\", \"cli\", \"mcp\"]);\nexport type ClientType = z.infer<typeof clientTypeSchema>;\n\n/**\n * Client → Server handshake. Must be the first frame on a connection. `capabilities` advertises\n * the `CLIENT_CAPS.*` flags the client supports (a string→boolean map; see client-capabilities.ts).\n */\nexport const helloSchema = z.object({\n type: z.literal(\"hello\"),\n clientId: z.string(),\n clientType: clientTypeSchema,\n protocolVersion: z.number(),\n appVersion: z.string().optional(),\n capabilities: z.record(z.string(), z.boolean()).optional(),\n});\nexport type Hello = z.infer<typeof helloSchema>;\n\n/**\n * The `server_info` payload carried by the `status` envelope. There is no dedicated welcome\n * message — the server emits this after accepting the hello, then begins streaming.\n */\nexport const serverInfoPayloadSchema = z.object({\n status: z.literal(\"server_info\"),\n serverId: z.string(),\n hostname: z.string().optional(),\n version: z.string().optional(),\n capabilities: z.record(z.string(), z.unknown()),\n features: z.record(z.string(), z.unknown()),\n});\nexport type ServerInfoPayload = z.infer<typeof serverInfoPayloadSchema>;\n\n/** Server → Client status envelope wrapping `server_info`. */\nexport const statusSchema = z.object({\n type: z.literal(\"status\"),\n payload: serverInfoPayloadSchema,\n});\nexport type Status = z.infer<typeof statusSchema>;\n\n// ===========================================================================\n// Liveness (JSON ping/pong, NOT RFC6455 ping — browsers/RN cannot access protocol ping)\n// ===========================================================================\n\nexport const pingSchema = z.object({\n type: z.literal(\"ping\"),\n requestId: z.string(),\n clientSentAt: wireTimestampSchema.optional(),\n});\nexport type Ping = z.infer<typeof pingSchema>;\n\nexport const pongSchema = z.object({\n type: z.literal(\"pong\"),\n requestId: z.string(),\n clientSentAt: wireTimestampSchema.optional(),\n serverReceivedAt: wireTimestampSchema,\n serverSentAt: wireTimestampSchema,\n});\nexport type Pong = z.infer<typeof pongSchema>;\n\n// ===========================================================================\n// RPC naming convention\n// ===========================================================================\n\nexport type RpcDirection = \"request\" | \"response\";\n\n/**\n * Builds a dotted RPC name: `domain.provider.operation.direction`\n * (e.g. `checkout.github.set_auto_merge.request`). Segments read left→right:\n * domain → provider/subsystem → operation (a verb) → direction. New RPCs use this form; legacy\n * flat names (e.g. `checkout_pr_merge_request`) remain *accepted* but must not be generated.\n */\nexport function rpcName(\n domain: string,\n providerOrSubsystem: string,\n operation: string,\n direction: RpcDirection,\n): string {\n return `${domain}.${providerOrSubsystem}.${operation}.${direction}`;\n}\n\n// ===========================================================================\n// Agent session config (create_agent_request)\n// ===========================================================================\n\n/** Status the daemon reports for an agent (architecture/persistence.md — `lastStatus`). */\nexport const agentStatusEnum = z.enum([\"initializing\", \"idle\", \"running\", \"error\", \"closed\"]);\nexport type AgentStatus = z.infer<typeof agentStatusEnum>;\n\nexport const imageAttachmentSchema = z\n .object({ mimeType: z.string().optional(), data: z.string().optional() })\n .passthrough();\nexport type ImageAttachment = z.infer<typeof imageAttachmentSchema>;\n\nexport const agentAttachmentsSchema = z\n .object({\n prs: z.array(z.unknown()).optional(),\n issues: z.array(z.unknown()).optional(),\n })\n .passthrough();\n\n/**\n * `AgentSessionConfig` — provider/mode/model and run options for an agent. `provider`/`modeId`/\n * `model`/`thinkingOptionId` are dynamic strings (discovered at runtime per agent-providers.md),\n * not fixed enums, so they are not narrowed here.\n */\nexport const agentSessionConfigSchema = z.object({\n provider: z.string(),\n cwd: z.string(),\n modeId: z.string().optional(),\n model: z.string().optional(),\n thinkingOptionId: z.string().optional(),\n featureValues: z.record(z.string(), z.unknown()).optional(),\n title: z.string().nullable().optional(),\n approvalPolicy: z.string().optional(),\n sandboxMode: z.string().optional(),\n networkAccess: z.boolean().optional(),\n webSearch: z.boolean().optional(),\n extra: z.object({ pi: z.unknown().optional() }).passthrough().optional(),\n systemPrompt: z.string().optional(),\n mcpServers: z.record(z.string(), z.unknown()).optional(),\n});\nexport type AgentSessionConfig = z.infer<typeof agentSessionConfigSchema>;\n\nexport const createAgentRequestSchema = z.object({\n type: z.literal(\"create_agent_request\"),\n requestId: z.string(),\n config: agentSessionConfigSchema,\n env: z.record(z.string(), z.string()).optional(),\n workspaceId: z.string().optional(),\n worktreeName: z.string().optional(),\n initialPrompt: z.string().optional(),\n clientMessageId: z.string().optional(),\n outputSchema: z.record(z.string(), z.unknown()).optional(),\n images: z.array(imageAttachmentSchema).optional(),\n attachments: agentAttachmentsSchema.optional(),\n git: z.unknown().optional(),\n worktree: z.unknown().optional(),\n autoArchive: z.boolean().optional(),\n labels: z.record(z.string(), z.string()).default({}),\n});\nexport type CreateAgentRequest = z.infer<typeof createAgentRequestSchema>;\n\nexport const createAgentResponseSchema = z.object({\n type: z.literal(\"create_agent_response\"),\n requestId: z.string(),\n payload: z.object({ agentId: z.string() }).passthrough(),\n});\nexport type CreateAgentResponse = z.infer<typeof createAgentResponseSchema>;\n\n// ===========================================================================\n// Agent lifecycle messages\n// ===========================================================================\n\nexport const agentUpdateSchema = z.object({\n type: z.literal(\"agent_update\"),\n agentId: z.string(),\n status: agentStatusEnum.optional(),\n title: z.string().nullable().optional(),\n labels: z.record(z.string(), z.string()).optional(),\n});\n\nexport const agentStatusMessageSchema = z.object({\n type: z.literal(\"agent_status\"),\n agentId: z.string(),\n status: agentStatusEnum,\n});\n\nexport const agentListSchema = z.object({\n type: z.literal(\"agent_list\"),\n agents: z.array(z.object({ id: z.string() }).passthrough()),\n});\n\nexport const agentDeletedSchema = z.object({\n type: z.literal(\"agent_deleted\"),\n agentId: z.string(),\n});\n\nexport const agentArchivedSchema = z.object({\n type: z.literal(\"agent_archived\"),\n agentId: z.string(),\n archivedAt: isoTimestampSchema.optional(),\n});\n\n// ===========================================================================\n// Tool-call details + stream events\n// ===========================================================================\n\n/** `ToolCallDetail` — normalized across providers, discriminated on `kind`. */\nexport const toolCallDetailSchema = z.discriminatedUnion(\"kind\", [\n z.object({ kind: z.literal(\"shell\"), command: z.string().optional(), output: z.string().optional() }),\n z.object({ kind: z.literal(\"read\"), path: z.string().optional(), output: z.string().optional() }),\n z.object({\n kind: z.literal(\"edit\"),\n path: z.string().optional(),\n diff: z.string().optional(),\n output: z.string().optional(),\n }),\n z.object({ kind: z.literal(\"write\"), path: z.string().optional(), output: z.string().optional() }),\n z.object({ kind: z.literal(\"search\"), query: z.string().optional(), output: z.string().optional() }),\n z.object({ kind: z.literal(\"fetch\"), url: z.string().optional(), output: z.string().optional() }),\n z.object({\n kind: z.literal(\"task\"),\n description: z.string().optional(),\n output: z.string().optional(),\n }),\n]);\nexport type ToolCallDetail = z.infer<typeof toolCallDetailSchema>;\n\n/** `AgentStreamEvent` — discriminated on `kind`. */\nexport const agentStreamEventSchema = z.discriminatedUnion(\"kind\", [\n z.object({\n kind: z.literal(\"user_message\"),\n messageId: z.string().optional(),\n text: z.string().optional(),\n images: z.array(imageAttachmentSchema).optional(),\n }),\n z.object({\n kind: z.literal(\"assistant_message\"),\n messageId: z.string().optional(),\n text: z.string().optional(),\n }),\n z.object({ kind: z.literal(\"reasoning\"), text: z.string().optional() }),\n z.object({\n kind: z.literal(\"tool_call\"),\n callId: z.string().optional(),\n tool: toolCallDetailSchema,\n status: z.string().optional(),\n }),\n z.object({ kind: z.literal(\"turn_started\"), turnId: z.string().optional() }),\n z.object({ kind: z.literal(\"turn_completed\"), turnId: z.string().optional() }),\n z.object({\n kind: z.literal(\"turn_failed\"),\n turnId: z.string().optional(),\n error: z.string().optional(),\n }),\n z.object({ kind: z.literal(\"turn_canceled\"), turnId: z.string().optional() }),\n z.object({ kind: z.literal(\"error\"), message: z.string().optional() }),\n]);\nexport type AgentStreamEvent = z.infer<typeof agentStreamEventSchema>;\n\nexport const agentStreamSchema = z.object({\n type: z.literal(\"agent_stream\"),\n agentId: z.string(),\n seq: z.number().optional(),\n timestamp: wireTimestampSchema.optional(),\n event: agentStreamEventSchema,\n});\n\n// ===========================================================================\n// Timeline fetch (authoritative paged history)\n// ===========================================================================\n\nexport const timelineDirectionEnum = z.enum([\"before\", \"after\"]);\nexport type TimelineDirection = z.infer<typeof timelineDirectionEnum>;\n\nexport const sourceSeqRangeSchema = z.object({ start: z.number(), end: z.number() });\nexport type SourceSeqRange = z.infer<typeof sourceSeqRangeSchema>;\n\nexport const fetchAgentTimelineRequestSchema = z.object({\n type: z.literal(\"fetch_agent_timeline_request\"),\n requestId: z.string(),\n agentId: z.string(),\n cursor: z.string().nullable().optional(),\n direction: timelineDirectionEnum,\n limit: z.number().optional(),\n});\nexport type FetchAgentTimelineRequest = z.infer<typeof fetchAgentTimelineRequestSchema>;\n\nexport const fetchAgentTimelineResponseSchema = z.object({\n type: z.literal(\"fetch_agent_timeline_response\"),\n requestId: z.string(),\n agentId: z.string().optional(),\n items: z.array(z.unknown()),\n seqStart: z.number(),\n seqEnd: z.number(),\n sourceSeqRanges: z.array(sourceSeqRangeSchema),\n collapsed: z.boolean(),\n hasNewer: z.boolean(),\n startCursor: z.string().nullable().optional(),\n endCursor: z.string().nullable().optional(),\n});\nexport type FetchAgentTimelineResponse = z.infer<typeof fetchAgentTimelineResponseSchema>;\n\n// ===========================================================================\n// Tool-call permissions\n// ===========================================================================\n\nexport const agentPermissionRequestSchema = z.object({\n type: z.literal(\"agent_permission_request\"),\n requestId: z.string(),\n agentId: z.string(),\n toolName: z.string().optional(),\n tool: toolCallDetailSchema.optional(),\n action: z.unknown().optional(),\n responses: z.array(z.string()).optional(),\n});\n\nexport const agentPermissionResolvedSchema = z.object({\n type: z.literal(\"agent_permission_resolved\"),\n requestId: z.string(),\n agentId: z.string(),\n decision: z.string().optional(),\n});\n\n/** respond-to-permission RPC (client → daemon), dotted name `agent.permission.respond.request`. */\nexport const respondToPermissionRequestSchema = z.object({\n type: z.literal(\"agent.permission.respond.request\"),\n requestId: z.string(),\n permissionRequestId: z.string(),\n response: z.unknown(),\n});\n\nexport const respondToPermissionResponseSchema = z.object({\n type: z.literal(\"agent.permission.respond.response\"),\n requestId: z.string(),\n payload: z.object({ resolved: z.boolean() }).passthrough(),\n});\n\n/** Legacy flat name for the same operation — still accepted (parsed), never generated. */\nexport const legacyRespondToPermissionSchema = z.object({\n type: z.literal(\"respond_to_permission\"),\n requestId: z.string(),\n permissionRequestId: z.string(),\n response: z.unknown(),\n});\n\n// ===========================================================================\n// ===========================================================================\n// Rewind RPC (features/rewind.md) — additive, new in sprint-015\n// ===========================================================================\n\nexport const rewindModeSchema = z.enum([\"conversation\", \"files\", \"both\"]);\nexport type RewindMode = z.infer<typeof rewindModeSchema>;\n\nexport const agentRewindRequestSchema = z.object({\n type: z.literal(\"agent.rewind.request\"),\n requestId: z.string(),\n agentId: z.string(),\n messageId: z.string(),\n mode: rewindModeSchema,\n}).passthrough();\nexport type AgentRewindRequest = z.infer<typeof agentRewindRequestSchema>;\n\nexport const agentRewindResponseSchema = z.object({\n type: z.literal(\"agent.rewind.response\"),\n requestId: z.string(),\n payload: z.object({\n agentId: z.string(),\n messageId: z.string(),\n mode: rewindModeSchema,\n truncatedAt: z.string().optional(),\n }).passthrough(),\n}).passthrough();\nexport type AgentRewindResponse = z.infer<typeof agentRewindResponseSchema>;\n\n// ===========================================================================\n// RPC error\n// ===========================================================================\n\nexport const rpcErrorSchema = z.object({\n type: z.literal(\"rpc_error\"),\n requestId: z.string(),\n code: z.string().optional(),\n message: z.string().optional(),\n error: z.string().optional(),\n});\nexport type RpcError = z.infer<typeof rpcErrorSchema>;\n\n// ===========================================================================\n// Session message union (agent / timeline / permission core)\n// ===========================================================================\n\n/**\n * The discriminated union of session messages defined so far. Later feature sprints extend this\n * union with workspace/git/terminal/chat/schedule/loop families.\n */\nexport const sessionMessageSchema = z.discriminatedUnion(\"type\", [\n createAgentRequestSchema,\n createAgentResponseSchema,\n agentUpdateSchema,\n agentStatusMessageSchema,\n agentListSchema,\n agentDeletedSchema,\n agentArchivedSchema,\n agentStreamSchema,\n fetchAgentTimelineRequestSchema,\n fetchAgentTimelineResponseSchema,\n agentPermissionRequestSchema,\n agentPermissionResolvedSchema,\n respondToPermissionRequestSchema,\n respondToPermissionResponseSchema,\n legacyRespondToPermissionSchema,\n agentRewindRequestSchema,\n agentRewindResponseSchema,\n rpcErrorSchema,\n]);\nexport type SessionMessage = z.infer<typeof sessionMessageSchema>;\n\n/**\n * Structural fallback for any session message (carries a `type` discriminant). Unknown/future\n * session types still parse here (append-only / \"ignore unknown type per handler policy\"), so the\n * envelope accepts them and handlers validate per-family with the specific schema above.\n */\nexport const sessionMessageBaseSchema = z.object({ type: z.string() }).passthrough();\nexport type SessionMessageBase = z.infer<typeof sessionMessageBaseSchema>;\n\n// ===========================================================================\n// Session envelope + top-level union\n// ===========================================================================\n\n/**\n * Envelope wrapping the rich session message union. Known message types validate strictly against\n * `sessionMessageSchema`; unknown/future types fall back to the structural base so an older client\n * still parses newer messages.\n */\nexport const sessionEnvelopeSchema = z.object({\n type: z.literal(\"session\"),\n message: z.union([sessionMessageSchema, sessionMessageBaseSchema]),\n});\nexport type SessionEnvelope = z.infer<typeof sessionEnvelopeSchema>;\n\n/** Discriminated union of all top-level envelopes, keyed by `type`. */\nexport const topLevelEnvelopeSchema = z.discriminatedUnion(\"type\", [\n helloSchema,\n statusSchema,\n pingSchema,\n pongSchema,\n sessionEnvelopeSchema,\n]);\nexport type TopLevelEnvelope = z.infer<typeof topLevelEnvelopeSchema>;\n","import { z } from \"zod\";\n\n/**\n * Terminal stream binary frame codec (architecture/websocket-protocol.md § Binary frames,\n * features/terminals.md § Binary stream protocol).\n *\n * Frame layout: `[1-byte opcode][1-byte slot][payload]`.\n * - `slot` demuxes multiple terminals on one socket (0–255).\n * - Output/Input/Snapshot/Restore payloads are raw bytes (pass-through).\n * - Resize payload is UTF-8 JSON `{ rows, cols }`.\n *\n * Uses `Uint8Array` (not Node `Buffer`) so the codec runs in browser/RN as well as Node.\n */\n\n/** Terminal frame opcodes. `Restore` value is TODO(verify) against the live codec. */\nexport const TerminalOpcode = {\n Output: 0x01,\n Input: 0x02,\n Resize: 0x03,\n Snapshot: 0x04,\n Restore: 0x05,\n} as const;\n\nexport type TerminalOpcodeName = keyof typeof TerminalOpcode;\nexport type TerminalOpcodeValue = (typeof TerminalOpcode)[TerminalOpcodeName];\n\nconst OPCODE_BY_VALUE: Record<number, TerminalOpcodeName> = Object.fromEntries(\n Object.entries(TerminalOpcode).map(([name, value]) => [value, name as TerminalOpcodeName]),\n);\n\n/** Resize payload schema (`{ rows, cols }`, non-negative integers). */\nexport const terminalResizeSchema = z.object({\n rows: z.number().int().nonnegative(),\n cols: z.number().int().nonnegative(),\n});\nexport type TerminalResize = z.infer<typeof terminalResizeSchema>;\n\nexport type TerminalFrame =\n | { opcode: \"Output\"; slot: number; data: Uint8Array }\n | { opcode: \"Input\"; slot: number; data: Uint8Array }\n | { opcode: \"Resize\"; slot: number; rows: number; cols: number }\n | { opcode: \"Snapshot\"; slot: number; data: Uint8Array }\n | { opcode: \"Restore\"; slot: number; data: Uint8Array };\n\n/** Thrown when a frame cannot be decoded (too short, unknown opcode, bad payload). */\nexport class TerminalFrameError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"TerminalFrameError\";\n }\n}\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\nfunction assertSlot(slot: number): void {\n if (!Number.isInteger(slot) || slot < 0 || slot > 0xff) {\n throw new TerminalFrameError(`slot must be an integer 0–255, got ${slot}`);\n }\n}\n\nfunction frameWithPayload(opcodeValue: number, slot: number, payload: Uint8Array): Uint8Array {\n const out = new Uint8Array(2 + payload.length);\n out[0] = opcodeValue;\n out[1] = slot;\n out.set(payload, 2);\n return out;\n}\n\n/** Encode a typed terminal frame to bytes. */\nexport function encodeTerminalFrame(frame: TerminalFrame): Uint8Array {\n assertSlot(frame.slot);\n switch (frame.opcode) {\n case \"Output\":\n return frameWithPayload(TerminalOpcode.Output, frame.slot, frame.data);\n case \"Input\":\n return frameWithPayload(TerminalOpcode.Input, frame.slot, frame.data);\n case \"Snapshot\":\n return frameWithPayload(TerminalOpcode.Snapshot, frame.slot, frame.data);\n case \"Restore\":\n return frameWithPayload(TerminalOpcode.Restore, frame.slot, frame.data);\n case \"Resize\": {\n const json = JSON.stringify(\n terminalResizeSchema.parse({ rows: frame.rows, cols: frame.cols }),\n );\n return frameWithPayload(TerminalOpcode.Resize, frame.slot, textEncoder.encode(json));\n }\n }\n}\n\n/** Decode bytes into a typed terminal frame. Throws `TerminalFrameError` on invalid input. */\nexport function decodeTerminalFrame(bytes: Uint8Array): TerminalFrame {\n if (bytes.length < 2) {\n throw new TerminalFrameError(`frame too short: ${bytes.length} bytes (need ≥ 2)`);\n }\n const opcodeValue = bytes[0] as number;\n const slot = bytes[1] as number;\n const opcode = OPCODE_BY_VALUE[opcodeValue];\n if (!opcode) {\n throw new TerminalFrameError(`unknown terminal opcode 0x${opcodeValue.toString(16)}`);\n }\n const payload = bytes.subarray(2);\n\n if (opcode === \"Resize\") {\n let parsed: unknown;\n try {\n parsed = JSON.parse(textDecoder.decode(payload));\n } catch {\n throw new TerminalFrameError(\"resize payload is not valid JSON\");\n }\n const result = terminalResizeSchema.safeParse(parsed);\n if (!result.success) {\n throw new TerminalFrameError(\"resize payload is not { rows, cols }\");\n }\n return { opcode: \"Resize\", slot, rows: result.data.rows, cols: result.data.cols };\n }\n\n // Output/Input/Snapshot/Restore: raw bytes pass through (copied so callers own the buffer).\n return { opcode, slot, data: payload.slice() };\n}\n\n/** Returns the decoded frame, or `null` instead of throwing (safe handling of unknown opcodes). */\nexport function tryDecodeTerminalFrame(bytes: Uint8Array): TerminalFrame | null {\n try {\n return decodeTerminalFrame(bytes);\n } catch {\n return null;\n }\n}\n","import { z } from \"zod\";\n\n/**\n * File-transfer binary frame codec (architecture/websocket-protocol.md § Binary frames,\n * features/file-explorer-transfer.md § Binary transfer frames). Separate from the terminal codec\n * but lives in the same module family.\n *\n * Frame layout: `[1-byte opcode][1-byte stream][payload]`.\n * - `stream` multiplexes concurrent transfers on one socket (0–255).\n * - A transfer is `Begin` (metadata header) → N × `Chunk` (raw bytes) → `End` (completion marker),\n * or `Error` to abort.\n *\n * NOTE: exact opcode values / chunk sizing / completion marker are TODO(verify) against the live\n * codec; this is a faithful chunked-with-completion-marker reconstruction from the scope.\n */\n\nexport const FileTransferOpcode = {\n Begin: 0x10,\n Chunk: 0x11,\n End: 0x12,\n Error: 0x13,\n} as const;\n\nexport type FileTransferOpcodeName = keyof typeof FileTransferOpcode;\n\nconst OPCODE_BY_VALUE: Record<number, FileTransferOpcodeName> = Object.fromEntries(\n Object.entries(FileTransferOpcode).map(([name, value]) => [\n value,\n name as FileTransferOpcodeName,\n ]),\n);\n\n/** Metadata header for a transfer (carried by the `Begin` frame as UTF-8 JSON). */\nexport const fileTransferBeginSchema = z.object({\n transferId: z.string(),\n fileName: z.string().optional(),\n mimeType: z.string().optional(),\n totalBytes: z.number().int().nonnegative().optional(),\n});\nexport type FileTransferBegin = z.infer<typeof fileTransferBeginSchema>;\n\nexport type FileTransferFrame =\n | { opcode: \"Begin\"; stream: number; meta: FileTransferBegin }\n | { opcode: \"Chunk\"; stream: number; data: Uint8Array }\n | { opcode: \"End\"; stream: number; ok: boolean }\n | { opcode: \"Error\"; stream: number; message: string };\n\nexport class FileTransferFrameError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FileTransferFrameError\";\n }\n}\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\nfunction assertStream(stream: number): void {\n if (!Number.isInteger(stream) || stream < 0 || stream > 0xff) {\n throw new FileTransferFrameError(`stream must be an integer 0–255, got ${stream}`);\n }\n}\n\nfunction frame(opcodeValue: number, stream: number, payload: Uint8Array): Uint8Array {\n const out = new Uint8Array(2 + payload.length);\n out[0] = opcodeValue;\n out[1] = stream;\n out.set(payload, 2);\n return out;\n}\n\nexport function encodeFileTransferFrame(f: FileTransferFrame): Uint8Array {\n assertStream(f.stream);\n switch (f.opcode) {\n case \"Begin\":\n return frame(\n FileTransferOpcode.Begin,\n f.stream,\n textEncoder.encode(JSON.stringify(fileTransferBeginSchema.parse(f.meta))),\n );\n case \"Chunk\":\n return frame(FileTransferOpcode.Chunk, f.stream, f.data);\n case \"End\":\n // Completion marker; payload encodes the ok/abort flag as a single byte.\n return frame(FileTransferOpcode.End, f.stream, new Uint8Array([f.ok ? 1 : 0]));\n case \"Error\":\n return frame(FileTransferOpcode.Error, f.stream, textEncoder.encode(f.message));\n }\n}\n\nexport function decodeFileTransferFrame(bytes: Uint8Array): FileTransferFrame {\n if (bytes.length < 2) {\n throw new FileTransferFrameError(`frame too short: ${bytes.length} bytes (need ≥ 2)`);\n }\n const opcodeValue = bytes[0] as number;\n const stream = bytes[1] as number;\n const opcode = OPCODE_BY_VALUE[opcodeValue];\n if (!opcode) {\n throw new FileTransferFrameError(`unknown file-transfer opcode 0x${opcodeValue.toString(16)}`);\n }\n const payload = bytes.subarray(2);\n\n switch (opcode) {\n case \"Begin\": {\n let parsed: unknown;\n try {\n parsed = JSON.parse(textDecoder.decode(payload));\n } catch {\n throw new FileTransferFrameError(\"begin payload is not valid JSON\");\n }\n const result = fileTransferBeginSchema.safeParse(parsed);\n if (!result.success) {\n throw new FileTransferFrameError(\"begin payload is not a valid transfer header\");\n }\n return { opcode: \"Begin\", stream, meta: result.data };\n }\n case \"Chunk\":\n return { opcode: \"Chunk\", stream, data: payload.slice() };\n case \"End\":\n return { opcode: \"End\", stream, ok: (payload[0] ?? 1) !== 0 };\n case \"Error\":\n return { opcode: \"Error\", stream, message: textDecoder.decode(payload) };\n }\n}\n\nexport function tryDecodeFileTransferFrame(bytes: Uint8Array): FileTransferFrame | null {\n try {\n return decodeFileTransferFrame(bytes);\n } catch {\n return null;\n }\n}\n","import { z } from \"zod\";\n\n/**\n * Provider manifest types (features/agent-providers.md § Registration surface, § Capability flags).\n *\n * The manifest is UI scaffolding only — actual models/modes/features are discovered at runtime via\n * the provider RPC. These types express provider definitions, mode metadata (icon + colorTier), and\n * the `AgentCapabilityFlags` shape.\n */\n\n/** Provider ids must match `/^[a-z][a-z0-9-]*$/` (agent-providers.md). */\nexport const providerIdSchema = z.string().regex(/^[a-z][a-z0-9-]*$/, \"invalid provider id\");\n\n/** Mode danger/intent tier driving UI color. */\nexport const colorTierSchema = z.enum([\"safe\", \"moderate\", \"dangerous\", \"planning\"]);\nexport type ColorTier = z.infer<typeof colorTierSchema>;\n\n/** Per-provider capability flags. */\nexport const agentCapabilityFlagsSchema = z.object({\n supportsStreaming: z.boolean(),\n supportsSessionPersistence: z.boolean(),\n supportsDynamicModes: z.boolean(),\n supportsMcpServers: z.boolean(),\n supportsReasoningStream: z.boolean(),\n supportsToolInvocations: z.boolean(),\n // Rewind capability flags (features/rewind.md) — additive, optional, old clients ignore.\n supportsRewindConversation: z.boolean().optional(),\n supportsRewindFiles: z.boolean().optional(),\n supportsRewindBoth: z.boolean().optional(),\n}).passthrough();\nexport type AgentCapabilityFlags = z.infer<typeof agentCapabilityFlagsSchema>;\n\n/** UI metadata for a provider mode (plan/default/full-access/...). */\nexport const providerModeSchema = z.object({\n id: z.string(),\n label: z.string().optional(),\n description: z.string().optional(),\n icon: z.string().optional(),\n colorTier: colorTierSchema,\n});\nexport type ProviderMode = z.infer<typeof providerModeSchema>;\n\n/** A provider manifest definition. `extends` marks a custom Pi-compatible profile. */\nexport const providerDefinitionSchema = z.object({\n id: providerIdSchema,\n label: z.string(),\n description: z.string().optional(),\n extends: z.string().optional(),\n modes: z.array(providerModeSchema),\n capabilities: agentCapabilityFlagsSchema,\n});\nexport type ProviderDefinition = z.infer<typeof providerDefinitionSchema>;\n","/**\n * Pluggable transport abstraction — direct WebSocket and (future) relay E2EE share this API.\n * See architecture/client-app-runtime.md § Layered client library.\n */\n\nexport type TransportMessageHandler = (data: string | ArrayBuffer | Blob) => void;\nexport type TransportCloseHandler = (code: number, reason: string) => void;\nexport type TransportErrorHandler = (error: unknown) => void;\n\nexport interface Transport {\n /** Initiate the connection. Resolves when the socket is open (raw, pre-handshake). */\n connect(url: string): Promise<void>;\n /** Send a text frame. */\n sendText(data: string): void;\n /** Send a binary frame. */\n sendBinary(data: Uint8Array): void;\n /** Close the transport cleanly. */\n close(code?: number, reason?: string): void;\n /** Whether the transport is currently open and writeable. */\n readonly isOpen: boolean;\n\n onMessage: TransportMessageHandler | null;\n onClose: TransportCloseHandler | null;\n onError: TransportErrorHandler | null;\n}\n\n// ─── Browser / Node WebSocket transport ────────────────────────────────────────\n\nexport type AnyWebSocket = {\n readyState: number;\n send(data: string | ArrayBuffer | Uint8Array): void;\n close(code?: number, reason?: string): void;\n onopen: ((ev: unknown) => void) | null;\n onclose: ((ev: { code: number; reason: string }) => void) | null;\n onerror: ((ev: unknown) => void) | null;\n onmessage: ((ev: { data: unknown }) => void) | null;\n};\n\nexport type WsFactory = (url: string, protocols?: string[]) => AnyWebSocket;\n\n// Node `ws` compatibility: ws closes have reason as Buffer, not string.\nexport function reasonString(reason: unknown): string {\n if (typeof reason === \"string\") return reason;\n if (reason instanceof Uint8Array || Buffer.isBuffer(reason)) return reason.toString(\"utf8\");\n return String(reason ?? \"\");\n}\n\n/**\n * Create a direct WebSocket transport using the supplied factory.\n * Default factory uses the global `WebSocket` (browser/RN/Node 22+) or throws.\n */\nexport function createWebSocketTransport(factory?: WsFactory): Transport {\n let ws: AnyWebSocket | null = null;\n const self: Transport = {\n onMessage: null,\n onClose: null,\n onError: null,\n get isOpen(): boolean {\n return ws !== null && ws.readyState === 1; // OPEN\n },\n connect(url: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const create = factory ?? (() => new WebSocket(url) as unknown as AnyWebSocket);\n try {\n ws = factory ? create(url) : (new WebSocket(url) as unknown as AnyWebSocket);\n } catch (err) {\n reject(err);\n return;\n }\n ws.onopen = () => resolve();\n ws.onerror = (ev) => {\n if (ws?.readyState !== 1) {\n reject(ev);\n }\n self.onError?.(ev);\n };\n ws.onclose = (ev) => {\n self.onClose?.(ev.code, reasonString(ev.reason));\n };\n ws.onmessage = (ev) => {\n const { data } = ev;\n if (data instanceof ArrayBuffer || data instanceof Uint8Array) {\n self.onMessage?.(data as ArrayBuffer);\n } else if (typeof data === \"string\") {\n self.onMessage?.(data);\n } else if (typeof Blob !== \"undefined\" && data instanceof Blob) {\n // Browser default `binaryType` is \"blob\" — DaemonClient.handleIncoming already\n // reads Blob payloads asynchronously via `.arrayBuffer()`, so forward it as-is\n // instead of silently dropping every binary (terminal/file-transfer) frame.\n self.onMessage?.(data);\n } else if (Buffer.isBuffer(data)) {\n // Node `ws` may deliver Buffer\n self.onMessage?.(\n data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer,\n );\n }\n };\n });\n },\n sendText(data: string): void {\n ws?.send(data);\n },\n sendBinary(data: Uint8Array): void {\n ws?.send(data);\n },\n close(code = 1000, reason = \"\"): void {\n ws?.close(code, reason);\n ws = null;\n },\n };\n return self;\n}\n","import {\n decodeTerminalFrame,\n tryDecodeFileTransferFrame,\n FileTransferOpcode,\n type ClientType,\n type ServerInfoPayload,\n type SessionMessage,\n type TerminalFrame,\n type FileTransferFrame,\n} from \"@av-pi-studio/protocol\";\n\nimport type { Transport } from \"./transport.js\";\nimport { createWebSocketTransport } from \"./transport.js\";\n\n/**\n * Low-level `DaemonClient` WebSocket driver: connect, hello handshake, framing, RPC correlation,\n * ping/pong liveness, and a pluggable transport abstraction.\n *\n * See architecture/client-app-runtime.md § Connection and architecture/websocket-protocol.md\n * § Connection & handshake, § Top-level envelopes.\n *\n * Out of scope here: the `Pi-StudioClient` facade (task-002), relay transport (sprint-013), and the\n * terminal-stream router (task-003) — though this driver exposes the hooks the router rides on.\n */\n\n/** Connection lifecycle states. */\nexport type ConnectionState = \"idle\" | \"connecting\" | \"open\" | \"closing\" | \"closed\";\n\n/** The default wire protocol version this client speaks. */\nexport const PROTOCOL_VERSION = 1;\n\n/** Generate a request correlation id. */\nfunction newRequestId(): string {\n // crypto.randomUUID is available in browsers, RN (Hermes 0.74+), and Node 16+.\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n return `req-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport interface DaemonClientOptions {\n url: string;\n clientId: string;\n clientType: ClientType;\n protocolVersion?: number;\n appVersion?: string;\n capabilities?: Record<string, boolean>;\n /** Inject a transport (tests/relay). Defaults to a direct WebSocket transport. */\n transport?: Transport;\n /** Default RPC timeout (ms). RPC timeouts are operation errors, NOT socket deaths. */\n rpcTimeoutMs?: number;\n /** Optional clock for deterministic tests. */\n now?: () => number;\n}\n\n/** A pending RPC awaiting its correlated response. */\ninterface PendingRpc {\n resolve: (payload: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout> | null;\n}\n\n/** A failed RPC surfaced from a correlated `rpc_error`. */\nexport class RpcError extends Error {\n constructor(\n message: string,\n readonly requestId: string,\n readonly code?: string,\n ) {\n super(message);\n this.name = \"RpcError\";\n }\n}\n\n/** A timed-out RPC. Distinguished from a dead socket per the protocol contract. */\nexport class RpcTimeoutError extends Error {\n constructor(readonly requestId: string) {\n super(`RPC ${requestId} timed out`);\n this.name = \"RpcTimeoutError\";\n }\n}\n\nexport type SessionMessageHandler = (message: SessionMessage) => void;\nexport type TerminalFrameHandler = (frame: TerminalFrame) => void;\nexport type FileTransferFrameHandler = (frame: FileTransferFrame) => void;\nexport type StateChangeHandler = (state: ConnectionState) => void;\n\nexport class DaemonClient {\n private readonly transport: Transport;\n private readonly rpcTimeoutMs: number;\n private readonly now: () => number;\n\n private _state: ConnectionState = \"idle\";\n private _serverId: string | null = null;\n private _features: Record<string, unknown> = {};\n private _serverCapabilities: Record<string, unknown> = {};\n\n private readonly pending = new Map<string, PendingRpc>();\n private readonly sessionHandlers = new Set<SessionMessageHandler>();\n private readonly terminalHandlers = new Set<TerminalFrameHandler>();\n private readonly fileTransferHandlers = new Set<FileTransferFrameHandler>();\n private readonly stateHandlers = new Set<StateChangeHandler>();\n\n private helloResolve: ((info: ServerInfoPayload) => void) | null = null;\n private helloReject: ((error: Error) => void) | null = null;\n\n constructor(private readonly options: DaemonClientOptions) {\n this.transport = options.transport ?? createWebSocketTransport();\n this.rpcTimeoutMs = options.rpcTimeoutMs ?? 30_000;\n this.now = options.now ?? (() => Date.now());\n\n this.transport.onMessage = (data) => this.handleIncoming(data);\n this.transport.onClose = (code, reason) => this.handleClose(code, reason);\n this.transport.onError = () => {\n /* surfaced via close / rpc errors; do not treat as fatal on its own */\n };\n }\n\n // ─── Public accessors ─────────────────────────────────────────────────────\n\n get state(): ConnectionState {\n return this._state;\n }\n get serverId(): string | null {\n return this._serverId;\n }\n get features(): Record<string, unknown> {\n return this._features;\n }\n get serverCapabilities(): Record<string, unknown> {\n return this._serverCapabilities;\n }\n /** True iff the daemon advertised the given `server_info.features.*` flag truthy. */\n hasFeature(flag: string): boolean {\n return Boolean(this._features[flag]);\n }\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────\n\n /**\n * Open the socket, send `hello`, and await `status`/`server_info`. Resolves with the\n * recorded server info (serverId + features).\n */\n async connect(): Promise<ServerInfoPayload> {\n this.setState(\"connecting\");\n await this.transport.connect(this.options.url);\n\n const handshake = new Promise<ServerInfoPayload>((resolve, reject) => {\n this.helloResolve = resolve;\n this.helloReject = reject;\n });\n\n this.transport.sendText(\n JSON.stringify({\n type: \"hello\",\n clientId: this.options.clientId,\n clientType: this.options.clientType,\n protocolVersion: this.options.protocolVersion ?? PROTOCOL_VERSION,\n appVersion: this.options.appVersion,\n capabilities: this.options.capabilities,\n }),\n );\n\n const info = await handshake;\n this._serverId = info.serverId;\n this._features = info.features;\n this._serverCapabilities = info.capabilities;\n this.setState(\"open\");\n return info;\n }\n\n /** Close the socket cleanly. */\n close(code = 1000, reason = \"\"): void {\n this.setState(\"closing\");\n this.transport.close(code, reason);\n }\n\n // ─── RPC / send ───────────────────────────────────────────────────────────\n\n /**\n * Send a correlated session RPC. Resolves with the response `payload` when a session message\n * carrying the same `requestId` arrives, rejects with `RpcError` on a correlated `rpc_error`,\n * and rejects with `RpcTimeoutError` (operation error only, socket stays alive) on timeout.\n */\n request<T = unknown>(\n type: string,\n params: Record<string, unknown> = {},\n timeoutMs?: number,\n ): Promise<T> {\n const requestId = (params.requestId as string | undefined) ?? newRequestId();\n return new Promise<T>((resolve, reject) => {\n const timeout = timeoutMs ?? this.rpcTimeoutMs;\n const timer =\n timeout > 0\n ? setTimeout(() => {\n this.pending.delete(requestId);\n reject(new RpcTimeoutError(requestId));\n }, timeout)\n : null;\n this.pending.set(requestId, {\n resolve: (payload) => resolve(payload as T),\n reject,\n timer,\n });\n this.sendSession({ type, ...params, requestId } as unknown as SessionMessage);\n });\n }\n\n /** Send a fire-and-forget session message (wrapped in a `session` envelope). */\n sendSession(message: SessionMessage): void {\n this.transport.sendText(JSON.stringify({ type: \"session\", message }));\n }\n\n /** Send a raw binary frame (terminal / file-transfer). */\n sendBinary(data: Uint8Array): void {\n this.transport.sendBinary(data);\n }\n\n /**\n * Send a JSON `ping` and await the correlated `pong`. Liveness rides on the data path because\n * browser/RN WebSocket APIs do not expose RFC6455 ping.\n */\n ping(timeoutMs = 10_000): Promise<void> {\n const requestId = newRequestId();\n return new Promise<void>((resolve, reject) => {\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n this.pending.delete(requestId);\n reject(new RpcTimeoutError(requestId));\n }, timeoutMs)\n : null;\n this.pending.set(requestId, {\n resolve: () => resolve(),\n reject,\n timer,\n });\n this.transport.sendText(\n JSON.stringify({ type: \"ping\", requestId, clientSentAt: this.now() }),\n );\n });\n }\n\n // ─── Subscriptions ──────────────────────────────────────────────────────────\n\n /** Subscribe to all inbound session messages (e.g. `agent_stream`, `agent_update`). */\n onSessionMessage(handler: SessionMessageHandler): () => void {\n this.sessionHandlers.add(handler);\n return () => this.sessionHandlers.delete(handler);\n }\n\n /** Subscribe to decoded inbound terminal binary frames. */\n onTerminalFrame(handler: TerminalFrameHandler): () => void {\n this.terminalHandlers.add(handler);\n return () => this.terminalHandlers.delete(handler);\n }\n\n /** Subscribe to decoded inbound file-transfer binary frames (download/upload chunks). */\n onFileTransferFrame(handler: FileTransferFrameHandler): () => void {\n this.fileTransferHandlers.add(handler);\n return () => this.fileTransferHandlers.delete(handler);\n }\n\n /** Subscribe to connection-state transitions. */\n onStateChange(handler: StateChangeHandler): () => void {\n this.stateHandlers.add(handler);\n return () => this.stateHandlers.delete(handler);\n }\n\n // ─── Internals ──────────────────────────────────────────────────────────────\n\n private setState(next: ConnectionState): void {\n if (this._state === next) return;\n this._state = next;\n for (const handler of this.stateHandlers) handler(next);\n }\n\n private handleClose(_code: number, reason: string): void {\n // Reject the in-flight handshake if the socket closed before server_info.\n if (this.helloReject) {\n this.helloReject(new Error(`socket closed during handshake: ${reason}`));\n this.helloResolve = null;\n this.helloReject = null;\n }\n // Reject all pending RPCs — the socket is gone (distinct from an RPC timeout).\n for (const [, pending] of this.pending) {\n if (pending.timer) clearTimeout(pending.timer);\n pending.reject(new Error(`socket closed: ${reason}`));\n }\n this.pending.clear();\n this.setState(\"closed\");\n }\n\n private handleIncoming(data: string | ArrayBuffer | Blob): void {\n if (typeof data === \"string\") {\n this.handleTextFrame(data);\n return;\n }\n if (data instanceof ArrayBuffer) {\n this.handleBinaryFrame(new Uint8Array(data));\n return;\n }\n // Blob (browser) — read asynchronously.\n if (typeof (data as Blob).arrayBuffer === \"function\") {\n void (data as Blob).arrayBuffer().then((buf) => this.handleBinaryFrame(new Uint8Array(buf)));\n }\n }\n\n /**\n * Route an inbound binary frame by its opcode byte: file-transfer opcodes (`0x10`–`0x13`) go to\n * `fileTransferHandlers`, everything else decodes as a terminal frame. Two independent binary\n * protocols share one opcode byte's numeric range without colliding — see\n * `@av-pi-studio/protocol`'s `TerminalOpcode` (`0x01`–`0x05`) vs `FileTransferOpcode`\n * (`0x10`–`0x13`).\n */\n private handleBinaryFrame(bytes: Uint8Array): void {\n const opcodeByte = bytes[0];\n const isFileTransfer =\n opcodeByte !== undefined &&\n Object.values(FileTransferOpcode).includes(opcodeByte as never);\n if (isFileTransfer) {\n const frame = tryDecodeFileTransferFrame(bytes);\n if (!frame) return; // malformed frame — drop rather than throw\n for (const handler of this.fileTransferHandlers) handler(frame);\n return;\n }\n const frame = decodeTerminalFrame(bytes);\n for (const handler of this.terminalHandlers) handler(frame);\n }\n\n private handleTextFrame(raw: string): void {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return; // ignore malformed frames\n }\n const envelope = parsed as Record<string, unknown>;\n switch (envelope.type) {\n case \"status\":\n this.handleStatus(envelope);\n return;\n case \"pong\":\n this.resolvePending(envelope.requestId as string, undefined);\n return;\n case \"ping\":\n // Some daemons send keepalive pings; reply with a pong.\n this.transport.sendText(\n JSON.stringify({\n type: \"pong\",\n requestId: envelope.requestId,\n serverReceivedAt: this.now(),\n serverSentAt: this.now(),\n }),\n );\n return;\n case \"session\":\n this.handleSession(envelope.message as SessionMessage);\n return;\n default:\n return; // unknown top-level envelope — ignore\n }\n }\n\n private handleStatus(envelope: Record<string, unknown>): void {\n const payload = envelope.payload as ServerInfoPayload | undefined;\n if (payload?.status === \"server_info\" && this.helloResolve) {\n this.helloResolve(payload);\n this.helloResolve = null;\n this.helloReject = null;\n }\n }\n\n private handleSession(message: SessionMessage | undefined): void {\n if (!message || typeof message !== \"object\") return;\n const msg = message as unknown as Record<string, unknown>;\n\n // Correlated rpc_error → reject the matching pending RPC.\n if (msg.type === \"rpc_error\" && typeof msg.requestId === \"string\") {\n const pending = this.pending.get(msg.requestId);\n if (pending) {\n if (pending.timer) clearTimeout(pending.timer);\n this.pending.delete(msg.requestId);\n pending.reject(\n new RpcError(\n (msg.message as string) ?? (msg.error as string) ?? \"RPC failed\",\n msg.requestId,\n msg.code as string | undefined,\n ),\n );\n }\n return;\n }\n\n // Correlated response → resolve the matching pending RPC (and still fan out).\n if (typeof msg.requestId === \"string\" && this.pending.has(msg.requestId)) {\n const payload = \"payload\" in msg ? msg.payload : msg;\n this.resolvePending(msg.requestId, payload);\n }\n\n // Fan out to subscribers (streams, updates, uncorrelated broadcasts).\n for (const handler of this.sessionHandlers) handler(message);\n }\n\n private resolvePending(requestId: string, payload: unknown): void {\n const pending = this.pending.get(requestId);\n if (!pending) return;\n if (pending.timer) clearTimeout(pending.timer);\n this.pending.delete(requestId);\n pending.resolve(payload);\n }\n}\n","import type {\n AgentStreamEvent,\n CreateAgentRequest,\n FetchAgentTimelineResponse,\n SessionMessage,\n TimelineDirection,\n} from \"@av-pi-studio/protocol\";\n\nimport type { DaemonClient } from \"./daemon-client.js\";\n\n/**\n * High-level `PiStudioClient` SDK facade over the low-level `DaemonClient` driver.\n *\n * Exposes workspace/agent/provider handles plus update handler registration, mirroring the\n * scope's `Pi-StudioClient` surface (architecture/client-app-runtime.md § Layered client library /\n * Facade and features/agent-sessions.md § Public Contract). Method names follow the server RPC\n * type names (`create_agent_request`, `send_agent_prompt`, `interrupt_agent`, `update_agent`,\n * `resume_agent`, `import_agent_session`, `fetch_agent_timeline_request`).\n *\n * Out of scope: app runtime controller (sprint-012), terminal router (task-003).\n */\n\n// ─── Update handler types ─────────────────────────────────────────────────────\n\nexport type PiStudioAgentUpdateHandler = (update: AgentUpdateMessage) => void;\nexport type PiStudioWorkspaceUpdateHandler = (update: WorkspaceUpdateMessage) => void;\nexport type PiStudioAgentStreamHandler = (event: AgentStreamEvent) => void;\n\n/** Shape of an inbound `agent_update` session message (append-only; passthrough). */\nexport interface AgentUpdateMessage {\n type: \"agent_update\";\n agentId: string;\n status?: string;\n title?: string;\n labels?: Record<string, string>;\n [key: string]: unknown;\n}\n\n/** Shape of an inbound `workspace_update` session message. */\nexport interface WorkspaceUpdateMessage {\n type: \"workspace_update\";\n workspaceId: string;\n [key: string]: unknown;\n}\n\n// ─── Agent / timeline / workspace / provider handle interfaces ──────────────────\n\nexport interface PiStudioAgentTimelineHandle {\n /** Fetch a bounded page of authoritative timeline history. */\n fetch(opts?: {\n cursor?: string;\n direction?: TimelineDirection;\n limit?: number;\n }): Promise<FetchAgentTimelineResponse>;\n /** Subscribe to live `agent_stream` events for this agent. Returns an unsubscribe fn. */\n subscribe(handler: PiStudioAgentStreamHandler): () => void;\n}\n\nexport interface PiStudioAgentActions {\n readonly agentId: string;\n readonly timeline: PiStudioAgentTimelineHandle;\n /** Send a follow-up prompt to the running/idle agent. */\n send(prompt: string, opts?: { clientMessageId?: string; images?: unknown[] }): Promise<unknown>;\n /** Alias for `send` reflecting the scope's \"run a turn\" verb. */\n run(prompt: string, opts?: { clientMessageId?: string; images?: unknown[] }): Promise<unknown>;\n /** Interrupt the current turn. */\n interrupt(): Promise<unknown>;\n /** Update model/mode/thinking/features/title/labels without recreating the session. */\n update(patch: {\n modeId?: string;\n model?: string;\n thinkingOptionId?: string;\n featureValues?: Record<string, unknown>;\n title?: string;\n labels?: Record<string, string>;\n }): Promise<unknown>;\n /** Resume a closed session via its persistence handle. */\n resume(): Promise<unknown>;\n /** Archive (soft-delete) the agent — closes the runtime, keeps the record for resume. */\n archive(): Promise<unknown>;\n /** Permanently delete the agent's persisted record (hard delete — no trace, cannot resume). */\n delete(): Promise<unknown>;\n /** Subscribe to `agent_update` events scoped to this agent. */\n onUpdate(handler: PiStudioAgentUpdateHandler): () => void;\n}\n\nexport interface PiStudioWorkspaceActions {\n readonly workspaceId: string;\n /** Subscribe to `workspace_update` events scoped to this workspace. */\n onUpdate(handler: PiStudioWorkspaceUpdateHandler): () => void;\n}\n\nexport interface PiStudioProviderActions {\n /** List available providers. */\n listProviders(): Promise<unknown>;\n /** List models for a provider. */\n listModels(provider: string): Promise<unknown>;\n /** List modes for a provider. */\n listModes(provider: string): Promise<unknown>;\n /** Trigger an explicit provider snapshot refresh (no hidden revalidation). */\n refreshSnapshot(): Promise<unknown>;\n}\n\n// ─── Facade implementation ──────────────────────────────────────────────────────\n\nexport class PiStudioClient {\n constructor(private readonly daemon: DaemonClient) {}\n\n get connection(): DaemonClient {\n return this.daemon;\n }\n\n /**\n * Create a new agent session. Returns the response payload (includes `agentId`). When\n * `initialPrompt` is set, the daemon runs the first turn and streams events to subscribers.\n */\n async createAgent(\n req: Omit<CreateAgentRequest, \"type\" | \"requestId\"> & { requestId?: string },\n ): Promise<{ agentId: string; [key: string]: unknown }> {\n const payload = await this.daemon.request<{ agentId: string }>(\"create_agent_request\", {\n ...req,\n });\n return payload as { agentId: string };\n }\n\n /** Get an agent handle for an existing agentId. */\n agent(agentId: string): PiStudioAgentActions {\n return new AgentHandle(this.daemon, agentId);\n }\n\n /** Get a workspace handle for an existing workspaceId. */\n workspace(workspaceId: string): PiStudioWorkspaceActions {\n return new WorkspaceHandle(this.daemon, workspaceId);\n }\n\n /** Provider actions (list providers/models/modes, refresh snapshot). */\n get providers(): PiStudioProviderActions {\n return new ProviderHandle(this.daemon);\n }\n\n /** Subscribe to ALL `agent_update` events (any agent). */\n onAgentUpdate(handler: PiStudioAgentUpdateHandler): () => void {\n return this.daemon.onSessionMessage((msg) => {\n if ((msg as { type?: string }).type === \"agent_update\") {\n handler(msg as unknown as AgentUpdateMessage);\n }\n });\n }\n\n /** Subscribe to ALL `workspace_update` events (any workspace). */\n onWorkspaceUpdate(handler: PiStudioWorkspaceUpdateHandler): () => void {\n return this.daemon.onSessionMessage((msg) => {\n if ((msg as { type?: string }).type === \"workspace_update\") {\n handler(msg as unknown as WorkspaceUpdateMessage);\n }\n });\n }\n}\n\n// ─── Handle classes ──────────────────────────────────────────────────────────────\n\nclass TimelineHandle implements PiStudioAgentTimelineHandle {\n constructor(\n private readonly daemon: DaemonClient,\n private readonly agentId: string,\n ) {}\n\n fetch(\n opts: { cursor?: string; direction?: TimelineDirection; limit?: number } = {},\n ): Promise<FetchAgentTimelineResponse> {\n return this.daemon.request<FetchAgentTimelineResponse>(\"fetch_agent_timeline_request\", {\n agentId: this.agentId,\n cursor: opts.cursor,\n direction: opts.direction,\n limit: opts.limit,\n });\n }\n\n subscribe(handler: PiStudioAgentStreamHandler): () => void {\n return this.daemon.onSessionMessage((msg) => {\n const m = msg as unknown as { type?: string; agentId?: string; event?: AgentStreamEvent };\n if (m.type === \"agent_stream\" && m.agentId === this.agentId && m.event) {\n handler(m.event);\n }\n });\n }\n}\n\nclass AgentHandle implements PiStudioAgentActions {\n readonly timeline: PiStudioAgentTimelineHandle;\n\n constructor(\n private readonly daemon: DaemonClient,\n readonly agentId: string,\n ) {\n this.timeline = new TimelineHandle(daemon, agentId);\n }\n\n send(\n prompt: string,\n opts: { clientMessageId?: string; images?: unknown[] } = {},\n ): Promise<unknown> {\n return this.daemon.request(\"send_agent_prompt\", {\n agentId: this.agentId,\n prompt,\n clientMessageId: opts.clientMessageId,\n images: opts.images,\n });\n }\n\n run(prompt: string, opts?: { clientMessageId?: string; images?: unknown[] }): Promise<unknown> {\n return this.send(prompt, opts);\n }\n\n interrupt(): Promise<unknown> {\n return this.daemon.request(\"interrupt_agent\", { agentId: this.agentId });\n }\n\n update(patch: {\n modeId?: string;\n model?: string;\n thinkingOptionId?: string;\n featureValues?: Record<string, unknown>;\n title?: string;\n labels?: Record<string, string>;\n }): Promise<unknown> {\n return this.daemon.request(\"update_agent\", { agentId: this.agentId, ...patch });\n }\n\n resume(): Promise<unknown> {\n return this.daemon.request(\"resume_agent\", { agentId: this.agentId });\n }\n\n archive(): Promise<unknown> {\n return this.daemon.request(\"archive_agent\", { agentId: this.agentId });\n }\n\n delete(): Promise<unknown> {\n return this.daemon.request(\"delete_agent\", { agentId: this.agentId });\n }\n\n onUpdate(handler: PiStudioAgentUpdateHandler): () => void {\n return this.daemon.onSessionMessage((msg) => {\n const m = msg as unknown as AgentUpdateMessage;\n if (m.type === \"agent_update\" && m.agentId === this.agentId) handler(m);\n });\n }\n}\n\nclass WorkspaceHandle implements PiStudioWorkspaceActions {\n constructor(\n private readonly daemon: DaemonClient,\n readonly workspaceId: string,\n ) {}\n\n onUpdate(handler: PiStudioWorkspaceUpdateHandler): () => void {\n return this.daemon.onSessionMessage((msg) => {\n const m = msg as unknown as WorkspaceUpdateMessage;\n if (m.type === \"workspace_update\" && m.workspaceId === this.workspaceId) handler(m);\n });\n }\n}\n\nclass ProviderHandle implements PiStudioProviderActions {\n constructor(private readonly daemon: DaemonClient) {}\n\n listProviders(): Promise<unknown> {\n return this.daemon.request(\"list_providers\", {});\n }\n listModels(provider: string): Promise<unknown> {\n return this.daemon.request(\"list_provider_models\", { provider });\n }\n listModes(provider: string): Promise<unknown> {\n return this.daemon.request(\"list_provider_modes\", { provider });\n }\n refreshSnapshot(): Promise<unknown> {\n // Dotted-namespace RPC per the protocol convention.\n return this.daemon.request(\"providers.snapshot.refresh.request\", {});\n }\n}\n\n/** Convenience re-export of the import operation (not agent-scoped). */\nexport function importAgentSession(\n daemon: DaemonClient,\n args: { provider: string; cwd: string; providerHandleId: string },\n): Promise<unknown> {\n return daemon.request(\"import_agent_session\", args);\n}\n\nexport type { SessionMessage };\n","import type { DaemonClient } from \"./daemon-client.js\";\n\n/**\n * Reconnection + capability-rehydrate driver (architecture/client-app-runtime.md § Connection,\n * § Error Handling; architecture/websocket-protocol.md § Behavior / on reconnect).\n *\n * On socket drop, backoff-reconnect, re-`hello` (the `DaemonClient.connect()` resends the same\n * capabilities), and rehydrate the recorded `serverId`/`features`. Timeline resume planning lives\n * in sprint-012; this driver exposes the hooks (`onReconnected`) the planner rides on.\n *\n * Reconnection backoff parameters are TODO(verify) — defaults below are conservative.\n */\n\nexport interface ReconnectionOptions {\n /** Initial backoff delay (ms). Default 500. */\n initialDelayMs?: number;\n /** Maximum backoff delay (ms). Default 30_000. */\n maxDelayMs?: number;\n /** Exponential growth factor. Default 2. */\n factor?: number;\n /** Random jitter ratio [0..1] applied to each delay. Default 0.2. */\n jitter?: number;\n /** Max reconnect attempts before giving up (Infinity = never). Default Infinity. */\n maxAttempts?: number;\n /** Injected timer scheduler (tests). Defaults to setTimeout. */\n setTimer?: (cb: () => void, ms: number) => unknown;\n clearTimer?: (handle: unknown) => void;\n /** Injected RNG in [0,1) (tests). Defaults to Math.random. */\n random?: () => number;\n}\n\nexport type ReconnectedHandler = (info: { attempt: number; serverId: string | null }) => void;\nexport type ReconnectFailedHandler = (error: unknown, attempt: number) => void;\n\nexport class ReconnectionManager {\n private readonly initialDelayMs: number;\n private readonly maxDelayMs: number;\n private readonly factor: number;\n private readonly jitter: number;\n private readonly maxAttempts: number;\n private readonly setTimer: (cb: () => void, ms: number) => unknown;\n private readonly clearTimer: (handle: unknown) => void;\n private readonly random: () => number;\n\n private attempt = 0;\n private timer: unknown = null;\n private active = false;\n private detachState: (() => void) | null = null;\n\n private readonly reconnectedHandlers = new Set<ReconnectedHandler>();\n private readonly failedHandlers = new Set<ReconnectFailedHandler>();\n\n constructor(\n private readonly daemon: DaemonClient,\n options: ReconnectionOptions = {},\n ) {\n this.initialDelayMs = options.initialDelayMs ?? 500;\n this.maxDelayMs = options.maxDelayMs ?? 30_000;\n this.factor = options.factor ?? 2;\n this.jitter = options.jitter ?? 0.2;\n this.maxAttempts = options.maxAttempts ?? Number.POSITIVE_INFINITY;\n this.setTimer = options.setTimer ?? ((cb, ms) => setTimeout(cb, ms));\n this.clearTimer =\n options.clearTimer ?? ((h) => clearTimeout(h as ReturnType<typeof setTimeout>));\n this.random = options.random ?? Math.random;\n }\n\n /** Compute the backoff delay for a given attempt number (1-based), with jitter. */\n delayForAttempt(attempt: number): number {\n const base = Math.min(this.maxDelayMs, this.initialDelayMs * this.factor ** (attempt - 1));\n const jitterAmount = base * this.jitter * (this.random() * 2 - 1);\n return Math.max(0, Math.round(base + jitterAmount));\n }\n\n /** Subscribe to successful reconnections (after re-handshake + rehydrate). */\n onReconnected(handler: ReconnectedHandler): () => void {\n this.reconnectedHandlers.add(handler);\n return () => this.reconnectedHandlers.delete(handler);\n }\n\n /** Subscribe to reconnect attempt failures. */\n onReconnectFailed(handler: ReconnectFailedHandler): () => void {\n this.failedHandlers.add(handler);\n return () => this.failedHandlers.delete(handler);\n }\n\n /**\n * Start watching the daemon connection. On a `closed` transition, schedule a backoff reconnect.\n */\n start(): void {\n if (this.active) return;\n this.active = true;\n this.detachState = this.daemon.onStateChange((state) => {\n if (state === \"closed\" && this.active) this.scheduleReconnect();\n if (state === \"open\") this.attempt = 0; // reset backoff on a healthy connection\n });\n }\n\n /** Stop watching and cancel any pending reconnect. */\n stop(): void {\n this.active = false;\n if (this.timer) this.clearTimer(this.timer);\n this.timer = null;\n this.detachState?.();\n this.detachState = null;\n }\n\n /** Number of reconnect attempts made since the last healthy open. */\n get attemptCount(): number {\n return this.attempt;\n }\n\n private scheduleReconnect(): void {\n if (!this.active) return;\n if (this.attempt >= this.maxAttempts) return;\n this.attempt += 1;\n const delay = this.delayForAttempt(this.attempt);\n this.timer = this.setTimer(() => {\n this.timer = null;\n void this.tryReconnect();\n }, delay);\n }\n\n private async tryReconnect(): Promise<void> {\n if (!this.active) return;\n const attempt = this.attempt;\n try {\n // connect() re-sends hello with the same capabilities → daemon rehydrates them, and the\n // client re-records serverId + features from the fresh server_info.\n await this.daemon.connect();\n for (const handler of this.reconnectedHandlers) {\n handler({ attempt, serverId: this.daemon.serverId });\n }\n } catch (error) {\n for (const handler of this.failedHandlers) handler(error, attempt);\n this.scheduleReconnect(); // back off and retry\n }\n }\n}\n","/**\n * Connection store — Zustand wrapper around `DaemonClient` + `PiStudioClient`\n * (@av-pi-studio/client). Replaces the POC's raw `WebSocket`/`doConnect()`/`rpc()` globals\n * (POC_TO_APP_PLAN_UI.md §4.1). No raw WebSockets outside this module.\n */\n\nimport { create } from \"zustand\";\nimport {\n DaemonClient,\n PiStudioClient,\n ReconnectionManager,\n createWebSocketTransport,\n type ConnectionState,\n} from \"@av-pi-studio/client\";\nimport type { ServerInfoPayload } from \"@av-pi-studio/protocol\";\n\nexport interface ConnectOptions {\n /** `ws://host:port` (or `wss://…`). */\n url: string;\n /** Bearer password, sent via the `pi-studio.bearer.<pw>` WS subprotocol. */\n password?: string;\n /** Stable per-tab client id; generated once if omitted. */\n clientId?: string;\n}\n\ninterface ConnectionStoreState {\n status: ConnectionState;\n serverInfo: ServerInfoPayload | null;\n error: string | null;\n /** Non-null once `connect()` has ever succeeded — the live SDK handles. */\n daemon: DaemonClient | null;\n client: PiStudioClient | null;\n reconnection: ReconnectionManager | null;\n\n connect(opts: ConnectOptions): Promise<void>;\n disconnect(): void;\n}\n\nfunction generateClientId(): string {\n return \"web-\" + Math.random().toString(36).slice(2, 10);\n}\n\nexport const useConnectionStore = create<ConnectionStoreState>()((set, get) => ({\n status: \"idle\",\n serverInfo: null,\n error: null,\n daemon: null,\n client: null,\n reconnection: null,\n\n async connect(opts) {\n // Tear down any previous connection before opening a new one.\n get().disconnect();\n\n const transport = opts.password\n ? createWebSocketTransport(\n (url) => new WebSocket(url, [`pi-studio.bearer.${opts.password}`]) as unknown as never,\n )\n : undefined;\n\n const daemon = new DaemonClient({\n url: opts.url,\n clientId: opts.clientId ?? generateClientId(),\n clientType: \"browser\",\n capabilities: {},\n transport,\n // The PiStudioClient facade has no per-call timeout override for\n // createAgent/send/interrupt, and agent turns can run far longer than a\n // typical RPC (POC used a 30 min turn timeout). One rpcTimeoutMs applies\n // to every request on this connection, so size it for the longest op.\n rpcTimeoutMs: 30 * 60 * 1000,\n });\n const client = new PiStudioClient(daemon);\n const reconnection = new ReconnectionManager(daemon);\n\n daemon.onStateChange((state) => set({ status: state }));\n reconnection.onReconnected(() => set({ error: null }));\n reconnection.onReconnectFailed((error) => {\n set({ error: error instanceof Error ? error.message : String(error) });\n });\n\n set({ daemon, client, reconnection, error: null, status: \"connecting\" });\n\n try {\n const info = await daemon.connect();\n set({ serverInfo: info });\n reconnection.start();\n } catch (error) {\n set({ error: error instanceof Error ? error.message : String(error) });\n throw error;\n }\n },\n\n disconnect() {\n const { daemon, reconnection } = get();\n reconnection?.stop();\n daemon?.close();\n set({\n status: \"idle\",\n serverInfo: null,\n daemon: null,\n client: null,\n reconnection: null,\n });\n },\n}));\n\n/** Convenience selector: the live `PiStudioClient`, or `null` when disconnected. */\nexport function useClient(): PiStudioClient | null {\n return useConnectionStore((s) => s.client);\n}\n\n/** Convenience selector: current connection lifecycle state. */\nexport function useConnectionStatus(): ConnectionState {\n return useConnectionStore((s) => s.status);\n}\n","/**\n * UI store — ephemeral, session-independent UI state that doesn't belong to any single agent\n * session: the toolbar's connection fields, overlay visibility (workspace-open dialog, session\n * context menu), and `cwd` — the default cwd for a brand-new bare session (SessionList's\n * \"+ New conversation\", session restore's zero-sessions fallback). NOT the \"current browsing\n * scope\" — that's `tab-store.ts`'s `activeWorkspaceCwd`, driven by whichever tab is in view\n * (§4.7 follow-up: workspace-scoped tabs). Sidebar visibility/width (collapsed flags, resizable\n * widths dragged via `ResizeHandle`) also live here. Mirrors POC globals `$(\"host\")`,\n * `$(\"password\")`, `$(\"chat-cwd\")`, `cwdPickerPath`, `menuTargetSessionId`.\n */\n\nimport { create } from \"zustand\";\n\nexport const MIN_SIDEBAR_WIDTH = 100;\nexport const MAX_SIDEBAR_WIDTH = 480;\nconst DEFAULT_LEFT_SIDEBAR_WIDTH = 220;\nconst DEFAULT_RIGHT_SIDEBAR_WIDTH = 280;\n\nfunction clampSidebarWidth(width: number): number {\n return Math.min(MAX_SIDEBAR_WIDTH, Math.max(MIN_SIDEBAR_WIDTH, width));\n}\n\nexport interface UiStoreState {\n host: string;\n password: string;\n cwd: string;\n\n cwdPickerOpen: boolean;\n sessionMenu: { sessionId: string; x: number; y: number } | null;\n rightSidebarTab: \"files\" | \"changes\" | \"terminals\";\n /** Workspace cwds collapsed in the sidebar tree (§4.3 workspace grouping); expanded by default. */\n collapsedWorkspaces: Set<string>;\n /** Left (sessions tree) / right (Files·Changes) sidebar visibility; both expanded by default. */\n leftSidebarCollapsed: boolean;\n rightSidebarCollapsed: boolean;\n /** User-resizable sidebar widths in px, draggable via `ResizeHandle` — clamped to [MIN_SIDEBAR_WIDTH, MAX_SIDEBAR_WIDTH]. */\n leftSidebarWidth: number;\n rightSidebarWidth: number;\n\n setHost(host: string): void;\n setPassword(password: string): void;\n setCwd(cwd: string): void;\n openCwdPicker(): void;\n closeCwdPicker(): void;\n openSessionMenu(sessionId: string, x: number, y: number): void;\n closeSessionMenu(): void;\n setRightSidebarTab(tab: \"files\" | \"changes\" | \"terminals\"): void;\n toggleWorkspaceCollapsed(cwd: string): void;\n toggleLeftSidebar(): void;\n toggleRightSidebar(): void;\n setLeftSidebarWidth(width: number): void;\n setRightSidebarWidth(width: number): void;\n /** Delta-based resize, reading current width from store state — avoids stale-closure drift\n * across the rapid-fire `pointermove` deltas `ResizeHandle` reports. */\n resizeLeftSidebar(deltaX: number): void;\n resizeRightSidebar(deltaX: number): void;\n}\n\nexport const useUiStore = create<UiStoreState>()((set) => ({\n host: \"ws://127.0.0.1:6767\",\n password: \"\",\n cwd: \"~\",\n\n cwdPickerOpen: false,\n sessionMenu: null,\n rightSidebarTab: \"files\",\n collapsedWorkspaces: new Set(),\n leftSidebarCollapsed: false,\n rightSidebarCollapsed: false,\n leftSidebarWidth: DEFAULT_LEFT_SIDEBAR_WIDTH,\n rightSidebarWidth: DEFAULT_RIGHT_SIDEBAR_WIDTH,\n\n setHost: (host) => set({ host }),\n setPassword: (password) => set({ password }),\n setCwd: (cwd) => set({ cwd }),\n openCwdPicker: () => set({ cwdPickerOpen: true }),\n closeCwdPicker: () => set({ cwdPickerOpen: false }),\n openSessionMenu: (sessionId, x, y) => set({ sessionMenu: { sessionId, x, y } }),\n closeSessionMenu: () => set({ sessionMenu: null }),\n setRightSidebarTab: (tab) => set({ rightSidebarTab: tab }),\n toggleWorkspaceCollapsed: (cwd) =>\n set((s) => {\n const next = new Set(s.collapsedWorkspaces);\n if (next.has(cwd)) next.delete(cwd);\n else next.add(cwd);\n return { collapsedWorkspaces: next };\n }),\n toggleLeftSidebar: () => set((s) => ({ leftSidebarCollapsed: !s.leftSidebarCollapsed })),\n toggleRightSidebar: () => set((s) => ({ rightSidebarCollapsed: !s.rightSidebarCollapsed })),\n setLeftSidebarWidth: (width) => set({ leftSidebarWidth: clampSidebarWidth(width) }),\n setRightSidebarWidth: (width) => set({ rightSidebarWidth: clampSidebarWidth(width) }),\n resizeLeftSidebar: (deltaX) =>\n set((s) => ({ leftSidebarWidth: clampSidebarWidth(s.leftSidebarWidth + deltaX) })),\n resizeRightSidebar: (deltaX) =>\n set((s) => ({ rightSidebarWidth: clampSidebarWidth(s.rightSidebarWidth + deltaX) })),\n}));\n","/**\n * Connection status dot + label (POC `#dot`/`#status`, POC_TO_APP_PLAN_UI.md §4.1).\n */\n\nimport { clsx } from \"clsx\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport styles from \"./ConnectionStatus.module.css\";\n\nconst LABEL: Record<string, string> = {\n idle: \"disconnected\",\n connecting: \"connecting…\",\n open: \"connected\",\n closing: \"disconnecting…\",\n closed: \"disconnected\",\n};\n\nexport function ConnectionStatus() {\n const status = useConnectionStore((s) => s.status);\n const error = useConnectionStore((s) => s.error);\n\n return (\n <span className={styles.wrap} title={error ?? undefined}>\n <span className={clsx(styles.dot, styles[status])} />\n {LABEL[status] ?? status}\n </span>\n );\n}\n","/**\n * Toolbar — brand, connection status, host/password inputs, connect/disconnect (POC toolbar,\n * POC_TO_APP_PLAN_UI.md §4.1). Both sidebar-visibility toggles sit together at the far right\n * (`.sidebarToggles`, `margin-left: auto`). Provider is always \"pi\" (see Composer.tsx) — this\n * project has no other provider to select. \"Open Workspace\" lives in the sidebar header\n * (SessionList.tsx), not here.\n */\n\nimport { PanelLeftClose, PanelLeftOpen, PanelRightClose, PanelRightOpen } from \"lucide-react\";\nimport { Button } from \"@pi-studio-ui/components/primitives/Button.js\";\nimport { TextInput } from \"@pi-studio-ui/components/primitives/TextInput.js\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useUiStore } from \"@pi-studio-ui/stores/ui-store.js\";\nimport { ConnectionStatus } from \"./ConnectionStatus.js\";\nimport styles from \"./Toolbar.module.css\";\n\nexport function Toolbar() {\n const status = useConnectionStore((s) => s.status);\n const connect = useConnectionStore((s) => s.connect);\n const disconnect = useConnectionStore((s) => s.disconnect);\n\n const host = useUiStore((s) => s.host);\n const setHost = useUiStore((s) => s.setHost);\n const password = useUiStore((s) => s.password);\n const setPassword = useUiStore((s) => s.setPassword);\n const leftSidebarCollapsed = useUiStore((s) => s.leftSidebarCollapsed);\n const toggleLeftSidebar = useUiStore((s) => s.toggleLeftSidebar);\n const rightSidebarCollapsed = useUiStore((s) => s.rightSidebarCollapsed);\n const toggleRightSidebar = useUiStore((s) => s.toggleRightSidebar);\n\n const connected = status === \"open\" || status === \"connecting\";\n\n return (\n <div className={styles.toolbar}>\n <span className={styles.brand}>Pi-Studio</span>\n <ConnectionStatus />\n <div className={styles.sep} />\n <TextInput\n className={styles.host}\n value={host}\n onChange={(e) => setHost(e.target.value)}\n placeholder=\"ws://host:port\"\n disabled={connected}\n />\n <TextInput\n className={styles.password}\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"password\"\n disabled={connected}\n />\n {status === \"open\" || status === \"connecting\" || status === \"closing\" ? (\n <Button size=\"sm\" onClick={() => disconnect()}>\n Disconnect\n </Button>\n ) : (\n <Button\n size=\"sm\"\n variant=\"default\"\n onClick={() => void connect({ url: host, password: password || undefined })}\n >\n Connect\n </Button>\n )}\n <div className={styles.sidebarToggles}>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n iconOnly\n title={leftSidebarCollapsed ? \"Show sessions sidebar\" : \"Hide sessions sidebar\"}\n onClick={() => toggleLeftSidebar()}\n >\n {leftSidebarCollapsed ? <PanelLeftOpen size={16} /> : <PanelLeftClose size={16} />}\n </Button>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n iconOnly\n title={rightSidebarCollapsed ? \"Show files/changes sidebar\" : \"Hide files/changes sidebar\"}\n onClick={() => toggleRightSidebar()}\n >\n {rightSidebarCollapsed ? <PanelRightOpen size={16} /> : <PanelRightClose size={16} />}\n </Button>\n </div>\n </div>\n );\n}\n","/**\n * TimelineRow — the render model streamed/replayed chat rows collapse into.\n * Discriminated union mirrors the POC's per-kind DOM append logic\n * (`handleAgentStream`, POC_TO_APP_PLAN_UI.md §4.3) without the DOM.\n */\n\nimport type { ToolCallDetail } from \"@av-pi-studio/protocol\";\n\nexport type ToolCallStatus = \"running\" | \"completed\" | \"error\";\n\nexport interface UserRow {\n kind: \"user\";\n id: string;\n text: string;\n images?: Array<{ mimeType?: string; data?: string }>;\n}\n\nexport interface AssistantRow {\n kind: \"assistant\";\n id: string;\n text: string;\n /** True while more `assistant_message` deltas for this turn may still arrive. */\n streaming: boolean;\n}\n\nexport interface ReasoningRow {\n kind: \"reasoning\";\n id: string;\n text: string;\n streaming: boolean;\n}\n\nexport interface ToolRow {\n kind: \"tool\";\n id: string;\n /** Stable key used to upsert/dedupe: `callId` when present, else `${tool.kind}:${detail}`. */\n callId: string;\n tool: ToolCallDetail;\n status: ToolCallStatus;\n}\n\nexport interface ErrorRow {\n kind: \"error\";\n id: string;\n text: string;\n}\n\nexport interface SystemRow {\n kind: \"system\";\n id: string;\n text: string;\n}\n\nexport type TimelineRow = UserRow | AssistantRow | ReasoningRow | ToolRow | ErrorRow | SystemRow;\n\nexport interface TimelineState {\n rows: TimelineRow[];\n /** Index into `rows` of the in-progress assistant row, if any (fast tail lookup). */\n streamingAssistantIndex: number | null;\n /** Index into `rows` of the in-progress reasoning row, if any. */\n streamingReasoningIndex: number | null;\n /** callId → row index, for O(1) tool-call upsert. */\n toolIndexByCallId: Record<string, number>;\n}\n\nexport const EMPTY_TIMELINE: TimelineState = {\n rows: [],\n streamingAssistantIndex: null,\n streamingReasoningIndex: null,\n toolIndexByCallId: {},\n};\n\nlet rowSeq = 0;\nexport function nextRowId(): string {\n rowSeq += 1;\n return `row-${rowSeq}`;\n}\n\n/** Detail text extracted from a tool-call for display (POC `t.command || t.path || …`). */\nexport function toolDetailText(tool: ToolCallDetail): string {\n switch (tool.kind) {\n case \"shell\":\n return tool.command ?? \"\";\n case \"read\":\n case \"write\":\n return tool.path ?? \"\";\n case \"edit\":\n return tool.path ?? \"\";\n case \"search\":\n return tool.query ?? \"\";\n case \"fetch\":\n return tool.url ?? \"\";\n case \"task\":\n return tool.description ?? \"\";\n default:\n return \"\";\n }\n}\n\n/** Stable dedupe key for a tool call (POC `e.callId || (t.kind + \":\" + detail)`). */\nexport function toolCallKey(callId: string | undefined, tool: ToolCallDetail): string {\n return callId ?? `${tool.kind}:${toolDetailText(tool)}`;\n}\n","/**\n * Pure `(TimelineState, AgentStreamEvent) → TimelineState` reducer. Replaces the POC's\n * imperative `handleAgentStream` DOM switch (POC_TO_APP_PLAN_UI.md §4.3). No DOM, no globals —\n * fully unit-testable.\n */\n\nimport type { AgentStreamEvent, ToolCallDetail } from \"@av-pi-studio/protocol\";\nimport {\n EMPTY_TIMELINE,\n nextRowId,\n toolCallKey,\n type TimelineRow,\n type TimelineState,\n} from \"./row-model.js\";\n\n/** Reset streaming refs for a new turn (POC: `turn_started` clears `currentBubble`/`currentReasoning`/`toolEls`). */\nfunction onTurnStarted(state: TimelineState): TimelineState {\n return {\n ...state,\n streamingAssistantIndex: null,\n streamingReasoningIndex: null,\n // toolIndexByCallId intentionally persists across turns — POC only clears `st.toolEls` on\n // `turn_started`, but tool rows themselves remain in the DOM; dedupe-by-callId is scoped to\n // the whole session, not the turn, and callIds are unique per invocation anyway.\n };\n}\n\nfunction onAssistantMessage(state: TimelineState, text: string): TimelineState {\n const rows = state.rows.slice();\n let index = state.streamingAssistantIndex;\n if (index === null || rows[index]?.kind !== \"assistant\") {\n const row: TimelineRow = { kind: \"assistant\", id: nextRowId(), text: \"\", streaming: true };\n rows.push(row);\n index = rows.length - 1;\n }\n const row = rows[index];\n if (!row || row.kind !== \"assistant\") return state; // unreachable given the guard above\n rows[index] = { ...row, text: row.text + text };\n return {\n ...state,\n rows,\n streamingAssistantIndex: index,\n streamingReasoningIndex: null, // POC: assistant_message clears currentReasoning\n };\n}\n\nfunction onReasoning(state: TimelineState, text: string): TimelineState {\n const rows = state.rows.slice();\n let index = state.streamingReasoningIndex;\n if (index === null || rows[index]?.kind !== \"reasoning\") {\n const row: TimelineRow = { kind: \"reasoning\", id: nextRowId(), text: \"\", streaming: true };\n rows.push(row);\n index = rows.length - 1;\n }\n const row = rows[index];\n if (!row || row.kind !== \"reasoning\") return state;\n rows[index] = { ...row, text: row.text + text };\n return {\n ...state,\n rows,\n streamingReasoningIndex: index,\n streamingAssistantIndex: null, // POC: reasoning clears currentBubble\n };\n}\n\n/**\n * Merge tool detail across upserts for the same callId. Pi splits detail across events: the\n * command/path arrive on `tool_execution_start` (→ status \"running\") while the edit diff and the\n * command's output arrive only on `tool_execution_end` (→ \"completed\"/\"error\"), which carries no\n * args. Overwriting wholesale would blank the command/path when the end event lands, so we keep\n * each existing non-empty field unless the incoming event provides a value for it. Same `kind`\n * always (both events describe the same tool), so the union stays well-formed.\n */\nfunction mergeTool(prev: ToolCallDetail, next: ToolCallDetail): ToolCallDetail {\n if (prev.kind !== next.kind) return next;\n const merged: Record<string, unknown> = { ...prev };\n for (const [key, value] of Object.entries(next)) {\n if (value !== undefined && value !== \"\") merged[key] = value;\n }\n return merged as ToolCallDetail;\n}\n\nfunction onToolCall(\n state: TimelineState,\n event: Extract<AgentStreamEvent, { kind: \"tool_call\" }>,\n): TimelineState {\n const key = toolCallKey(event.callId, event.tool);\n const status = event.status === \"completed\" || event.status === \"error\" ? event.status : \"running\";\n const existingIndex = state.toolIndexByCallId[key];\n const rows = state.rows.slice();\n\n if (existingIndex !== undefined && rows[existingIndex]?.kind === \"tool\") {\n const row = rows[existingIndex];\n if (row.kind !== \"tool\") return state;\n rows[existingIndex] = { ...row, tool: mergeTool(row.tool, event.tool), status };\n return {\n ...state,\n rows,\n streamingAssistantIndex: null,\n streamingReasoningIndex: null,\n };\n }\n\n const row: TimelineRow = { kind: \"tool\", id: nextRowId(), callId: key, tool: event.tool, status };\n rows.push(row);\n return {\n ...state,\n rows,\n toolIndexByCallId: { ...state.toolIndexByCallId, [key]: rows.length - 1 },\n streamingAssistantIndex: null,\n streamingReasoningIndex: null,\n };\n}\n\nfunction onTurnCompleted(state: TimelineState): TimelineState {\n const rows = state.rows.slice();\n if (state.streamingAssistantIndex !== null) {\n const row = rows[state.streamingAssistantIndex];\n if (row?.kind === \"assistant\") rows[state.streamingAssistantIndex] = { ...row, streaming: false };\n }\n if (state.streamingReasoningIndex !== null) {\n const row = rows[state.streamingReasoningIndex];\n if (row?.kind === \"reasoning\") rows[state.streamingReasoningIndex] = { ...row, streaming: false };\n }\n return { ...state, rows, streamingAssistantIndex: null, streamingReasoningIndex: null };\n}\n\nfunction onTurnFailed(state: TimelineState, error: string | undefined): TimelineState {\n const finalized = onTurnCompleted(state);\n const row: TimelineRow = { kind: \"error\", id: nextRowId(), text: error || \"turn failed\" };\n return { ...finalized, rows: [...finalized.rows, row] };\n}\n\nfunction onTurnCanceled(state: TimelineState): TimelineState {\n const finalized = onTurnCompleted(state);\n const row: TimelineRow = { kind: \"system\", id: nextRowId(), text: \"(canceled)\" };\n return { ...finalized, rows: [...finalized.rows, row] };\n}\n\nfunction onError(state: TimelineState, message: string | undefined): TimelineState {\n const row: TimelineRow = { kind: \"error\", id: nextRowId(), text: message || \"error\" };\n return { ...state, rows: [...state.rows, row] };\n}\n\nfunction onUserMessage(\n state: TimelineState,\n text: string,\n images?: Array<{ mimeType?: string; data?: string }>,\n): TimelineState {\n const row: TimelineRow = { kind: \"user\", id: nextRowId(), text, images };\n return { ...state, rows: [...state.rows, row] };\n}\n\n/** Apply one `AgentStreamEvent` to timeline state. Pure — no mutation of the input. */\nexport function applyStreamEvent(state: TimelineState, event: AgentStreamEvent): TimelineState {\n switch (event.kind) {\n case \"turn_started\":\n return onTurnStarted(state);\n case \"user_message\":\n return onUserMessage(state, event.text ?? \"\", event.images);\n case \"assistant_message\":\n return onAssistantMessage(state, event.text ?? \"\");\n case \"reasoning\":\n return onReasoning(state, event.text ?? \"\");\n case \"tool_call\":\n return onToolCall(state, event);\n case \"turn_completed\":\n return onTurnCompleted(state);\n case \"turn_failed\":\n return onTurnFailed(state, event.error);\n case \"turn_canceled\":\n return onTurnCanceled(state);\n case \"error\":\n return onError(state, event.message);\n default:\n return state; // unknown/future kind — ignore gracefully (append-only protocol)\n }\n}\n\n/** Replay a whole ordered event list (session restore / timeline hydration) into fresh state. */\nexport function replayEvents(events: readonly AgentStreamEvent[]): TimelineState {\n return events.reduce(applyStreamEvent, EMPTY_TIMELINE);\n}\n\nexport { EMPTY_TIMELINE };\nexport type { TimelineState, TimelineRow };\n","/**\n * Session store — replaces the POC's `sessions[]`/`activeSessionId` globals\n * (POC_TO_APP_PLAN_UI.md §4.3). Kills the POC's `expectingAgent` race: a session only subscribes\n * to `agent(id).timeline.subscribe` once `agentId` is bound (from the `createAgent` response),\n * so no event can arrive before the session that owns it is known — no guessing required.\n */\n\nimport { create } from \"zustand\";\nimport type { AgentStatus } from \"@av-pi-studio/protocol\";\nimport { EMPTY_TIMELINE, applyStreamEvent as applyStreamEventToTimeline } from \"@pi-studio-ui/timeline/reducer.js\";\nimport type { TimelineState } from \"@pi-studio-ui/timeline/reducer.js\";\nimport type { AgentStreamEvent } from \"@av-pi-studio/protocol\";\n\nexport interface SessionEntry {\n id: string;\n agentId: string | null;\n title: string;\n status: AgentStatus | \"idle\";\n cwd: string;\n timeline: TimelineState;\n /** User-turn count, for the sidebar meta line (POC: `messages.filter(role==='user').length`). */\n userMessageCount: number;\n}\n\ninterface SessionStoreState {\n sessions: Record<string, SessionEntry>;\n order: string[];\n activeSessionId: string | null;\n\n createSession(cwd: string): string;\n bindAgent(sessionId: string, agentId: string): void;\n setStatus(sessionId: string, status: AgentStatus | \"idle\"): void;\n setStatusByAgentId(agentId: string, status: AgentStatus | \"idle\"): void;\n setTitle(sessionId: string, title: string): void;\n setCwd(sessionId: string, cwd: string): void;\n applyStreamEvent(sessionId: string, event: AgentStreamEvent): void;\n addUserMessage(sessionId: string): void;\n activate(sessionId: string): void;\n remove(sessionId: string): void;\n /** Register a restored session (session restore on connect, §4.3 \"Session restore\"). */\n hydrate(entry: SessionEntry): void;\n findByAgentId(agentId: string): SessionEntry | undefined;\n}\n\nlet sessionSeq = 0;\nfunction newSessionId(): string {\n sessionSeq += 1;\n return `s-${Date.now().toString(36)}-${sessionSeq}`;\n}\n\nexport const useSessionStore = create<SessionStoreState>()((set, get) => ({\n sessions: {},\n order: [],\n activeSessionId: null,\n\n createSession(cwd) {\n const id = newSessionId();\n const entry: SessionEntry = {\n id,\n agentId: null,\n title: \"New chat\",\n status: \"idle\",\n cwd,\n timeline: EMPTY_TIMELINE,\n userMessageCount: 0,\n };\n set((s) => ({\n sessions: { ...s.sessions, [id]: entry },\n order: [id, ...s.order],\n activeSessionId: id,\n }));\n return id;\n },\n\n hydrate(entry) {\n set((s) => {\n if (s.sessions[entry.id]) return s; // already known — don't clobber live state\n return {\n sessions: { ...s.sessions, [entry.id]: entry },\n order: [...s.order, entry.id],\n };\n });\n },\n\n bindAgent(sessionId, agentId) {\n set((s) => {\n const entry = s.sessions[sessionId];\n if (!entry) return s;\n return { sessions: { ...s.sessions, [sessionId]: { ...entry, agentId } } };\n });\n },\n\n setStatus(sessionId, status) {\n set((s) => {\n const entry = s.sessions[sessionId];\n if (!entry) return s;\n return { sessions: { ...s.sessions, [sessionId]: { ...entry, status } } };\n });\n },\n\n setStatusByAgentId(agentId, status) {\n const entry = get().findByAgentId(agentId);\n if (entry) get().setStatus(entry.id, status);\n },\n\n setTitle(sessionId, title) {\n set((s) => {\n const entry = s.sessions[sessionId];\n if (!entry) return s;\n return { sessions: { ...s.sessions, [sessionId]: { ...entry, title } } };\n });\n },\n\n setCwd(sessionId, cwd) {\n set((s) => {\n const entry = s.sessions[sessionId];\n if (!entry) return s;\n return { sessions: { ...s.sessions, [sessionId]: { ...entry, cwd } } };\n });\n },\n\n applyStreamEvent(sessionId, event) {\n set((s) => {\n const entry = s.sessions[sessionId];\n if (!entry) return s;\n const timeline = applyStreamEventToTimeline(entry.timeline, event);\n let title = entry.title;\n if (event.kind === \"turn_completed\" && title === \"New chat\") {\n const lastAssistant = timeline.rows\n .slice()\n .reverse()\n .find((r) => r.kind === \"assistant\");\n if (lastAssistant && lastAssistant.kind === \"assistant\" && lastAssistant.text) {\n title = lastAssistant.text.slice(0, 40) + (lastAssistant.text.length > 40 ? \"…\" : \"\");\n }\n }\n return { sessions: { ...s.sessions, [sessionId]: { ...entry, timeline, title } } };\n });\n },\n\n addUserMessage(sessionId) {\n set((s) => {\n const entry = s.sessions[sessionId];\n if (!entry) return s;\n return {\n sessions: {\n ...s.sessions,\n [sessionId]: { ...entry, userMessageCount: entry.userMessageCount + 1 },\n },\n };\n });\n },\n\n activate(sessionId) {\n set({ activeSessionId: sessionId });\n },\n\n remove(sessionId) {\n set((s) => {\n const { [sessionId]: _removed, ...rest } = s.sessions;\n const order = s.order.filter((id) => id !== sessionId);\n const activeSessionId = s.activeSessionId === sessionId ? order[0] ?? null : s.activeSessionId;\n return { sessions: rest, order, activeSessionId };\n });\n },\n\n findByAgentId(agentId) {\n return Object.values(get().sessions).find((s) => s.agentId === agentId);\n },\n}));\n","/**\n * Tab store — replaces the POC's `tabs[]`/`activeTabId` globals + `addTab`/`closeTab`/\n * `activateTab` (POC_TO_APP_PLAN_UI.md §4.2). `TabPanelHost` keeps inactive panels mounted but\n * hidden, so this store only tracks identity/order/label — not panel content.\n *\n * Tabs are scoped by workspace (§4.3/§4.7 follow-up: workspace-scoped tabs): every `Tab` carries\n * the (already `normalizeCwd`-normalized) workspace cwd it belongs to, and `activeWorkspaceCwd`\n * names which workspace's tabs `TabStrip`/`TabPanelHost` currently render. All tabs stay mounted\n * regardless of workspace — same \"hidden but alive\" model that already preserves terminal/scroll\n * state across in-workspace tab switches — only the visible SUBSET changes on a workspace switch.\n * `lastActiveTabByWorkspace` remembers each workspace's last-focused tab so switching back to a\n * workspace restores exactly where you left it, not always its first tab.\n */\n\nimport { create } from \"zustand\";\n\nexport type TabKind = \"chat\" | \"file\" | \"diff\" | \"terminal\";\n\nexport interface FileTabData {\n path: string;\n}\n\nexport interface DiffTabData {\n path: string;\n staged: boolean;\n}\n\nexport interface ChatTabData {\n sessionId: string;\n}\n\nexport interface TerminalTabData {\n slot: number | null;\n /** Cwd captured at shortcut-fire time — the terminal launches here regardless of which\n * session/workspace becomes active before its `create_terminal_request` resolves. */\n cwd: string;\n}\n\nexport type TabData = ChatTabData | FileTabData | DiffTabData | TerminalTabData;\n\nexport interface Tab {\n id: string;\n kind: TabKind;\n label: string;\n closable: boolean;\n data: TabData;\n /** Normalized workspace cwd this tab belongs to (see `workspace-grouping.ts#normalizeCwd`) —\n * fixed at creation time, never re-derived live from whatever session happens to be active. */\n workspaceCwd: string;\n}\n\ninterface TabStoreState {\n tabs: Tab[];\n activeTabId: string | null;\n /** Which workspace's tabs are currently visible in the strip/panel host. */\n activeWorkspaceCwd: string | null;\n /** Last-focused tab id per workspace cwd, for restore-on-switch. */\n lastActiveTabByWorkspace: Record<string, string>;\n\n /** Open a tab, or activate it if a tab with the same `id` already exists (POC `addTab`).\n * Always brings the tab's workspace into view. */\n open(tab: Tab): void;\n close(id: string): void;\n /** Activate a tab by id, bringing its workspace into view too. */\n activate(id: string): void;\n /** Switch the visible workspace, restoring its last-focused tab (or its first tab, or none). */\n switchWorkspace(cwd: string): void;\n reorder(fromId: string, toId: string): void;\n updateLabel(id: string, label: string): void;\n updateData(id: string, data: Partial<TabData>): void;\n}\n\nfunction tabsInWorkspace(tabs: Tab[], cwd: string): Tab[] {\n return tabs.filter((t) => t.workspaceCwd === cwd);\n}\n\nexport const useTabStore = create<TabStoreState>()((set, get) => ({\n tabs: [],\n activeTabId: null,\n activeWorkspaceCwd: null,\n lastActiveTabByWorkspace: {},\n\n open(tab) {\n const existing = get().tabs.find((t) => t.id === tab.id);\n if (existing) {\n get().activate(existing.id);\n return;\n }\n set((s) => ({\n tabs: [...s.tabs, tab],\n activeTabId: tab.id,\n activeWorkspaceCwd: tab.workspaceCwd,\n lastActiveTabByWorkspace: { ...s.lastActiveTabByWorkspace, [tab.workspaceCwd]: tab.id },\n }));\n },\n\n close(id) {\n set((s) => {\n const closed = s.tabs.find((t) => t.id === id);\n if (!closed) return s;\n const idx = s.tabs.findIndex((t) => t.id === id);\n const tabs = s.tabs.filter((t) => t.id !== id);\n\n let activeTabId = s.activeTabId;\n const lastActiveTabByWorkspace = { ...s.lastActiveTabByWorkspace };\n if (activeTabId === id) {\n // Fall back to a sibling within the SAME workspace, not the global next tab — closing a\n // tab must never jump the visible workspace out from under the user.\n const siblings = tabsInWorkspace(tabs, closed.workspaceCwd);\n const siblingIdx = Math.min(idx, siblings.length - 1);\n const next = siblings[siblingIdx];\n activeTabId = next?.id ?? null;\n }\n if (lastActiveTabByWorkspace[closed.workspaceCwd] === id) {\n const stillOpen = tabsInWorkspace(tabs, closed.workspaceCwd)[0];\n if (stillOpen) lastActiveTabByWorkspace[closed.workspaceCwd] = stillOpen.id;\n else delete lastActiveTabByWorkspace[closed.workspaceCwd];\n }\n return { tabs, activeTabId, lastActiveTabByWorkspace };\n });\n },\n\n activate(id) {\n set((s) => {\n const tab = s.tabs.find((t) => t.id === id);\n if (!tab) return s;\n return {\n activeTabId: id,\n activeWorkspaceCwd: tab.workspaceCwd,\n lastActiveTabByWorkspace: { ...s.lastActiveTabByWorkspace, [tab.workspaceCwd]: id },\n };\n });\n },\n\n switchWorkspace(cwd) {\n set((s) => {\n const siblings = tabsInWorkspace(s.tabs, cwd);\n const remembered = s.lastActiveTabByWorkspace[cwd];\n const stillOpen = remembered && siblings.some((t) => t.id === remembered);\n const activeTabId = stillOpen ? remembered : siblings[0]?.id ?? null;\n return {\n activeWorkspaceCwd: cwd,\n activeTabId,\n lastActiveTabByWorkspace: activeTabId\n ? { ...s.lastActiveTabByWorkspace, [cwd]: activeTabId }\n : s.lastActiveTabByWorkspace,\n };\n });\n },\n\n reorder(fromId, toId) {\n set((s) => {\n const fromIdx = s.tabs.findIndex((t) => t.id === fromId);\n const toIdx = s.tabs.findIndex((t) => t.id === toId);\n if (fromIdx === -1 || toIdx === -1 || fromIdx === toIdx) return s;\n const tabs = s.tabs.slice();\n const [moved] = tabs.splice(fromIdx, 1);\n if (!moved) return s;\n tabs.splice(toIdx, 0, moved);\n return { tabs };\n });\n },\n\n updateLabel(id, label) {\n set((s) => ({ tabs: s.tabs.map((t) => (t.id === id ? { ...t, label } : t)) }));\n },\n\n updateData(id, data) {\n set((s) => ({\n tabs: s.tabs.map((t) => (t.id === id ? { ...t, data: { ...t.data, ...data } } : t)),\n }));\n },\n}));\n\n// ─── Tab id / open helpers (POC id conventions: \"chat-<id>\", \"file-<path>\", …) ────────────\n\nexport const tabIds = {\n chat: (sessionId: string) => `chat-${sessionId}`,\n file: (path: string) => `file-${path}`,\n diff: (path: string, staged: boolean) => `diff-${path}${staged ? \"-staged\" : \"\"}`,\n terminal: (slotOrToken: number | string) => `term-${slotOrToken}`,\n};\n\nlet terminalCount = 0;\n\n/** Open a brand-new terminal tab against a workspace cwd (no slot yet — `TerminalPanel` creates\n * one on mount). Shared by the `Ctrl/Cmd+T` shortcut (`use-shortcuts.ts`) and the TabStrip's \"+\"\n * button so both paths mint tab ids/labels identically. */\nexport function openNewTerminal(workspaceCwd: string): void {\n terminalCount += 1;\n useTabStore.getState().open({\n id: tabIds.terminal(`new-${terminalCount}`),\n kind: \"terminal\",\n label: `Terminal ${terminalCount}`,\n closable: true,\n data: { slot: null, cwd: workspaceCwd },\n workspaceCwd,\n });\n}\n","/**\n * Explorer store — the right-sidebar Files tab's navigation position (POC `currentFilePath`\n * global, POC_TO_APP_PLAN_UI.md §4.7 / §3 `stores/explorer-store.ts`). File listing data itself is\n * TanStack Query cache (see `hooks/use-explorer.ts`), NOT store state — this store only tracks\n * *where* the explorer is currently looking.\n */\n\nimport { create } from \"zustand\";\nimport type { PiStudioClient } from \"@av-pi-studio/client\";\n\ninterface ExplorerStoreState {\n currentPath: string;\n /** The active workspace's resolved cwd — the upper navigation boundary. `goUp()` never\n * crosses above it and the \"up\" row hides once `currentPath` reaches it (§4.7: the file\n * explorer is scoped to the workspace, not a general filesystem browser). */\n rootPath: string;\n\n /** Set both the boundary and the current position — called when the active workspace changes\n * (POC_TO_APP_PLAN_UI.md §4.7 follow-up: workspace-scoped browsing). */\n setRoot(path: string): void;\n setPath(path: string): void;\n /** Navigate to the parent of `currentPath` (POC `#file-up` click handler), clamped to `rootPath`. */\n goUp(): void;\n}\n\nexport const useExplorerStore = create<ExplorerStoreState>()((set, get) => ({\n currentPath: \"\",\n rootPath: \"\",\n\n setRoot: (path) => set({ rootPath: path, currentPath: path }),\n\n setPath: (path) => set({ currentPath: path }),\n\n goUp: () => {\n const { currentPath, rootPath } = get();\n if (!rootPath || currentPath === rootPath) return;\n const parent = currentPath.split(\"/\").slice(0, -1).join(\"/\") || \"/\";\n // Never navigate above the workspace root: if the computed parent is shorter than (or not a\n // prefix of) rootPath, we've reached — or would overshoot — the boundary, so clamp to it.\n const clamped = parent.length < rootPath.length || !parent.startsWith(rootPath) ? rootPath : parent;\n set({ currentPath: clamped });\n },\n}));\n\ninterface ExplorerEntry {\n name?: string;\n kind?: string;\n type?: string;\n}\n\ninterface ExplorerRpcResult {\n entries?: ExplorerEntry[];\n}\n\ninterface ExplorerRpcResponse {\n result?: ExplorerRpcResult;\n entries?: ExplorerEntry[];\n}\n\nlet cachedHomeDir: string | null = null;\ntype HomeDirListener = (homeDir: string) => void;\nconst homeDirListeners = new Set<HomeDirListener>();\n\n/** Subscribe to the resolved home dir becoming available; returns an unsubscribe function. */\nexport function onHomeDirResolved(listener: HomeDirListener): () => void {\n homeDirListeners.add(listener);\n return () => homeDirListeners.delete(listener);\n}\n\n/**\n * Ports the POC's `resolveHome()` (chat.html ~line 1055-1056): probes `file_explorer_request`\n * for `/home`, takes the first directory entry, and caches the result for the process lifetime.\n * A failure (e.g. requested before the connection is fully open) returns `/tmp` for that call but\n * is NOT cached, so a later call — once connected — can still resolve the real home dir.\n */\nexport async function resolveHome(client: PiStudioClient): Promise<string> {\n if (cachedHomeDir) return cachedHomeDir;\n try {\n const response = await client.connection.request<ExplorerRpcResponse>(\n \"file_explorer_request\",\n { path: \"/home\" },\n );\n const entries = response.result?.entries ?? response.entries ?? [];\n const first = entries.find((e) => e.kind === \"directory\" || e.type === \"directory\");\n cachedHomeDir = \"/home/\" + (first?.name || \"user\");\n } catch {\n return \"/tmp\";\n }\n for (const listener of homeDirListeners) listener(cachedHomeDir);\n return cachedHomeDir;\n}\n\n/** Synchronous best-effort peek at the cached home dir (populated once `resolveHome` has run). */\nexport function peekCachedHomeDir(): string | null {\n return cachedHomeDir;\n}\n\n/** Expand a leading `~` (POC's `(path === \"~\" || path.startsWith(\"~/\")) ? …` pattern). */\nexport async function resolveTildePath(client: PiStudioClient, path: string): Promise<string> {\n if (path === \"~\" || path.startsWith(\"~/\")) {\n const home = await resolveHome(client);\n return home + path.slice(1);\n }\n return path;\n}\n","/**\n * Resolves (and reactively exposes) the daemon's home directory, so `~`-relative session `cwd`s\n * can be normalized against their absolute form for workspace grouping (§4.3 sidebar tree).\n * Triggers `resolveHome` once the connection is fully open; re-renders the caller once it resolves.\n */\n\nimport { useEffect, useState } from \"react\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { resolveHome, peekCachedHomeDir, onHomeDirResolved } from \"@pi-studio-ui/stores/explorer-store.js\";\n\nexport function useHomeDir(): string | null {\n const client = useConnectionStore((s) => s.client);\n const status = useConnectionStore((s) => s.status);\n const [homeDir, setHomeDir] = useState<string | null>(peekCachedHomeDir());\n\n useEffect(() => onHomeDirResolved(setHomeDir), []);\n\n useEffect(() => {\n if (!client || status !== \"open\" || homeDir) return;\n void resolveHome(client);\n }, [client, status, homeDir]);\n\n return homeDir;\n}\n","// AgentStatusDot — state-bucket → color mapping.\n// ui-components.md § Status dots & avatars\n\n// Agent status strings that the daemon can send (from agent-sessions.md).\nexport type AgentStatus =\n | \"idle\"\n | \"running\"\n | \"waiting\"\n | \"finished\"\n | \"error\"\n | \"queued\"\n | \"archived\";\n\nexport type AttentionReason = \"finished\" | \"error\" | \"permission\";\n\nexport type StatusDotInput = {\n status: AgentStatus | undefined | null;\n requiresAttention?: boolean;\n attentionReason?: AttentionReason;\n pendingPermissionCount?: number;\n showInactive?: boolean;\n};\n\nexport type StatusDotColor =\n | \"accent\" // running/active\n | \"statusSuccess\" // finished-ok / success\n | \"statusDanger\" // error / attention\n | \"statusWarning\" // waiting / pending permission\n | \"foregroundMuted\"; // idle / inactive\n\n/**\n * Returns the theme token key for the status dot color, or null if the dot\n * should not be rendered (missing/invalid status and showInactive=false).\n */\nexport function statusDotColor(input: StatusDotInput): StatusDotColor | null {\n const { status, requiresAttention, attentionReason, showInactive } = input;\n\n if (!status) return null;\n\n // Attention overrides the base status color.\n if (requiresAttention) {\n switch (attentionReason) {\n case \"permission\":\n return \"statusWarning\";\n case \"error\":\n return \"statusDanger\";\n case \"finished\":\n default:\n return \"statusSuccess\";\n }\n }\n\n switch (status) {\n case \"running\":\n case \"queued\":\n return \"accent\";\n case \"waiting\":\n return \"statusWarning\";\n case \"finished\":\n return \"statusSuccess\";\n case \"error\":\n return \"statusDanger\";\n case \"idle\":\n case \"archived\":\n return showInactive ? \"foregroundMuted\" : null;\n }\n}\n\n// Status dot dimensions per the spec (8×8 round).\nexport const STATUS_DOT_SIZE = 8;\n","/**\n * AgentStatusDot — 8×8 circle whose color reflects agent status.\n * ui-components.md § Status dots & avatars\n */\n\nimport styles from \"./StatusDot.module.css\";\nimport { statusDotColor, STATUS_DOT_SIZE, type StatusDotInput } from \"@pi-studio-ui/ui/status-dot.js\";\n\nconst COLOR_TOKEN_VAR: Record<string, string> = {\n accent: \"var(--pi-color-accent)\",\n statusSuccess: \"var(--pi-color-statusSuccess)\",\n statusDanger: \"var(--pi-color-statusDanger)\",\n statusWarning: \"var(--pi-color-statusWarning)\",\n foregroundMuted: \"var(--pi-color-foregroundMuted)\",\n};\n\nexport type StatusDotProps = StatusDotInput & {\n className?: string;\n};\n\nexport function StatusDot({ className, ...input }: StatusDotProps) {\n const colorToken = statusDotColor(input);\n if (!colorToken) return null;\n\n return (\n <span\n className={`${styles.dot}${className ? ` ${className}` : \"\"}`}\n style={{\n width: STATUS_DOT_SIZE,\n height: STATUS_DOT_SIZE,\n backgroundColor: COLOR_TOKEN_VAR[colorToken] ?? \"currentColor\",\n }}\n role=\"presentation\"\n />\n );\n}\n","/**\n * Protocol `AgentStatus` (initializing/idle/running/error/closed) → design-system\n * `StatusDotInput.status` (idle/running/waiting/finished/error/queued/archived). The two enums\n * differ because the design system's dot vocabulary predates this protocol; this is the single\n * translation point.\n */\n\nimport type { AgentStatus as ProtocolAgentStatus } from \"@av-pi-studio/protocol\";\nimport type { AgentStatus as DotAgentStatus } from \"@pi-studio-ui/ui/status-dot.js\";\n\nconst MAP: Record<ProtocolAgentStatus, DotAgentStatus> = {\n initializing: \"queued\",\n idle: \"idle\",\n running: \"running\",\n error: \"error\",\n closed: \"finished\",\n};\n\nexport function toDotStatus(status: ProtocolAgentStatus | \"idle\"): DotAgentStatus {\n return MAP[status] ?? \"idle\";\n}\n","/**\n * One row in the session list (POC `renderSessions` per-item markup, POC_TO_APP_PLAN_UI.md §4.2).\n */\n\nimport { MoreVertical } from \"lucide-react\";\nimport { clsx } from \"clsx\";\nimport { StatusDot } from \"@pi-studio-ui/components/primitives/StatusDot.js\";\nimport type { SessionEntry } from \"@pi-studio-ui/stores/session-store.js\";\nimport { toDotStatus } from \"./status-map.js\";\nimport styles from \"./SessionList.module.css\";\n\nexport interface SessionItemProps {\n session: SessionEntry;\n active: boolean;\n onSelect: () => void;\n onOpenMenu: (x: number, y: number) => void;\n}\n\nexport function SessionItem({ session, active, onSelect, onOpenMenu }: SessionItemProps) {\n return (\n <div\n className={clsx(styles.item, active && styles.active)}\n onClick={onSelect}\n onContextMenu={(ev) => {\n ev.preventDefault();\n onOpenMenu(ev.clientX, ev.clientY);\n }}\n >\n <div className={styles.title}>\n <StatusDot status={toDotStatus(session.status)} showInactive />\n <span>{session.title}</span>\n </div>\n <div className={styles.meta}>\n {session.cwd || \"~\"} · {session.agentId ? session.agentId.slice(0, 8) : \"new\"} ·{\" \"}\n {session.userMessageCount} msgs\n </div>\n <button\n type=\"button\"\n className={styles.menuBtn}\n title=\"Actions\"\n onClick={(ev) => {\n ev.stopPropagation();\n const rect = ev.currentTarget.getBoundingClientRect();\n onOpenMenu(rect.left, rect.bottom);\n }}\n >\n <MoreVertical size={14} />\n </button>\n </div>\n );\n}\n","/**\n * Session context menu — Radix DropdownMenu anchored at cursor coordinates, replacing the POC's\n * manually-positioned `#session-menu` (POC_TO_APP_PLAN_UI.md §4.7). Actions: rename\n * (`update_agent` labels), stop (`interrupt_agent`), archive (`archive_agent` — soft delete, keeps\n * the record for resume), delete (`delete_agent` — hard delete, no trace). Both archive and delete\n * are real remote RPCs: local session/tab state is only removed after the daemon confirms, so a\n * failure (disconnected, agent already gone, etc.) surfaces instead of silently leaving the record\n * on disk while the UI pretends it's gone.\n */\n\nimport { useEffect, useRef, useState } from \"react\";\nimport * as DropdownMenu from \"@radix-ui/react-dropdown-menu\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useSessionStore } from \"@pi-studio-ui/stores/session-store.js\";\nimport { useTabStore, tabIds } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { useUiStore } from \"@pi-studio-ui/stores/ui-store.js\";\nimport styles from \"./SessionContextMenu.module.css\";\n\nexport function SessionContextMenu() {\n const menu = useUiStore((s) => s.sessionMenu);\n const closeSessionMenu = useUiStore((s) => s.closeSessionMenu);\n const client = useConnectionStore((s) => s.client);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const [open, setOpen] = useState(false);\n\n useEffect(() => {\n setOpen(menu !== null);\n }, [menu]);\n\n const session = useSessionStore((s) => (menu ? s.sessions[menu.sessionId] : undefined));\n\n if (!menu) return null;\n const canStop = Boolean(session?.agentId) && session?.status === \"running\";\n\n function handleOpenChange(next: boolean) {\n setOpen(next);\n if (!next) closeSessionMenu();\n }\n\n function rename() {\n if (!session) return;\n const next = window.prompt(\"Rename:\", session.title);\n if (next?.trim()) {\n const title = next.trim();\n useSessionStore.getState().setTitle(session.id, title);\n useTabStore.getState().updateLabel(tabIds.chat(session.id), title);\n if (session.agentId) void client?.agent(session.agentId).update({ title }).catch(() => {});\n }\n closeSessionMenu();\n }\n\n function stop() {\n if (session?.agentId) void client?.agent(session.agentId).interrupt().catch(() => {});\n closeSessionMenu();\n }\n\n /** Remove the session/tab from local state only — called after a confirmed remote success, or\n * directly for a session with no `agentId` (never sent a message, nothing exists server-side). */\n function removeLocal(sessionId: string) {\n useSessionStore.getState().remove(sessionId);\n useTabStore.getState().close(tabIds.chat(sessionId));\n }\n\n async function archive() {\n if (!session) return;\n if (!window.confirm(\"Archive this agent? It can be resumed later.\")) return closeSessionMenu();\n closeSessionMenu();\n if (!session.agentId) {\n removeLocal(session.id);\n return;\n }\n if (!client) {\n window.alert(\"Not connected — cannot archive remotely.\");\n return;\n }\n try {\n await client.agent(session.agentId).archive();\n removeLocal(session.id);\n } catch (err) {\n window.alert(`Failed to archive: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n async function remove() {\n if (!session) return;\n if (!window.confirm(\"Permanently delete this agent? This cannot be undone.\")) {\n return closeSessionMenu();\n }\n closeSessionMenu();\n if (!session.agentId) {\n removeLocal(session.id);\n return;\n }\n if (!client) {\n window.alert(\"Not connected — cannot delete remotely.\");\n return;\n }\n try {\n await client.agent(session.agentId).delete();\n removeLocal(session.id);\n } catch (err) {\n window.alert(`Failed to delete: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n return (\n <DropdownMenu.Root open={open} onOpenChange={handleOpenChange} modal={false}>\n <DropdownMenu.Trigger asChild>\n <button\n ref={triggerRef}\n type=\"button\"\n className={styles.trigger}\n style={{ left: menu.x, top: menu.y }}\n aria-hidden\n tabIndex={-1}\n />\n </DropdownMenu.Trigger>\n <DropdownMenu.Portal>\n <DropdownMenu.Content className={styles.content} align=\"start\" sideOffset={2}>\n <DropdownMenu.Item className={styles.item} onSelect={rename}>\n Rename\n </DropdownMenu.Item>\n <DropdownMenu.Item className={styles.item} disabled={!canStop} onSelect={stop}>\n Stop agent\n </DropdownMenu.Item>\n <DropdownMenu.Separator className={styles.sep} />\n <DropdownMenu.Item className={`${styles.item} ${styles.danger}`} onSelect={archive}>\n Archive\n </DropdownMenu.Item>\n <DropdownMenu.Item className={`${styles.item} ${styles.danger}`} onSelect={remove}>\n Delete permanently\n </DropdownMenu.Item>\n </DropdownMenu.Content>\n </DropdownMenu.Portal>\n </DropdownMenu.Root>\n );\n}\n","/**\n * Collapsible workspace header in the sidebar tree — one per distinct session `cwd`\n * (POC_TO_APP_PLAN_UI.md §4.3 workspace grouping). Clicking anywhere on the header (or its\n * chevron) toggles collapse/expand; opening a session is a separate, explicit action (click a\n * session row, or the \"New conversation\" button).\n */\n\nimport { ChevronDown, ChevronRight, FolderClosed, Plus } from \"lucide-react\";\nimport { clsx } from \"clsx\";\nimport { Button } from \"@pi-studio-ui/components/primitives/Button.js\";\nimport styles from \"./SessionList.module.css\";\n\nexport interface WorkspaceGroupHeaderProps {\n label: string;\n cwd: string;\n sessionCount: number;\n collapsed: boolean;\n onToggleCollapsed: () => void;\n onNewSession: () => void;\n}\n\nexport function WorkspaceGroupHeader({\n label,\n cwd,\n sessionCount,\n collapsed,\n onToggleCollapsed,\n onNewSession,\n}: WorkspaceGroupHeaderProps) {\n return (\n <div className={styles.workspaceHeader} onClick={onToggleCollapsed} title={cwd}>\n <button\n type=\"button\"\n className={styles.workspaceChevron}\n aria-label={collapsed ? \"Expand workspace\" : \"Collapse workspace\"}\n onClick={(ev) => {\n ev.stopPropagation();\n onToggleCollapsed();\n }}\n >\n {collapsed ? <ChevronRight size={13} /> : <ChevronDown size={13} />}\n </button>\n <FolderClosed size={13} className={clsx(styles.workspaceIcon)} />\n <span className={styles.workspaceLabel}>{label}</span>\n <span className={styles.workspaceCount}>{sessionCount}</span>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n iconOnly\n title=\"New conversation\"\n className={styles.newSessionBtn}\n onClick={(ev) => {\n ev.stopPropagation();\n onNewSession();\n }}\n >\n <Plus size={13} />\n </Button>\n </div>\n );\n}\n","/**\n * Groups sidebar sessions by workspace (cwd) — each workspace is a project folder that can host\n * several chat sessions, nested under a collapsible header (POC_TO_APP_PLAN_UI.md §4.3 follow-up:\n * workspace-tree sidebar).\n */\n\nimport type { SessionEntry } from \"@pi-studio-ui/stores/session-store.js\";\n\nexport interface WorkspaceGroup {\n /** Normalized (tilde-expanded when known) cwd — the group identity and RPC-ready path. */\n cwd: string;\n sessions: SessionEntry[];\n}\n\n/** Expand a leading `~` against `homeDir` when known; otherwise return `path` unchanged. Single\n * source of truth for workspace identity — every consumer that needs to compare/derive a\n * workspace cwd (grouping, tab scoping, the open-workspace resolver) MUST go through this, or a\n * second normalization drifting from this one reintroduces tilde/absolute duplicate workspaces. */\nexport function normalizeCwd(path: string, homeDir: string | null): string {\n if (homeDir && (path === \"~\" || path.startsWith(\"~/\"))) return homeDir + path.slice(1);\n return path;\n}\n\n/**\n * Buckets sessions by `cwd` (tilde-normalized against `homeDir` so `~/proj` and\n * `/home/user/proj` merge into one workspace once the home dir is known), preserving `order`'s\n * recency: a workspace's position is its first appearance in `order`, and sessions within it keep\n * their relative `order` position too — so the most recently created/active session in each\n * workspace sorts first.\n */\nexport function groupSessionsByWorkspace(\n order: string[],\n sessions: Record<string, SessionEntry>,\n homeDir: string | null,\n): WorkspaceGroup[] {\n const groups = new Map<string, SessionEntry[]>();\n for (const id of order) {\n const session = sessions[id];\n if (!session) continue;\n const cwd = normalizeCwd(session.cwd || \"~\", homeDir);\n const bucket = groups.get(cwd);\n if (bucket) bucket.push(session);\n else groups.set(cwd, [session]);\n }\n return Array.from(groups, ([cwd, sessionsInGroup]) => ({ cwd, sessions: sessionsInGroup }));\n}\n\n/** Short display label for a workspace path — the last path segment, or `~` for the home dir. */\nexport function workspaceLabel(cwd: string): string {\n if (!cwd || cwd === \"~\") return \"~\";\n return cwd.split(\"/\").filter(Boolean).pop() || cwd;\n}\n","/**\n * Left sidebar session list (POC `#session-list`/`renderSessions`, POC_TO_APP_PLAN_UI.md §4.3).\n * Sessions are grouped into a collapsible tree by workspace (project `cwd`): clicking a workspace\n * header toggles that workspace's collapsed state; clicking a nested session opens that session.\n */\n\nimport { FolderOpen } from \"lucide-react\";\nimport { useSessionStore } from \"@pi-studio-ui/stores/session-store.js\";\nimport { useTabStore, tabIds } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useUiStore } from \"@pi-studio-ui/stores/ui-store.js\";\nimport { Button } from \"@pi-studio-ui/components/primitives/Button.js\";\nimport { useHomeDir } from \"@pi-studio-ui/hooks/use-home-dir.js\";\nimport { SessionItem } from \"./SessionItem.js\";\nimport { SessionContextMenu } from \"./SessionContextMenu.js\";\nimport { WorkspaceGroupHeader } from \"./WorkspaceGroupHeader.js\";\nimport { groupSessionsByWorkspace, normalizeCwd, workspaceLabel } from \"./workspace-grouping.js\";\nimport styles from \"./SessionList.module.css\";\n\nexport function SessionList() {\n const order = useSessionStore((s) => s.order);\n const sessions = useSessionStore((s) => s.sessions);\n const activeSessionId = useSessionStore((s) => s.activeSessionId);\n const activate = useSessionStore((s) => s.activate);\n const createSession = useSessionStore((s) => s.createSession);\n const openTab = useTabStore((s) => s.open);\n const status = useConnectionStore((s) => s.status);\n const openSessionMenu = useUiStore((s) => s.openSessionMenu);\n const setCwd = useUiStore((s) => s.setCwd);\n const openCwdPicker = useUiStore((s) => s.openCwdPicker);\n const collapsedWorkspaces = useUiStore((s) => s.collapsedWorkspaces);\n const toggleWorkspaceCollapsed = useUiStore((s) => s.toggleWorkspaceCollapsed);\n const homeDir = useHomeDir();\n\n function handleNewSession(targetWorkspaceCwd: string) {\n const targetCwd = normalizeCwd(targetWorkspaceCwd || \"~\", homeDir);\n const id = createSession(targetCwd);\n openTab({\n id: tabIds.chat(id),\n kind: \"chat\",\n label: \"New chat\",\n closable: true,\n data: { sessionId: id },\n workspaceCwd: targetCwd,\n });\n }\n\n function handleSelect(sessionId: string) {\n activate(sessionId);\n const session = sessions[sessionId];\n const targetCwd = normalizeCwd(session?.cwd || \"~\", homeDir);\n if (session?.cwd) setCwd(session.cwd);\n openTab({\n id: tabIds.chat(sessionId),\n kind: \"chat\",\n label: session?.title ?? \"Chat\",\n closable: true,\n data: { sessionId },\n workspaceCwd: targetCwd,\n });\n }\n\n const groups = groupSessionsByWorkspace(order, sessions, homeDir);\n\n return (\n <div className={styles.wrap}>\n <div className={styles.header}>\n <h3>Workspaces</h3>\n <Button\n size=\"xs\"\n variant=\"ghost\"\n iconOnly\n title={status !== \"open\" ? \"Connect to open a workspace\" : \"Open a workspace folder\"}\n disabled={status !== \"open\"}\n onClick={() => openCwdPicker()}\n >\n <FolderOpen size={14} />\n </Button>\n </div>\n <div className={styles.list}>\n {groups.map((group) => {\n const collapsed = collapsedWorkspaces.has(group.cwd);\n return (\n <div key={group.cwd} className={styles.workspaceGroup}>\n <WorkspaceGroupHeader\n label={workspaceLabel(group.cwd)}\n cwd={group.cwd}\n sessionCount={group.sessions.length}\n collapsed={collapsed}\n onToggleCollapsed={() => toggleWorkspaceCollapsed(group.cwd)}\n onNewSession={() => handleNewSession(group.cwd)}\n />\n {!collapsed && (\n <div className={styles.workspaceSessions}>\n {group.sessions.map((session) => (\n <SessionItem\n key={session.id}\n session={session}\n active={session.id === activeSessionId}\n onSelect={() => handleSelect(session.id)}\n onOpenMenu={(x, y) => openSessionMenu(session.id, x, y)}\n />\n ))}\n </div>\n )}\n </div>\n );\n })}\n </div>\n <SessionContextMenu />\n </div>\n );\n}\n","/**\n * TabStrip — dnd-kit sortable tab strip (POC `renderTabs`, POC_TO_APP_PLAN_UI.md §4.2).\n * Adds drag reorder (the POC couldn't reorder) + Radix Tooltip on truncated labels.\n */\n\nimport { MessageSquare, FileText, GitCompare, TerminalSquare, X } from \"lucide-react\";\nimport { clsx } from \"clsx\";\nimport {\n DndContext,\n closestCenter,\n PointerSensor,\n useSensor,\n useSensors,\n type DragEndEvent,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\n horizontalListSortingStrategy,\n useSortable,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { useTabStore, type Tab, type TabKind } from \"@pi-studio-ui/stores/tab-store.js\";\nimport styles from \"./TabStrip.module.css\";\n\nconst ICON_BY_KIND: Record<TabKind, typeof MessageSquare> = {\n chat: MessageSquare,\n file: FileText,\n diff: GitCompare,\n terminal: TerminalSquare,\n};\n\nfunction TabItem({ tab }: { tab: Tab }) {\n const activeTabId = useTabStore((s) => s.activeTabId);\n const activate = useTabStore((s) => s.activate);\n const close = useTabStore((s) => s.close);\n const Icon = ICON_BY_KIND[tab.kind];\n\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n id: tab.id,\n });\n\n return (\n <div\n ref={setNodeRef}\n className={clsx(styles.tab, tab.id === activeTabId && styles.active)}\n style={{\n transform: CSS.Transform.toString(transform),\n transition,\n opacity: isDragging ? 0.5 : 1,\n }}\n onClick={() => activate(tab.id)}\n onAuxClick={(ev) => {\n if (ev.button === 1 && tab.closable) close(tab.id);\n }}\n title={tab.label}\n {...attributes}\n {...listeners}\n >\n <span className={styles.icon}>\n <Icon size={13} />\n </span>\n <span className={styles.label}>{tab.label}</span>\n {tab.closable && (\n <span\n className={styles.close}\n onClick={(ev) => {\n ev.stopPropagation();\n close(tab.id);\n }}\n >\n <X size={12} />\n </span>\n )}\n </div>\n );\n}\n\nexport function TabStrip() {\n const tabs = useTabStore((s) => s.tabs);\n const activeWorkspaceCwd = useTabStore((s) => s.activeWorkspaceCwd);\n const reorder = useTabStore((s) => s.reorder);\n const visibleTabs = tabs.filter((t) => t.workspaceCwd === activeWorkspaceCwd);\n const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 4 } }));\n\n function handleDragEnd(event: DragEndEvent) {\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n reorder(String(active.id), String(over.id));\n }\n\n return (\n <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>\n <SortableContext items={visibleTabs.map((t) => t.id)} strategy={horizontalListSortingStrategy}>\n <div className={styles.strip}>\n {visibleTabs.map((tab) => (\n <TabItem key={tab.id} tab={tab} />\n ))}\n </div>\n </SortableContext>\n </DndContext>\n );\n}\n","/**\n * Spinner — a thin activity-indicator circle.\n * ui-components.md § Feedback\n */\n\nimport styles from \"./Spinner.module.css\";\n\nexport type SpinnerSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\nconst SPINNER_SIZE_PX: Record<SpinnerSize, number> = { xs: 12, sm: 16, md: 20, lg: 24 };\nconst SPINNER_BORDER_PX: Record<SpinnerSize, number> = { xs: 1, sm: 2, md: 2, lg: 3 };\n\nexport interface SpinnerProps {\n size?: SpinnerSize | number;\n /** CSS color string; defaults to currentColor. */\n color?: string;\n className?: string;\n \"aria-label\"?: string;\n}\n\nexport function Spinner({\n size = \"md\",\n color = \"currentColor\",\n className,\n \"aria-label\": ariaLabel = \"Loading\",\n}: SpinnerProps) {\n const px = typeof size === \"number\" ? size : SPINNER_SIZE_PX[size];\n const borderPx =\n typeof size === \"number\"\n ? 2\n : SPINNER_BORDER_PX[size as SpinnerSize];\n\n return (\n <span\n className={`${styles.spinner}${className ? ` ${className}` : \"\"}`}\n role=\"status\"\n aria-label={ariaLabel}\n style={{\n width: px,\n height: px,\n borderWidth: borderPx,\n color,\n }}\n />\n );\n}\n","/**\n * Tab kind → panel component map (POC `createTabPanel` switch, POC_TO_APP_PLAN_UI.md §4.2).\n * Panels are lazy-loaded — the terminal (xterm) and diff/highlighter modules only pay their\n * bundle cost once a tab of that kind actually opens (§6 Performance strategy).\n */\n\nimport { lazy, type ComponentType } from \"react\";\nimport type { Tab } from \"@pi-studio-ui/stores/tab-store.js\";\n\nexport interface PanelProps {\n tab: Tab;\n}\n\nconst ChatPanel = lazy(() => import(\"../chat/ChatPanel.js\").then((m) => ({ default: m.ChatPanel })));\nconst FilePanel = lazy(() => import(\"../files/FilePanel.js\").then((m) => ({ default: m.FilePanel })));\nconst TerminalPanel = lazy(() =>\n import(\"../terminal/TerminalPanel.js\").then((m) => ({ default: m.TerminalPanel })),\n);\n\nexport const PANEL_BY_KIND: Record<Tab[\"kind\"], ComponentType<PanelProps>> = {\n chat: ChatPanel,\n file: FilePanel,\n diff: FilePanel, // FilePanel handles both file/diff view toggle internally (POC §4.5)\n terminal: TerminalPanel,\n};\n","/**\n * TabPanelHost — mounts one panel per open tab via `panel-registry`, keeps inactive panels\n * mounted but hidden (`display:none`), preserving scroll/terminal state across tab switches\n * exactly like the POC's DOM-persisted panels (POC_TO_APP_PLAN_UI.md §4.2), but through React\n * reconciliation instead of `data-tab-panel` querying. This now spans workspace switches too\n * (§4.7 follow-up: workspace-scoped tabs) — ALL tabs across every workspace stay mounted; only\n * the strip/empty-state visibility is workspace-scoped, so switching workspaces never tears down\n * a live terminal or loses scroll position in another workspace's tabs.\n */\n\nimport { Suspense } from \"react\";\nimport { clsx } from \"clsx\";\nimport { Spinner } from \"@pi-studio-ui/components/primitives/Spinner.js\";\nimport { useTabStore } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { PANEL_BY_KIND } from \"./panel-registry.js\";\nimport styles from \"./TabPanelHost.module.css\";\n\nexport function TabPanelHost() {\n const tabs = useTabStore((s) => s.tabs);\n const activeTabId = useTabStore((s) => s.activeTabId);\n const activeWorkspaceCwd = useTabStore((s) => s.activeWorkspaceCwd);\n const hasTabsInWorkspace = tabs.some((t) => t.workspaceCwd === activeWorkspaceCwd);\n\n if (!hasTabsInWorkspace) {\n return (\n <div className={styles.area}>\n <div className={styles.empty}>No open tabs in this workspace</div>\n </div>\n );\n }\n\n return (\n <div className={styles.area}>\n {tabs.map((tab) => {\n const Panel = PANEL_BY_KIND[tab.kind];\n return (\n <div\n key={tab.id}\n className={clsx(styles.panel, tab.id === activeTabId && styles.active)}\n >\n <Suspense fallback={<Spinner size=\"md\" />}>\n <Panel tab={tab} />\n </Suspense>\n </div>\n );\n })}\n </div>\n );\n}\n","/**\n * TanStack Query key factory — one namespaced key builder per RPC domain so cache\n * invalidation (e.g. the `filesChanged` signal, POC_TO_APP_PLAN_UI.md §4.5) targets exactly\n * the affected queries.\n */\n\nexport const rpcKeys = {\n fileRead: (path: string) => [\"file\", \"read\", path] as const,\n fileDownload: (path: string) => [\"file\", \"download\", path] as const,\n fileDiff: (path: string, cwd: string, staged: boolean) =>\n [\"file\", \"diff\", path, cwd, staged] as const,\n explorer: (path: string) => [\"explorer\", path] as const,\n agentList: () => [\"agents\", \"list\"] as const,\n agentTimeline: (agentId: string) => [\"agents\", \"timeline\", agentId] as const,\n};\n","/**\n * `use-explorer` — TanStack Query wrapper over `file_explorer_request` (POC `loadFiles`,\n * POC_TO_APP_PLAN_UI.md §4.7 / §3 `hooks/use-explorer.ts`). Directory listings are read-through\n * cached, keyed by resolved path — components resolve `~` via `resolveTildePath` before calling.\n */\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { rpcKeys } from \"@pi-studio-ui/lib/connection/rpc-keys.js\";\n\nexport interface ExplorerEntry {\n name: string;\n kind: \"file\" | \"directory\" | \"symlink\" | \"other\";\n size?: number;\n mtimeMs?: number;\n}\n\ninterface RawExplorerEntry {\n name?: string;\n kind?: string;\n type?: string;\n size?: number;\n mtimeMs?: number;\n}\n\ninterface ExplorerDirectoryResult {\n ok: true;\n kind: \"directory\";\n path: string;\n resolvedPath: string;\n entries: RawExplorerEntry[];\n}\n\ninterface ExplorerTextResult {\n ok: true;\n kind: \"text\";\n path: string;\n resolvedPath: string;\n content: string;\n size: number;\n}\n\ninterface ExplorerErrorResult {\n ok: false;\n error: string;\n}\n\ntype ExplorerResult = ExplorerDirectoryResult | ExplorerTextResult | ExplorerErrorResult;\n\ninterface ExplorerRpcResponse {\n result?: ExplorerResult;\n}\n\nexport interface ExplorerListing {\n path: string;\n entries: ExplorerEntry[];\n}\n\nfunction normalizeEntry(e: RawExplorerEntry): ExplorerEntry {\n const kind = e.kind === \"directory\" || e.type === \"directory\" ? \"directory\" : \"file\";\n return { name: e.name ?? \"\", kind, size: e.size, mtimeMs: e.mtimeMs };\n}\n\n/** Dirs first (alphabetical), then files (alphabetical) — POC `fileList.sort`. */\nfunction sortEntries(entries: ExplorerEntry[]): ExplorerEntry[] {\n return entries.slice().sort((a, b) => {\n if (a.kind !== b.kind) return a.kind === \"directory\" ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n}\n\nexport function useExplorer(path: string, enabled = true) {\n const client = useConnectionStore((s) => s.client);\n\n return useQuery({\n queryKey: rpcKeys.explorer(path),\n queryFn: async (): Promise<ExplorerListing> => {\n if (!client) throw new Error(\"not connected\");\n const response = await client.connection.request<ExplorerRpcResponse>(\n \"file_explorer_request\",\n { path },\n );\n const result = response.result;\n if (!result || result.ok === false) {\n throw new Error(result && \"error\" in result ? result.error : \"explorer request failed\");\n }\n const entries = \"entries\" in result ? (result.entries ?? []) : [];\n return {\n path: result.resolvedPath ?? path,\n entries: sortEntries(entries.map(normalizeEntry)),\n };\n },\n enabled: Boolean(client) && Boolean(path) && enabled,\n });\n}\n","/**\n * FileExplorer — right-sidebar Files tab (POC `loadFiles`/`renderRightPanel`, chat.html\n * ~line 1014-1053, POC_TO_APP_PLAN_UI.md §4.7). Breadcrumb-style nav: an \"up\" row while below the\n * active workspace's root — navigation is clamped there, this is a workspace file browser, not a\n * general filesystem one — then directories (alphabetical) before files (alphabetical). Directory\n * click navigates in place; file click opens a file tab.\n */\n\nimport { useEffect, useRef } from \"react\";\nimport { Folder, File as FileIcon, ArrowUp } from \"lucide-react\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useExplorerStore, resolveTildePath } from \"@pi-studio-ui/stores/explorer-store.js\";\nimport { useExplorer } from \"@pi-studio-ui/hooks/use-explorer.js\";\nimport { useTabStore, tabIds } from \"@pi-studio-ui/stores/tab-store.js\";\nimport styles from \"./FileExplorer.module.css\";\n\nexport function FileExplorer() {\n const client = useConnectionStore((s) => s.client);\n const currentPath = useExplorerStore((s) => s.currentPath);\n const rootPath = useExplorerStore((s) => s.rootPath);\n const setPath = useExplorerStore((s) => s.setPath);\n const setRoot = useExplorerStore((s) => s.setRoot);\n const goUp = useExplorerStore((s) => s.goUp);\n // The workspace currently in view in the tab strip drives what Files/Changes browse — same\n // authoritative signal every tab creation site uses (§4.7 follow-up: workspace-scoped tabs).\n const activeWorkspaceCwd = useTabStore((s) => s.activeWorkspaceCwd);\n const openTab = useTabStore((s) => s.open);\n\n // Navigate whenever the active workspace changes, and seed it once on first connect. Seeding\n // sets `rootPath` too — the upper navigation boundary — so `goUp()`/the \"up\" row never let the\n // user browse above the workspace root. `lastSeededCwd` distinguishes \"the active workspace\n // changed\" from \"the user clicked into a subdirectory\" — only the former reseeds.\n const lastSeededCwd = useRef<string | null>(null);\n useEffect(() => {\n if (!client) return;\n const target = activeWorkspaceCwd || \"~\";\n if (lastSeededCwd.current === target) return;\n lastSeededCwd.current = target;\n void resolveTildePath(client, target).then((resolved) => setRoot(resolved));\n }, [client, activeWorkspaceCwd, setRoot]);\n\n const { data, isLoading, isError, error } = useExplorer(currentPath, Boolean(client));\n\n function handleDirClick(name: string) {\n setPath(currentPath ? `${currentPath}/${name}` : name);\n }\n\n function handleFileClick(name: string) {\n const fullPath = currentPath ? `${currentPath}/${name}` : name;\n openTab({\n id: tabIds.file(fullPath),\n kind: \"file\",\n label: fullPath.split(\"/\").pop() || fullPath,\n closable: true,\n data: { path: fullPath },\n workspaceCwd: activeWorkspaceCwd || \"~\",\n });\n }\n\n if (!client) {\n return <div className={styles.emptyState}>Connect to browse files</div>;\n }\n if (isLoading) {\n return <div className={styles.emptyState}>Loading...</div>;\n }\n if (isError) {\n return (\n <div className={styles.emptyState}>\n Error: {error instanceof Error ? error.message : \"unknown error\"}\n </div>\n );\n }\n if (!data || data.entries.length === 0) {\n return <div className={styles.emptyState}>No files loaded</div>;\n }\n\n return (\n <div className={styles.list}>\n {currentPath && currentPath !== rootPath && (\n <div className={styles.upRow} onClick={() => goUp()}>\n <ArrowUp size={12} />\n <span>{currentPath}</span>\n </div>\n )}\n {data.entries.map((entry) => (\n <div\n key={entry.name}\n className={styles.item}\n onClick={() =>\n entry.kind === \"directory\" ? handleDirClick(entry.name) : handleFileClick(entry.name)\n }\n >\n <span className={styles.icon}>\n {entry.kind === \"directory\" ? <Folder size={14} /> : <FileIcon size={14} />}\n </span>\n <span className={entry.kind === \"directory\" ? styles.dirName : styles.name}>\n {entry.name}\n </span>\n </div>\n ))}\n </div>\n );\n}\n","/**\n * Git store — the right-sidebar Changes tab's checkout-status projection (POC `changeList`\n * global + `handleCheckoutStatusUpdate`, POC_TO_APP_PLAN_UI.md §4.7). `applyProjection` ports the\n * POC mapping verbatim: staged entries key off `indexStatus`, unstaged entries key off\n * `worktreeStatus`, untracked paths are always \"added\"/unstaged.\n */\n\nimport { create } from \"zustand\";\n\nexport interface CheckoutFileEntry {\n path: string;\n indexStatus: string;\n worktreeStatus: string;\n}\n\nexport interface CheckoutStatusProjection {\n available: boolean;\n branch: string | null;\n upstream: string | null;\n ahead: number;\n behind: number;\n detached: boolean;\n staged: CheckoutFileEntry[];\n unstaged: CheckoutFileEntry[];\n untracked: string[];\n conflicted: string[];\n hasConflicts: boolean;\n unavailableReason?: string;\n}\n\nexport interface ChangeEntry {\n path: string;\n status: \"added\" | \"deleted\" | \"modified\";\n staged: boolean;\n}\n\ninterface GitStoreState {\n subscribedCwd: string | null;\n changes: ChangeEntry[];\n\n setSubscribedCwd(cwd: string | null): void;\n /** Map a `checkout_status_update` projection into flat change rows (POC `handleCheckoutStatusUpdate`). */\n applyProjection(projection: CheckoutStatusProjection | null | undefined): void;\n}\n\nexport const useGitStore = create<GitStoreState>()((set) => ({\n subscribedCwd: null,\n changes: [],\n\n setSubscribedCwd: (cwd) => set({ subscribedCwd: cwd }),\n\n applyProjection: (projection) => {\n if (!projection || !projection.available) {\n set({ changes: [] });\n return;\n }\n\n const changes: ChangeEntry[] = [];\n for (const f of projection.staged) {\n changes.push({\n path: f.path,\n status: f.indexStatus === \"A\" ? \"added\" : f.indexStatus === \"D\" ? \"deleted\" : \"modified\",\n staged: true,\n });\n }\n for (const f of projection.unstaged) {\n changes.push({\n path: f.path,\n status: f.worktreeStatus === \"D\" ? \"deleted\" : \"modified\",\n staged: false,\n });\n }\n for (const path of projection.untracked) {\n changes.push({ path, status: \"added\", staged: false });\n }\n set({ changes });\n },\n}));\n","/**\n * `use-checkout-status` — subscribes to live git checkout status for a cwd (POC `loadChanges` +\n * `handleCheckoutStatusUpdate`, POC_TO_APP_PLAN_UI.md §4.7 / §3 `hooks/use-checkout-status.ts`).\n * On `cwd` change: unsubscribes the previous cwd (`checkout_status_unsubscribe`), subscribes the\n * new one (`checkout_status_subscribe`), and routes pushed `checkout_status_update` session\n * messages into `git-store`. Cleans up both the RPC subscription and the message handler on\n * unmount/cwd-change — the POC leaked subscriptions on cwd change (§4.7).\n */\n\nimport { useEffect, useRef } from \"react\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useGitStore, type CheckoutStatusProjection } from \"@pi-studio-ui/stores/git-store.js\";\n\ninterface CheckoutStatusUpdateMessage {\n type: \"checkout_status_update\";\n cwd: string;\n projection: CheckoutStatusProjection;\n}\n\nfunction isCheckoutStatusUpdate(msg: unknown): msg is CheckoutStatusUpdateMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"type\" in msg &&\n msg.type === \"checkout_status_update\" &&\n \"cwd\" in msg &&\n typeof msg.cwd === \"string\" &&\n \"projection\" in msg &&\n typeof msg.projection === \"object\" &&\n msg.projection !== null\n );\n}\n\nexport interface UseCheckoutStatusResult {\n refresh(): void;\n}\n\nexport function useCheckoutStatus(cwd: string): UseCheckoutStatusResult {\n const client = useConnectionStore((s) => s.client);\n const applyProjection = useGitStore((s) => s.applyProjection);\n const setSubscribedCwd = useGitStore((s) => s.setSubscribedCwd);\n const clientRef = useRef(client);\n clientRef.current = client;\n\n useEffect(() => {\n if (!client || !cwd) return;\n\n let cancelled = false;\n void client.connection.request(\"checkout_status_subscribe\", { cwd }).catch(() => {});\n setSubscribedCwd(cwd);\n\n const unsubscribeMessages = client.connection.onSessionMessage((msg) => {\n if (cancelled) return;\n const raw: unknown = msg;\n if (!isCheckoutStatusUpdate(raw)) return;\n if (raw.cwd !== cwd) return;\n applyProjection(raw.projection);\n });\n\n void client.connection.request(\"checkout_refresh_request\", { cwd }).catch(() => {});\n\n return () => {\n cancelled = true;\n unsubscribeMessages();\n void client.connection.request(\"checkout_status_unsubscribe\", { cwd }).catch(() => {});\n setSubscribedCwd(null);\n };\n }, [client, cwd, applyProjection, setSubscribedCwd]);\n\n return {\n refresh() {\n const current = clientRef.current;\n if (!current || !cwd) return;\n void current.connection.request(\"checkout_refresh_request\", { cwd }).catch(() => {});\n },\n };\n}\n","/**\n * ChangesPanel — right-sidebar Changes tab (POC `renderRightPanel` changes branch, chat.html\n * ~line 1028-1039, POC_TO_APP_PLAN_UI.md §4.7). A/M/D badges + file path; click opens a diff tab.\n * Subscribes to live checkout status for the currently active workspace (§4.7 follow-up:\n * workspace-scoped tabs) — the same signal every tab creation site uses.\n */\n\nimport { clsx } from \"clsx\";\nimport { useCheckoutStatus } from \"@pi-studio-ui/hooks/use-checkout-status.js\";\nimport { useGitStore } from \"@pi-studio-ui/stores/git-store.js\";\nimport { useTabStore, tabIds } from \"@pi-studio-ui/stores/tab-store.js\";\nimport styles from \"./ChangesPanel.module.css\";\n\nconst BADGE_LABEL: Record<\"added\" | \"modified\" | \"deleted\", string> = {\n added: \"A\",\n modified: \"M\",\n deleted: \"D\",\n};\n\nexport function ChangesPanel() {\n const activeWorkspaceCwd = useTabStore((s) => s.activeWorkspaceCwd);\n const cwd = activeWorkspaceCwd || \"~\";\n\n useCheckoutStatus(cwd);\n const changes = useGitStore((s) => s.changes);\n const openTab = useTabStore((s) => s.open);\n\n function handleClick(path: string, staged: boolean) {\n openTab({\n id: tabIds.diff(path, staged),\n kind: \"diff\",\n label: path.split(\"/\").pop() || path,\n closable: true,\n data: { path, staged },\n workspaceCwd: cwd,\n });\n }\n\n if (changes.length === 0) {\n return <div className={styles.emptyState}>No changes detected</div>;\n }\n\n return (\n <div className={styles.list}>\n <div className={styles.summary}>\n {changes.length} changed file{changes.length === 1 ? \"\" : \"s\"}\n </div>\n {changes.map((change, i) => (\n <div\n key={`${change.path}-${change.staged}-${i}`}\n className={styles.item}\n onClick={() => handleClick(change.path, change.staged)}\n >\n <span className={clsx(styles.badge, styles[change.status])}>\n {BADGE_LABEL[change.status]}\n </span>\n <span className={styles.name}>{change.path}</span>\n </div>\n ))}\n </div>\n );\n}\n","/**\n * Terminal store — the right-sidebar Terminals tab's live view of every PTY currently running on\n * the daemon (server-authoritative `TerminalRuntimeEntry[]`, features/terminals.md § Control\n * RPCs). Exists to surface — and let the user kill — terminals that outlive their tab: closing a\n * terminal tab now sends `kill_terminal_request` (see `TerminalPanel.tsx`), but a terminal can\n * still end up orphaned (a daemon restart losing the tab's in-memory link, a dev-mode\n * double-mount race losing the created slot before it's persisted onto the tab, or simply a\n * terminal created outside this UI, e.g. via the CLI/MCP) — this store is the safety net for all\n * of those, not just the common case.\n */\n\nimport { create } from \"zustand\";\n\nexport interface TerminalRuntimeEntry {\n slot: number;\n workspaceId: string;\n name: string;\n cwd?: string;\n shell: string;\n cols: number;\n rows: number;\n closed: boolean;\n service?: boolean;\n}\n\ninterface TerminalStoreState {\n terminals: TerminalRuntimeEntry[];\n\n setTerminals(terminals: TerminalRuntimeEntry[]): void;\n}\n\nexport const useTerminalStore = create<TerminalStoreState>()((set) => ({\n terminals: [],\n\n setTerminals: (terminals) => set({ terminals }),\n}));\n","/**\n * `use-terminals` — subscribes to the live list of every terminal running on the daemon (POC has\n * no equivalent; new in the modern app, mirrors `use-checkout-status.ts`'s subscribe/push\n * pattern). `subscribe_terminals_request` returns the current snapshot immediately; subsequent\n * `terminals_update` broadcasts (fired by the server on create/rename/kill, to every connected\n * session — not scoped to whoever subscribed) keep the store live without polling.\n */\n\nimport { useEffect } from \"react\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useTerminalStore, type TerminalRuntimeEntry } from \"@pi-studio-ui/stores/terminal-store.js\";\n\ninterface TerminalsListResponse {\n terminals?: TerminalRuntimeEntry[];\n}\n\ninterface TerminalsUpdateMessage {\n type: \"terminals_update\";\n terminals: TerminalRuntimeEntry[];\n}\n\nfunction isTerminalsUpdate(msg: unknown): msg is TerminalsUpdateMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n \"type\" in msg &&\n msg.type === \"terminals_update\" &&\n \"terminals\" in msg &&\n Array.isArray((msg as { terminals: unknown }).terminals)\n );\n}\n\nexport function useTerminals(): void {\n const client = useConnectionStore((s) => s.client);\n const setTerminals = useTerminalStore((s) => s.setTerminals);\n\n useEffect(() => {\n if (!client) {\n setTerminals([]);\n return;\n }\n\n let cancelled = false;\n\n void client.connection\n .request<TerminalsListResponse>(\"subscribe_terminals_request\", {})\n .then((res) => {\n if (!cancelled) setTerminals(res.terminals ?? []);\n })\n .catch(() => {});\n\n const unsubscribeMessages = client.connection.onSessionMessage((msg) => {\n if (cancelled) return;\n const raw: unknown = msg;\n if (!isTerminalsUpdate(raw)) return;\n setTerminals(raw.terminals);\n });\n\n return () => {\n cancelled = true;\n unsubscribeMessages();\n void client.connection.request(\"unsubscribe_terminals_request\", {}).catch(() => {});\n };\n }, [client, setTerminals]);\n}\n","/**\n * TerminalsPanel — right-sidebar Terminals tab: a persistent \"New terminal\" button (mints a new\n * tab against the active workspace, same `openNewTerminal` helper as `Ctrl/Cmd+T` — this IS the\n * primary discoverable entry point for creating a terminal, no POC equivalent), plus the live\n * list of every PTY running on the daemon across every workspace, with reattach (open/focus its\n * tab) and kill actions. Complements `TerminalPanel.tsx` (POC_TO_APP_PLAN_UI.md §4.6): that\n * component owns one terminal *tab*'s lifecycle; the list here is the safety net for terminals\n * that outlive their tab (see `terminal-store.ts`'s doc comment for how that can happen) or were\n * never opened as a tab in this browser session at all (created via the CLI/MCP, or from another\n * connected client). Kill on an entry WITH an open tab closes that tab instead of hitting\n * `kill_terminal_request` directly — `TabPanelHost` unmounting is what actually tears down the\n * tab's live xterm instance and stream subscription, and `TerminalPanel`'s own unmount effect\n * sends the kill RPC itself (killing the PTY without closing the tab would leave a dead,\n * unresponsive terminal tab open).\n */\n\nimport { Terminal as TerminalIcon, X, Plus } from \"lucide-react\";\nimport { Button } from \"@pi-studio-ui/components/primitives/Button.js\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useTerminals } from \"@pi-studio-ui/hooks/use-terminals.js\";\nimport { useTerminalStore } from \"@pi-studio-ui/stores/terminal-store.js\";\nimport {\n useTabStore,\n tabIds,\n openNewTerminal,\n type Tab,\n type TerminalTabData,\n} from \"@pi-studio-ui/stores/tab-store.js\";\nimport styles from \"./TerminalsPanel.module.css\";\n\nexport function TerminalsPanel() {\n useTerminals();\n const client = useConnectionStore((s) => s.client);\n const terminals = useTerminalStore((s) => s.terminals);\n const tabs = useTabStore((s) => s.tabs);\n const activeWorkspaceCwd = useTabStore((s) => s.activeWorkspaceCwd);\n const openTab = useTabStore((s) => s.open);\n const activateTab = useTabStore((s) => s.activate);\n const closeTab = useTabStore((s) => s.close);\n\n // A terminal tab's `id` is NOT `tabIds.terminal(slot)` in general — `use-shortcuts.ts`'s\n // Ctrl/Cmd+T mints `term-new-<counter>` up front (before the slot exists) and never renames\n // the tab once `TerminalPanel` learns its real slot, only `data.slot` is updated. So the only\n // reliable way to find \"the tab for this slot\" is scanning by `data.slot`, not by id.\n function findTabForSlot(slot: number): Tab | undefined {\n return tabs.find((t) => t.kind === \"terminal\" && (t.data as TerminalTabData).slot === slot);\n }\n\n function handleReattach(slot: number, name: string, cwd: string | undefined) {\n const existing = findTabForSlot(slot);\n if (existing) {\n activateTab(existing.id);\n return;\n }\n // No tab anywhere points at this slot yet — open a fresh one around the EXISTING slot.\n // `TerminalPanel`'s slot-creation effect only fires when `data.slot` is null, so it reuses\n // this slot and just subscribes instead of spawning a new PTY.\n const workspaceCwd = cwd || \"~\";\n openTab({\n id: tabIds.terminal(slot),\n kind: \"terminal\",\n label: name,\n closable: true,\n data: { slot, cwd: workspaceCwd },\n workspaceCwd,\n });\n }\n\n function handleKill(slot: number) {\n // If a tab for this slot is open, closing it is enough — `TerminalPanel`'s true-unmount\n // effect sends `kill_terminal_request` itself, and closing the tab is the only way to also\n // tear down its live xterm instance/subscription (killing the PTY without closing the tab\n // would leave a dead, unresponsive terminal tab open). No open tab (an orphan) → nothing to\n // unmount, so kill the PTY directly.\n const existing = findTabForSlot(slot);\n if (existing) {\n closeTab(existing.id);\n return;\n }\n if (!client) return;\n void client.connection.request(\"kill_terminal_request\", { slot }).catch(() => {});\n }\n\n const newTerminalButton = (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n leftIcon={<Plus size={14} />}\n className={styles.newTerminal}\n onClick={() => openNewTerminal(activeWorkspaceCwd || \"~\")}\n >\n New terminal\n </Button>\n );\n\n if (terminals.length === 0) {\n return (\n <div className={styles.wrap}>\n {newTerminalButton}\n <div className={styles.emptyState}>No terminals running</div>\n </div>\n );\n }\n\n return (\n <div className={styles.wrap}>\n {newTerminalButton}\n <div className={styles.list}>\n <div className={styles.summary}>\n {terminals.length} terminal{terminals.length === 1 ? \"\" : \"s\"} running\n </div>\n {terminals.map((entry) => {\n const isOpen = findTabForSlot(entry.slot) !== undefined;\n return (\n <div key={entry.slot} className={styles.item}>\n <button\n type=\"button\"\n className={styles.reattach}\n onClick={() => handleReattach(entry.slot, entry.name, entry.cwd)}\n title={entry.cwd}\n >\n <TerminalIcon size={13} className={styles.icon} />\n <span className={styles.name}>{entry.name}</span>\n {isOpen && <span className={styles.openBadge}>open</span>}\n </button>\n <button\n type=\"button\"\n className={styles.kill}\n onClick={() => handleKill(entry.slot)}\n title=\"Kill terminal\"\n >\n <X size={13} />\n </button>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n","/**\n * RightSidebar — Files/Changes/Terminals tab bar + refresh button (POC `.sidebar-right`, chat.html\n * ~line 364-374, POC_TO_APP_PLAN_UI.md §4.7; Terminals tab is new in the modern app, no POC\n * equivalent — see `TerminalsPanel.tsx`). Imported by `routes/WorkspacePage.tsx`.\n */\n\nimport { RotateCw } from \"lucide-react\";\nimport { clsx } from \"clsx\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { Button } from \"@pi-studio-ui/components/primitives/Button.js\";\nimport { useUiStore } from \"@pi-studio-ui/stores/ui-store.js\";\nimport { useExplorerStore } from \"@pi-studio-ui/stores/explorer-store.js\";\nimport { useTabStore } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { FileExplorer } from \"./FileExplorer.js\";\nimport { ChangesPanel } from \"@pi-studio-ui/features/git/ChangesPanel.js\";\nimport { TerminalsPanel } from \"@pi-studio-ui/features/terminal/TerminalsPanel.js\";\nimport styles from \"./RightSidebar.module.css\";\n\nexport function RightSidebar() {\n const rightSidebarTab = useUiStore((s) => s.rightSidebarTab);\n const setRightSidebarTab = useUiStore((s) => s.setRightSidebarTab);\n const currentPath = useExplorerStore((s) => s.currentPath);\n // Files/Changes browse the workspace currently in view in the tab strip — the same signal\n // every tab creation site uses (§4.7 follow-up: workspace-scoped tabs).\n const activeWorkspaceCwd = useTabStore((s) => s.activeWorkspaceCwd);\n const cwd = activeWorkspaceCwd || \"~\";\n const client = useConnectionStore((s) => s.client);\n const queryClient = useQueryClient();\n\n // Fires a one-shot refresh RPC (POC `checkout_refresh_request`) — subscription lifecycle for\n // live `checkout_status_update` pushes is owned solely by `ChangesPanel`'s `useCheckoutStatus`,\n // so this button never opens a second subscription that could outlive/race the panel's. Only\n // relevant to Files/Changes — Terminals is live-pushed via `terminals_update`, nothing to poll.\n function handleRefresh() {\n void queryClient.invalidateQueries({ queryKey: [\"explorer\", currentPath] });\n if (client && cwd) {\n void client.connection.request(\"checkout_refresh_request\", { cwd }).catch(() => {});\n }\n }\n return (\n <div className={styles.wrap}>\n <div className={styles.tabBar}>\n <button\n type=\"button\"\n className={clsx(styles.tabBtn, rightSidebarTab === \"files\" && styles.active)}\n onClick={() => setRightSidebarTab(\"files\")}\n >\n Files\n </button>\n <button\n type=\"button\"\n className={clsx(styles.tabBtn, rightSidebarTab === \"changes\" && styles.active)}\n onClick={() => setRightSidebarTab(\"changes\")}\n >\n Changes\n </button>\n <button\n type=\"button\"\n className={clsx(styles.tabBtn, rightSidebarTab === \"terminals\" && styles.active)}\n onClick={() => setRightSidebarTab(\"terminals\")}\n >\n Terminals\n </button>\n {rightSidebarTab !== \"terminals\" && (\n <Button\n size=\"xs\"\n variant=\"ghost\"\n iconOnly\n title=\"Refresh\"\n className={styles.refreshBtn}\n onClick={handleRefresh}\n >\n <RotateCw size={13} />\n </Button>\n )}\n </div>\n <div className={styles.content}>\n {rightSidebarTab === \"files\" ? (\n <FileExplorer />\n ) : rightSidebarTab === \"changes\" ? (\n <ChangesPanel />\n ) : (\n <TerminalsPanel />\n )}\n </div>\n </div>\n );\n}\n","/**\n * Checkbox — accessible checkbox control.\n * ui-components.md § Inputs & form controls\n */\n\nimport { type InputHTMLAttributes, type ReactNode, forwardRef } from \"react\";\nimport { Check } from \"lucide-react\";\nimport styles from \"./Checkbox.module.css\";\n\nexport interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, \"type\"> {\n label?: ReactNode;\n checked?: boolean;\n}\n\nexport const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(\n function Checkbox({ label, checked = false, disabled, className, onChange, ...rest }, ref) {\n return (\n <label className={styles.wrapper} data-disabled={disabled || undefined}>\n <input\n ref={ref}\n type=\"checkbox\"\n className={styles.nativeInput}\n checked={checked}\n disabled={disabled}\n onChange={onChange}\n {...rest}\n />\n <span className={styles.box} data-checked={checked} aria-hidden>\n {checked && <Check size={12} className={styles.checkmark} />}\n </span>\n {label && <span>{label}</span>}\n </label>\n );\n },\n);\n","/**\n * Select + Combobox — form selection controls.\n * Select = native <select> styled for theme consistency.\n * Combobox = filterable text input + dropdown list, consuming sprint-012 combobox model.\n * ui-components.md § Inputs & form controls\n */\n\nimport {\n forwardRef,\n useState,\n useRef,\n useEffect,\n type SelectHTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport { clsx } from \"clsx\";\nimport styles from \"./Select.module.css\";\nimport {\n filterOptions,\n comboboxReducer,\n initialComboboxState,\n type ComboboxState,\n type ComboboxOption,\n type ComboboxAction,\n} from \"@pi-studio-ui/ui/combobox.js\";\n\n// ---------------------------------------------------------------------------\n// Native Select\n// ---------------------------------------------------------------------------\n\nexport interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {\n options: { value: string; label: string }[];\n placeholder?: string;\n}\n\nexport const Select = forwardRef<HTMLSelectElement, SelectProps>(\n function Select({ options, placeholder, className, ...rest }, ref) {\n return (\n <select ref={ref} className={clsx(styles.select, className)} {...rest}>\n {placeholder && (\n <option value=\"\" disabled>\n {placeholder}\n </option>\n )}\n {options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n );\n },\n);\n\n// ---------------------------------------------------------------------------\n// Combobox\n// ---------------------------------------------------------------------------\n\nexport interface ComboboxProps {\n options: ComboboxOption<string>[];\n value?: string;\n onSelect?: (value: string) => void;\n placeholder?: string;\n className?: string;\n}\n\nexport function Combobox({\n options,\n value,\n onSelect,\n placeholder = \"Search…\",\n className,\n}: ComboboxProps) {\n const [state, setState] = useState<ComboboxState<string>>(() =>\n initialComboboxState(options),\n );\n const inputRef = useRef<HTMLInputElement>(null);\n\n function dispatch(action: ComboboxAction) {\n setState((prev) => comboboxReducer(prev, action));\n }\n\n function handleSelect(opt: ComboboxOption<string>) {\n onSelect?.(opt.value);\n dispatch({ type: \"CLOSE\" });\n }\n\n // Close on outside click\n const wrapperRef = useRef<HTMLDivElement>(null);\n useEffect(() => {\n function handleClick(e: MouseEvent) {\n if (wrapperRef.current && !wrapperRef.current.contains(e.target as Node)) {\n dispatch({ type: \"CLOSE\" });\n }\n }\n document.addEventListener(\"mousedown\", handleClick);\n return () => document.removeEventListener(\"mousedown\", handleClick);\n }, []);\n\n return (\n <div ref={wrapperRef} className={clsx(styles.comboboxWrapper, className)}>\n <input\n ref={inputRef}\n className={styles.comboboxInput}\n value={state.query}\n placeholder={placeholder}\n onChange={(e) => dispatch({ type: \"SET_QUERY\", query: e.target.value })}\n onFocus={() => dispatch({ type: \"OPEN\" })}\n onKeyDown={(e) => {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n dispatch({ type: \"ARROW_DOWN\" });\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n dispatch({ type: \"ARROW_UP\" });\n } else if (e.key === \"Enter\" && state.isOpen) {\n e.preventDefault();\n const opt = state.filtered[state.highlightedIndex];\n if (opt) handleSelect(opt);\n } else if (e.key === \"Escape\") {\n dispatch({ type: \"CLOSE\" });\n }\n }}\n role=\"combobox\"\n aria-expanded={state.isOpen}\n aria-autocomplete=\"list\"\n />\n {state.isOpen && state.filtered.length > 0 && (\n <div className={styles.comboboxList} role=\"listbox\">\n {state.filtered.map((opt, i) => (\n <button\n key={opt.value}\n className={clsx(\n styles.comboboxOption,\n i === state.highlightedIndex && styles.comboboxOptionHighlighted,\n )}\n role=\"option\"\n aria-selected={i === state.highlightedIndex}\n onMouseDown={(e) => {\n e.preventDefault();\n handleSelect(opt);\n }}\n >\n {opt.label}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n","/**\n * Dialog — generic centered modal chrome (overlay + card + header/title/close + optional footer),\n * wrapping Radix `Dialog` (design-system.md § Overlays: `@radix-ui/react-dialog` for\n * dialogs/menus/tooltips/popovers). Every feature-specific modal (workspace picker today; rename/\n * confirm dialogs later) SHOULD build on this instead of hand-rolling `Dialog.Root/Portal/Overlay/\n * Content` again — only the body content and footer actions vary per use.\n */\n\nimport { type ReactNode } from \"react\";\nimport * as RadixDialog from \"@radix-ui/react-dialog\";\nimport { X } from \"lucide-react\";\nimport { clsx } from \"clsx\";\nimport styles from \"./Dialog.module.css\";\n\nexport interface DialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n title: string;\n /** Body content between the header and the optional footer. */\n children?: ReactNode;\n /** Sticky action row rendered under the body (e.g. Cancel/Confirm buttons). */\n footer?: ReactNode;\n /** Content card width; defaults to 500px (design-system.md § Overlays desktop dialog sizing). */\n width?: number | string;\n /** Extra class applied to the content card, for callers that need to tweak body sizing. */\n className?: string;\n /**\n * Omit the header bar's reserved height and float the close button over the body instead.\n * Use for content (e.g. an image lightbox) that should sit dead-center in the viewport rather\n * than be pushed down by an asymmetric top-only header. `title` remains as an accessible\n * (visually hidden) label for the dialog.\n */\n bare?: boolean;\n}\n\n/** Re-exported so callers can wrap a footer button in `<Dialog.Close asChild>` to auto-dismiss\n * without threading `onOpenChange(false)` through their own click handler. */\nexport const DialogClose = RadixDialog.Close;\n\nexport function Dialog({\n open,\n onOpenChange,\n title,\n children,\n footer,\n width,\n className,\n bare = false,\n}: DialogProps) {\n return (\n <RadixDialog.Root open={open} onOpenChange={onOpenChange}>\n <RadixDialog.Portal>\n <RadixDialog.Overlay className={styles.overlay} />\n <div className={styles.contentWrapper}>\n <RadixDialog.Content\n className={clsx(styles.dialog, bare && styles.bare, className)}\n style={width !== undefined ? { width } : undefined}\n >\n {bare ? (\n <RadixDialog.Title className={styles.visuallyHidden}>{title}</RadixDialog.Title>\n ) : (\n <div className={styles.header}>\n <RadixDialog.Title className={styles.title}>{title}</RadixDialog.Title>\n <RadixDialog.Close asChild>\n <button type=\"button\" className={styles.closeBtn} aria-label=\"Close\">\n <X size={16} />\n </button>\n </RadixDialog.Close>\n </div>\n )}\n <div className={clsx(styles.body, bare && styles.bareBody)}>{children}</div>\n {bare && (\n <RadixDialog.Close asChild>\n <button type=\"button\" className={styles.bareCloseBtn} aria-label=\"Close\">\n <X size={16} />\n </button>\n </RadixDialog.Close>\n )}\n {footer && <div className={styles.footer}>{footer}</div>}\n </RadixDialog.Content>\n </div>\n </RadixDialog.Portal>\n </RadixDialog.Root>\n );\n}\n","/**\n * ResizeHandle — thin draggable strip for resizing an adjacent sidebar (`WorkspacePage`'s\n * `sidebarLeft`/`sidebarRight`). Pointer-driven: `pointerdown` starts a drag, `pointermove`\n * reports the raw pixel delta since the last move, `pointerup` ends it. Width clamping and\n * persistence live in the caller (`ui-store.ts`'s `setLeftSidebarWidth`/`setRightSidebarWidth`) —\n * this component only ever reports deltas, never owns or reads the width itself.\n */\n\nimport { useCallback, useRef } from \"react\";\nimport { clsx } from \"clsx\";\nimport styles from \"./ResizeHandle.module.css\";\n\nexport interface ResizeHandleProps {\n /** Which sidebar this handle resizes. Left sidebar grows when dragged right (positive delta);\n * right sidebar shrinks when dragged right (its left edge moves right into the panel) — `side`\n * picks the correct sign so the caller only ever adds the raw delta to its current width. */\n side: \"left\" | \"right\";\n onResize(deltaX: number): void;\n className?: string;\n}\n\nexport function ResizeHandle({ side, onResize, className }: ResizeHandleProps) {\n const startXRef = useRef(0);\n\n const handlePointerMove = useCallback(\n (ev: PointerEvent) => {\n const raw = ev.clientX - startXRef.current;\n startXRef.current = ev.clientX;\n onResize(side === \"left\" ? raw : -raw);\n },\n [onResize, side],\n );\n\n const handlePointerUp = useCallback(\n (ev: PointerEvent) => {\n window.removeEventListener(\"pointermove\", handlePointerMove);\n window.removeEventListener(\"pointerup\", handlePointerUp);\n document.body.style.cursor = \"\";\n document.body.style.userSelect = \"\";\n (ev.target as HTMLElement | null)?.releasePointerCapture?.(ev.pointerId);\n },\n [handlePointerMove],\n );\n\n function handlePointerDown(ev: React.PointerEvent<HTMLDivElement>): void {\n ev.preventDefault();\n startXRef.current = ev.clientX;\n document.body.style.cursor = \"col-resize\";\n document.body.style.userSelect = \"none\";\n window.addEventListener(\"pointermove\", handlePointerMove);\n window.addEventListener(\"pointerup\", handlePointerUp);\n }\n\n return (\n <div\n className={clsx(styles.handle, className)}\n onPointerDown={handlePointerDown}\n role=\"separator\"\n aria-orientation=\"vertical\"\n />\n );\n}\n","/**\n * Shared \"open a workspace\" resolver for the folder-picker dialog: given a target cwd, either\n * activates the workspace's most recently active existing session, or creates a fresh one —\n * avoids piling up duplicate sessions when the user re-opens a folder they already have a chat\n * in. Distinct from `SessionList`'s \"+ New conversation\", which always starts a brand-new\n * session by design (POC_TO_APP_PLAN_UI.md §4.3 workspace grouping, §4.7 follow-up: open-workspace\n * dialog).\n */\n\nimport { useSessionStore } from \"@pi-studio-ui/stores/session-store.js\";\nimport { useTabStore, tabIds } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { useUiStore } from \"@pi-studio-ui/stores/ui-store.js\";\nimport { groupSessionsByWorkspace } from \"./workspace-grouping.js\";\n\n/**\n * Resolve `cwd` (already tilde/absolute-normalized by the caller, e.g. via `resolveTildePath`) to\n * a session: reuses the workspace's most recent session if one already exists at that cwd,\n * otherwise creates a new one. Activates the session and opens/focuses its chat tab. Also seeds\n * `useUiStore().cwd` so a subsequent bare \"+ New conversation\" defaults to this workspace.\n *\n * `homeDir` MUST be the same value the sidebar's grouping uses (`useHomeDir()`), so this matches\n * the exact workspace bucket a tilde-form and absolute-form cwd would collapse into — reusing a\n * different normalization here would resurrect the tilde/absolute duplicate-workspace bug.\n */\nexport function openWorkspace(cwd: string, homeDir: string | null): void {\n const { order, sessions, createSession, activate } = useSessionStore.getState();\n\n const groups = groupSessionsByWorkspace(order, sessions, homeDir);\n const existing = groups.find((g) => g.cwd === cwd);\n\n const sessionId = existing?.sessions[0]?.id ?? createSession(cwd);\n activate(sessionId);\n\n const session = useSessionStore.getState().sessions[sessionId];\n useTabStore.getState().open({\n id: tabIds.chat(sessionId),\n kind: \"chat\",\n label: session?.title ?? \"New chat\",\n closable: true,\n data: { sessionId },\n workspaceCwd: cwd,\n });\n\n useUiStore.getState().setCwd(cwd);\n}\n","/**\n * OpenWorkspaceDialog — directory browser for opening a new workspace (evolved from the POC\n * `#cwd-overlay`, chat.html ~line 328-336, 1122-1141; POC_TO_APP_PLAN_UI.md §4.7 follow-up:\n * toolbar \"Open Workspace\" button). Built on the generic `Dialog` primitive\n * (`components/primitives/Dialog.tsx`) — this file owns only the address input + Go/Up buttons +\n * directory list body. \"Open\" resolves the selected path to a session via `openWorkspace()` —\n * reusing the workspace's most recent existing session if one is already open there, else\n * creating a new one — then closes.\n */\n\nimport { useEffect, useState } from \"react\";\nimport { ArrowUp, Folder } from \"lucide-react\";\nimport { Button, Dialog, DialogClose } from \"@pi-studio-ui/components/primitives/index.js\";\nimport { TextInput } from \"@pi-studio-ui/components/primitives/TextInput.js\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useUiStore } from \"@pi-studio-ui/stores/ui-store.js\";\nimport { useTabStore } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { normalizeCwd } from \"@pi-studio-ui/features/sessions/workspace-grouping.js\";\nimport { useExplorer } from \"@pi-studio-ui/hooks/use-explorer.js\";\nimport { useHomeDir } from \"@pi-studio-ui/hooks/use-home-dir.js\";\nimport { openWorkspace } from \"@pi-studio-ui/features/sessions/open-workspace.js\";\nimport styles from \"./OpenWorkspaceDialog.module.css\";\n\nexport function OpenWorkspaceDialog() {\n const open = useUiStore((s) => s.cwdPickerOpen);\n const closeDialog = useUiStore((s) => s.closeCwdPicker);\n const activeWorkspaceCwd = useTabStore((s) => s.activeWorkspaceCwd);\n const client = useConnectionStore((s) => s.client);\n const homeDir = useHomeDir();\n\n const [path, setPathState] = useState(\"/\");\n const [inputValue, setInputValue] = useState(\"/\");\n\n // Seed the picker at the workspace currently in view each time it opens.\n useEffect(() => {\n if (!open) return;\n const seed = normalizeCwd(activeWorkspaceCwd || \"~\", homeDir);\n setPathState(seed);\n setInputValue(seed);\n }, [open, client, activeWorkspaceCwd]);\n\n const { data, isLoading, isError, error } = useExplorer(path, open && Boolean(client));\n const dirEntries = (data?.entries ?? []).filter((e) => e.kind === \"directory\");\n\n function navigate(next: string) {\n setPathState(next);\n setInputValue(next);\n }\n\n function handleUp() {\n navigate(path.split(\"/\").slice(0, -1).join(\"/\") || \"/\");\n }\n\n function handleGo() {\n navigate(inputValue.trim());\n }\n\n function handleOpen() {\n openWorkspace(path, homeDir);\n closeDialog();\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => !next && closeDialog()}\n title=\"Open Workspace\"\n footer={\n <>\n <DialogClose asChild>\n <Button size=\"sm\" variant=\"secondary\">\n Cancel\n </Button>\n </DialogClose>\n <Button size=\"sm\" onClick={handleOpen}>\n Open this folder\n </Button>\n </>\n }\n >\n <div className={styles.pathRow}>\n <Button size=\"xs\" variant=\"ghost\" iconOnly title=\"Up\" onClick={handleUp}>\n <ArrowUp size={13} />\n </Button>\n <TextInput\n className={styles.pathInput}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n handleGo();\n }\n }}\n />\n <Button size=\"xs\" onClick={handleGo}>\n Go\n </Button>\n </div>\n <div className={styles.list}>\n {isLoading ? (\n <div className={styles.emptyState}>Loading...</div>\n ) : isError ? (\n <div className={styles.emptyState}>\n Error: {error instanceof Error ? error.message : \"unknown error\"}\n </div>\n ) : dirEntries.length === 0 ? (\n <div className={styles.emptyState}>No subdirectories</div>\n ) : (\n dirEntries.map((entry) => (\n <div\n key={entry.name}\n className={styles.item}\n onClick={() => navigate(path === \"/\" ? `/${entry.name}` : `${path}/${entry.name}`)}\n >\n <Folder size={14} />\n <span className={styles.name}>{entry.name}</span>\n </div>\n ))\n )}\n </div>\n </Dialog>\n );\n}\n","/**\n * WorkspacePage — the 3-column shell (POC `.main`: sidebar-left / center / sidebar-right,\n * POC_TO_APP_PLAN_UI.md §3). Feature panels are mounted here once each lands. Either sidebar can\n * be hidden via the Toolbar's panel toggles (`ui-store.ts` `leftSidebarCollapsed`/\n * `rightSidebarCollapsed`) — collapsed just means the `aside` isn't rendered, so `TabPanelHost`'s\n * `.center` column simply grows to fill the freed width. Each visible sidebar is resizable via a\n * `ResizeHandle` on its inner edge, dragging `ui-store.ts`'s `leftSidebarWidth`/\n * `rightSidebarWidth` (clamped to `[MIN_SIDEBAR_WIDTH, MAX_SIDEBAR_WIDTH]`) through the\n * delta-based `resizeLeftSidebar`/`resizeRightSidebar` actions.\n */\n\nimport { Toolbar } from \"@pi-studio-ui/features/connection/Toolbar.js\";\nimport { SessionList } from \"@pi-studio-ui/features/sessions/SessionList.js\";\nimport { TabStrip } from \"@pi-studio-ui/features/workspace/TabStrip.js\";\nimport { TabPanelHost } from \"@pi-studio-ui/features/workspace/TabPanelHost.js\";\nimport { RightSidebar } from \"@pi-studio-ui/features/files/RightSidebar.js\";\nimport { OpenWorkspaceDialog } from \"@pi-studio-ui/features/workspace-picker/OpenWorkspaceDialog.js\";\nimport { ResizeHandle } from \"@pi-studio-ui/components/primitives/ResizeHandle.js\";\nimport { useUiStore } from \"@pi-studio-ui/stores/ui-store.js\";\nimport styles from \"./WorkspacePage.module.css\";\n\nexport function WorkspacePage() {\n const leftSidebarCollapsed = useUiStore((s) => s.leftSidebarCollapsed);\n const rightSidebarCollapsed = useUiStore((s) => s.rightSidebarCollapsed);\n const leftSidebarWidth = useUiStore((s) => s.leftSidebarWidth);\n const rightSidebarWidth = useUiStore((s) => s.rightSidebarWidth);\n const resizeLeftSidebar = useUiStore((s) => s.resizeLeftSidebar);\n const resizeRightSidebar = useUiStore((s) => s.resizeRightSidebar);\n\n return (\n <div className={styles.shell}>\n <Toolbar />\n <div className={styles.main}>\n {!leftSidebarCollapsed && (\n <aside className={styles.sidebarLeft} style={{ width: leftSidebarWidth }}>\n <SessionList />\n <ResizeHandle side=\"left\" className={styles.leftHandle} onResize={resizeLeftSidebar} />\n </aside>\n )}\n <div className={styles.center}>\n <TabStrip />\n <TabPanelHost />\n </div>\n {!rightSidebarCollapsed && (\n <aside className={styles.sidebarRight} style={{ width: rightSidebarWidth }}>\n <ResizeHandle side=\"right\" className={styles.rightHandle} onResize={resizeRightSidebar} />\n <RightSidebar />\n </aside>\n )}\n </div>\n <OpenWorkspaceDialog />\n </div>\n );\n}\n","/**\n * Boot hook: seeds the toolbar (host/password/cwd) from URL params\n * (POC `?host=&password=&cwd=&connect=1`, POC_TO_APP_PLAN_UI.md §4.1) and optionally\n * auto-connects. Runs once on mount.\n */\n\nimport { useEffect, useRef } from \"react\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useUiStore } from \"@pi-studio-ui/stores/ui-store.js\";\n\nexport function useConnectionBoot(): void {\n const ranRef = useRef(false);\n\n useEffect(() => {\n if (ranRef.current) return;\n ranRef.current = true;\n\n const qs = new URLSearchParams(window.location.search);\n const host = qs.get(\"host\");\n const password = qs.get(\"password\");\n const cwd = qs.get(\"cwd\");\n const shouldConnect = qs.get(\"connect\") === \"1\";\n\n const ui = useUiStore.getState();\n if (host) ui.setHost(host);\n if (password) ui.setPassword(password);\n if (cwd) ui.setCwd(cwd);\n\n if (shouldConnect) {\n const { host: h, password: pw } = useUiStore.getState();\n void useConnectionStore.getState().connect({ url: h, password: pw || undefined });\n }\n }, []);\n}\n","/**\n * `fetch_agent_timeline_request` returns `items: unknown[]` (not yet in the strict protocol\n * union). The POC's `restoreSessions()` observed a stable shape at runtime: each item is\n * `{kind:'other'|'assistant'|'tool_call', event?, events?}` carrying nested `AgentStreamEvent`s.\n * This flattens that shape into a plain event list the timeline reducer can replay.\n */\n\nimport type { AgentStreamEvent } from \"@av-pi-studio/protocol\";\n\ninterface TimelineItemOther {\n kind: \"other\";\n event?: AgentStreamEvent;\n}\n\ninterface TimelineItemAssistant {\n kind: \"assistant\";\n events?: AgentStreamEvent[];\n}\n\ninterface TimelineItemToolCall {\n kind: \"tool_call\";\n events?: AgentStreamEvent[];\n}\n\ntype TimelineItem = TimelineItemOther | TimelineItemAssistant | TimelineItemToolCall;\n\nfunction isTimelineItem(value: unknown): value is TimelineItem {\n return typeof value === \"object\" && value !== null && \"kind\" in value;\n}\n\n/** Flatten authoritative timeline `items` into an ordered `AgentStreamEvent[]` for replay. */\nexport function flattenTimelineItems(items: readonly unknown[]): AgentStreamEvent[] {\n const events: AgentStreamEvent[] = [];\n for (const raw of items) {\n if (!isTimelineItem(raw)) continue;\n if (raw.kind === \"other\") {\n if (raw.event) events.push(raw.event);\n } else {\n for (const event of raw.events ?? []) events.push(event);\n }\n }\n return events;\n}\n","/**\n * Session restore — on a fresh `open` connection, fetches known agents\n * (`list_agents_request`) and hydrates each into the session store from its authoritative\n * timeline (`fetch_agent_timeline_request`). Ports POC `restoreSessions()`/`onConnected()`\n * (POC_TO_APP_PLAN_UI.md §4.3 \"Session restore\"). Live agent-stream subscription for restored\n * sessions is wired by `ChatPanel`'s `useAgentStream` once its tab mounts, keyed off `agentId` —\n * no `expectingAgent` race, since `agentId` is always known before subscribing.\n */\n\nimport { useEffect, useRef } from \"react\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useSessionStore } from \"@pi-studio-ui/stores/session-store.js\";\nimport { useTabStore, tabIds } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { useUiStore } from \"@pi-studio-ui/stores/ui-store.js\";\nimport { resolveHome } from \"@pi-studio-ui/stores/explorer-store.js\";\nimport { normalizeCwd } from \"@pi-studio-ui/features/sessions/workspace-grouping.js\";\nimport { EMPTY_TIMELINE, applyStreamEvent } from \"@pi-studio-ui/timeline/reducer.js\";\nimport { flattenTimelineItems } from \"@pi-studio-ui/lib/protocol/events.js\";\nimport type { AgentStatus } from \"@av-pi-studio/protocol\";\n\ninterface RestoredAgent {\n agentId: string;\n status?: string;\n cwd?: string;\n title?: string;\n lastActivity?: number;\n}\n\nexport function useSessionRestore(): void {\n const client = useConnectionStore((s) => s.client);\n const status = useConnectionStore((s) => s.status);\n const restoredRef = useRef(false);\n\n useEffect(() => {\n if (status !== \"open\" || !client) return;\n if (restoredRef.current) return; // one restore per connection lifetime\n restoredRef.current = true;\n\n void (async () => {\n let agents: RestoredAgent[] = [];\n try {\n const result = await client.connection.request<{ agents?: RestoredAgent[] }>(\n \"list_agents_request\",\n { all: false },\n );\n agents = result.agents ?? [];\n } catch {\n agents = [];\n }\n\n agents.sort((a, b) => (b.lastActivity ?? 0) - (a.lastActivity ?? 0));\n\n const sessionStore = useSessionStore.getState();\n for (const agent of agents) {\n if (sessionStore.findByAgentId(agent.agentId)) continue;\n\n let timeline = EMPTY_TIMELINE;\n try {\n const page = await client.agent(agent.agentId).timeline.fetch({\n direction: \"after\",\n limit: 200,\n });\n const events = flattenTimelineItems(page.items);\n timeline = events.reduce(applyStreamEvent, EMPTY_TIMELINE);\n } catch {\n // Best-effort: an unreadable timeline still gets an (empty) restored session.\n }\n\n sessionStore.hydrate({\n id: `s-${agent.agentId.slice(0, 12)}`,\n agentId: agent.agentId,\n title: agent.title || agent.cwd?.split(\"/\").pop() || \"Restored\",\n status: (agent.status as AgentStatus | undefined) ?? \"idle\",\n cwd: agent.cwd || \"~\",\n timeline,\n userMessageCount: timeline.rows.filter((r) => r.kind === \"user\").length,\n });\n }\n\n const homeDir = await resolveHome(client).catch(() => null);\n const { order, sessions } = useSessionStore.getState();\n if (order.length > 0) {\n const first = sessions[order[0] ?? \"\"];\n if (first) {\n const targetCwd = normalizeCwd(first.cwd || \"~\", homeDir);\n useSessionStore.getState().activate(first.id);\n useUiStore.getState().setCwd(first.cwd);\n useTabStore.getState().open({\n id: tabIds.chat(first.id),\n kind: \"chat\",\n label: first.title,\n closable: true,\n data: { sessionId: first.id },\n workspaceCwd: targetCwd,\n });\n }\n } else {\n // No restored sessions — start one fresh, mirroring POC `if (sessions.length===0) createSession()`.\n const cwd = useUiStore.getState().cwd || \"~\";\n const targetCwd = normalizeCwd(cwd, homeDir);\n const id = useSessionStore.getState().createSession(cwd);\n useTabStore.getState().open({\n id: tabIds.chat(id),\n kind: \"chat\",\n label: \"New chat\",\n closable: true,\n data: { sessionId: id },\n workspaceCwd: targetCwd,\n });\n }\n })();\n }, [status, client]);\n}\n","/**\n * Global keymap: Ctrl/Cmd+T new terminal, Ctrl/Cmd+W close active tab, Esc close overlays\n * (POC `document.addEventListener(\"keydown\", …)`, POC_TO_APP_PLAN_UI.md §4.8).\n */\n\nimport { useEffect } from \"react\";\nimport { useTabStore, openNewTerminal } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { useUiStore } from \"@pi-studio-ui/stores/ui-store.js\";\n\nexport function useShortcuts(): void {\n useEffect(() => {\n function onKeyDown(ev: KeyboardEvent) {\n const ui = useUiStore.getState();\n\n if (ev.key === \"Escape\") {\n ui.closeCwdPicker();\n ui.closeSessionMenu();\n return;\n }\n\n const mod = ev.ctrlKey || ev.metaKey;\n if (mod && ev.key.toLowerCase() === \"t\") {\n ev.preventDefault();\n // Captured synchronously here, not inside the terminal's mount effect — a rapid\n // workspace-switch-then-Ctrl+T must never race the async `create_terminal_request`.\n openNewTerminal(useTabStore.getState().activeWorkspaceCwd || \"~\");\n return;\n }\n\n if (mod && ev.key.toLowerCase() === \"w\") {\n const { activeTabId, tabs, close } = useTabStore.getState();\n if (!activeTabId) return;\n const tab = tabs.find((t) => t.id === activeTabId);\n if (tab?.closable !== false) {\n ev.preventDefault();\n close(activeTabId);\n }\n }\n }\n\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, []);\n}\n","import { AppProviders } from \"./providers/AppProviders.js\";\nimport { WorkspacePage } from \"./routes/WorkspacePage.js\";\nimport { useConnectionBoot } from \"./hooks/use-connection.js\";\nimport { useSessionRestore } from \"./hooks/use-session-restore.js\";\nimport { useShortcuts } from \"./hooks/use-shortcuts.js\";\n\nfunction Boot() {\n useConnectionBoot();\n useSessionRestore();\n useShortcuts();\n return null;\n}\n\n// Root component. Connection boot + session restore + global shortcuts run once at the top;\n// the workspace shell renders the 3-column layout.\nexport function App() {\n return (\n <AppProviders>\n <Boot />\n <WorkspacePage />\n </AppProviders>\n );\n}\n","import { StrictMode } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport \"./global.css\";\nimport { App } from \"./app.js\";\n\nconst root = document.getElementById(\"root\");\nif (!root) throw new Error(\"Missing #root element\");\n\ncreateRoot(root).render(\n <StrictMode>\n <App />\n </StrictMode>,\n);\n"],"file":"assets/index-DnTTpAX5.js"}
|