agent-decoder 0.3.5__tar.gz → 0.3.6__tar.gz
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.
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/.gitignore +5 -1
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/ARCHITECTURE.md +52 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/CHANGELOG.md +21 -1
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/PKG-INFO +1 -1
- agent_decoder-0.3.6/docs/agent-runs.md +15 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/App.tsx +13 -1
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/api/client.ts +2 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/api/types.ts +22 -0
- agent_decoder-0.3.6/frontend/src/components/ArtifactsPanel/ArtifactsPanel.tsx +191 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/ConversationHeader.tsx +21 -1
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/__tests__/NativeTerminalView.test.tsx +2 -0
- agent_decoder-0.3.6/frontend/src/components/ChatView/nativeTerminal/__tests__/eraseScrollback.test.ts +64 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/nativeTerminal/xtermTerminal.ts +17 -1
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/TextBlock.tsx +29 -7
- agent_decoder-0.3.6/frontend/src/components/Runs.css +26 -0
- agent_decoder-0.3.6/frontend/src/components/Runs.tsx +97 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/ShortcutsPopup.tsx +1 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/Sidebar.tsx +5 -0
- agent_decoder-0.3.6/frontend/src/components/__tests__/Runs.test.tsx +50 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ui/icons.tsx +11 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/router.ts +2 -1
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/store/index.ts +9 -0
- agent_decoder-0.3.6/frontend/src/utils/__tests__/artifacts.test.ts +35 -0
- agent_decoder-0.3.6/frontend/src/utils/artifacts.ts +66 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/localStorage.ts +1 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/pyproject.toml +13 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/__init__.py +1 -1
- agent_decoder-0.3.6/src/agent_decoder/_tmux_capture.py +23 -0
- agent_decoder-0.3.6/src/agent_decoder/_tmux_exec.py +16 -0
- agent_decoder-0.3.6/src/agent_decoder/agent_runs.py +266 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/db.py +50 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/driver_manager.py +53 -78
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/drivers/base.py +14 -2
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/drivers/registry.py +4 -5
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/drivers/tmux_driver.py +24 -8
- agent_decoder-0.3.6/src/agent_decoder/execution_manager.py +414 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/pty_runner.py +145 -7
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/scanner.py +14 -2
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/server.py +134 -81
- agent_decoder-0.3.6/src/agent_decoder/static/assets/ArtifactsPanel-D-CqWycU.js +1 -0
- agent_decoder-0.3.5/src/agent_decoder/static/assets/ChatView-keqP4MzD.js → agent_decoder-0.3.6/src/agent_decoder/static/assets/ChatView-CFunVM15.js +39 -39
- agent_decoder-0.3.6/src/agent_decoder/static/assets/Dashboard-CBe8ZXJ-.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/FileViewer-CVtveC8R.js +1 -0
- agent_decoder-0.3.5/src/agent_decoder/static/assets/HelpPopup-D7mbl9pT.js → agent_decoder-0.3.6/src/agent_decoder/static/assets/HelpPopup-BYhBIvA6.js +1 -1
- agent_decoder-0.3.6/src/agent_decoder/static/assets/Runs-DvLUpR9O.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/Runs-Y8oF-gxW.css +1 -0
- agent_decoder-0.3.5/src/agent_decoder/static/assets/ScrollContainer-BT_Dzxah.js → agent_decoder-0.3.6/src/agent_decoder/static/assets/ScrollContainer-CA7-aYqv.js +1 -1
- agent_decoder-0.3.6/src/agent_decoder/static/assets/SearchOverlay-CF4dXG6x.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/SessionItem-BUejVuL5.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/SettingsModal-DWSMRQmn.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/ShortcutsPopup-Df9bhfs6.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/Sidebar-CJcwJziw.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/fileType-BHvAriyJ.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/formatRelative-CavTjOcy.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/hljs-CvdnYs1C.js +35 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/index-XPrlVfCr.js +5 -0
- agent_decoder-0.3.5/src/agent_decoder/static/assets/react-Bi_azaFt.js → agent_decoder-0.3.6/src/agent_decoder/static/assets/react-CCIEwYL0.js +4 -4
- agent_decoder-0.3.6/src/agent_decoder/static/assets/renderSnippet-CcNMfZRd.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/sessionCache-Cj3FsbQ4.js +1 -0
- agent_decoder-0.3.5/src/agent_decoder/static/assets/shared-DdVGprvF.js → agent_decoder-0.3.6/src/agent_decoder/static/assets/shared-CAVcrZag.js +1 -1
- agent_decoder-0.3.6/src/agent_decoder/static/assets/ui-BVMZ0uWi.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/useDebounce-CRtjXm0i.js +1 -0
- agent_decoder-0.3.6/src/agent_decoder/static/assets/useSessionDetail-D4HneaxZ.js +1 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/index.html +3 -4
- agent_decoder-0.3.6/src/agent_decoder/tmux_terminal.py +297 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/drivers/test_driver_manager.py +21 -4
- agent_decoder-0.3.6/tests/test_agent_runs.py +272 -0
- agent_decoder-0.3.6/tests/test_execution_manager.py +408 -0
- agent_decoder-0.3.6/tests/test_scan_unchanged_files.py +178 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_scanner.py +17 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_server.py +72 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_server_capabilities.py +3 -3
- agent_decoder-0.3.5/src/agent_decoder/static/assets/Dashboard-CTEAF_VF.js +0 -1
- agent_decoder-0.3.5/src/agent_decoder/static/assets/FileViewer-CgpI4NkB.js +0 -1
- agent_decoder-0.3.5/src/agent_decoder/static/assets/SearchOverlay-CHa-r4cd.js +0 -1
- agent_decoder-0.3.5/src/agent_decoder/static/assets/SessionItem-BimSOVG4.js +0 -1
- agent_decoder-0.3.5/src/agent_decoder/static/assets/SettingsModal-CuF_Vlos.js +0 -1
- agent_decoder-0.3.5/src/agent_decoder/static/assets/ShortcutsPopup-CMUseb-s.js +0 -1
- agent_decoder-0.3.5/src/agent_decoder/static/assets/Sidebar-BwH_O4R9.js +0 -1
- agent_decoder-0.3.5/src/agent_decoder/static/assets/client-C24egkBM.js +0 -1
- agent_decoder-0.3.5/src/agent_decoder/static/assets/formatRelative-j86webkr.js +0 -1
- agent_decoder-0.3.5/src/agent_decoder/static/assets/hljs-B3O74O7Z.js +0 -35
- agent_decoder-0.3.5/src/agent_decoder/static/assets/index-CVN26yaN.js +0 -5
- agent_decoder-0.3.5/src/agent_decoder/static/assets/renderSnippet-CqpQQugH.js +0 -1
- agent_decoder-0.3.5/src/agent_decoder/static/assets/ui-BcGPjpYn.js +0 -1
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/.editorconfig +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/.github/CODEOWNERS +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/.github/dependabot.yml +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/.github/workflows/ci.yml +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/.github/workflows/publish.yml +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/.pre-commit-config.yaml +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/CODE_OF_CONDUCT.md +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/CONTRIBUTING.md +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/LICENSE +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/Makefile +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/README.md +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/SECURITY.md +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/agent_decoder/__init__.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/docs/ad-poster.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/docs/demo-poster.jpg +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/index.html +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/package-lock.json +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/package.json +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/playwright.config.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/public/android-chrome-192x192.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/public/android-chrome-512x512.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/public/app-icon.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/public/apple-touch-icon.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/public/favicon-16x16.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/public/favicon-32x32.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/public/favicon.ico +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/public/site.webmanifest +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/api/__tests__/client.test.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/api/sessionCache.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/assets/fonts/LICENSES.md +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/assets/fonts/fira-code.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/assets/fonts/ioskeley-mono.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/assets/fonts/jetbrains-mono.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/assets/fonts/julia-mono.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/assets/fonts/libertinus-mono.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/assets/fonts/monaspace-argon.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/assets/fonts/source-code-pro.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/assets/fonts/spline-sans-mono.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/assets/fonts/xanh-mono.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/ChatInput.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/ChatInputBanners.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/ChatInputBar.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/ChatView.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/EmptyState.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/MessageListContainerContext.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/MessageListLoader.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/ModelPicker.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/NativeActionBanner.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/NativeTerminalView.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/OwnershipBanner.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/ProviderThemeContext.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/__tests__/ChatInput.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/__tests__/ChatViewLifecycle.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/__tests__/ConversationHeader.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/__tests__/EmptyState.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/__tests__/NativeActionBanner.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/chatCard.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/hooks/__tests__/useRecaps.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/hooks/useExpandPreserveAnchor.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/hooks/useMessageHistory.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/hooks/useNativePty.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/hooks/useRecaps.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/hooks/useSessionOwnership.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/ActivityStrip.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/Dashboard.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/DashboardEmpty.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/DashboardSearch.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/FeaturedSession.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/HeadlineInsight.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/Hero.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/ModelRates.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/ModelUsageStrip.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/ProjectStrip.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/SectionHeader.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/Sparkline.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/StatStrip.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/TipCard.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/TouchedFilesList.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Dashboard/fmt.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ErrorBoundary.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/FileViewer/FileViewer.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/FileViewer/index.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/AssistantMessage.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/ConfirmDialog.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/EphemeralMessage.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/MessageList.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/SidechainBranch.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/StreamingIndicator.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/ThinkingBlock.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/ThoughtChain.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/ToolUseBlock.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/UserMessage.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/__tests__/Ephemeral.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/__tests__/TextBlock.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/__tests__/ToolUseBlock.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/__tests__/UserMessage.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/blockRenderers.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/groupMessages.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/hooks/__tests__/useScrollController.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/hooks/__tests__/useSessionDetail.test.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/hooks/useCycle.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/hooks/useScrollController.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/hooks/useSessionDetail.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Messages/pairToolBlocks.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ScrollContainer.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Settings/NativePtyFontPicker.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Settings/PathEditor.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Settings/PermissionModeSection.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Settings/ProfileSection.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Settings/RecapSettings.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Settings/RescanButton.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Settings/SettingsModal.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Settings/SortOrderSection.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Settings/__tests__/SettingsModal.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Settings/shared.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/FileExplorer.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/HelpPopup.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/NewTaskButton.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/ProjectGroup.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/SearchOverlay.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/SessionItem.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/SidebarHeader.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/__tests__/NewTaskButton.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/__tests__/SearchOverlay.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/__tests__/SessionItem.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/__tests__/SessionItemRename.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/hooks/__tests__/useRunnerStatuses.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Sidebar/hooks/useRunnerStatuses.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/Toast.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ui/ContextMenu.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ui/DelayedSkeleton.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ui/LoadingAnimation.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ui/RenameInput.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/config/ui.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/constants/nativePtyFonts.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/main.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/store/__tests__/toggleExplorer.test.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/store/blocksState.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/styles/nativePtyFonts.css +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/styles/theme.css +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/test-setup.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/__tests__/lazyWithRetry.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/__tests__/sessionMeta.test.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/events.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/fileType.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/formatRelative.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/hljs.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/lazyWithRetry.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/nativePtyFocus.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/renderSnippet.tsx +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/sessionMeta.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/timestamps.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/utils/useDebounce.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/tests/ghostty-web.pw.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/tsconfig.json +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/tsconfig.node.json +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/vite.config.ts +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/scripts/capture-move-bundle.sh +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/__main__.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/_auth_env.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/_pty_preexec.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/analytics/__init__.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/analytics/aggregator.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/analytics/cost.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/analytics/extractor.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/analytics/fast.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/analytics/models.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/analytics/pricing.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/analytics/prompt.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/analytics/service.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/analytics/stats.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/analytics/tips.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/btw_capture.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/cli.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/config.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/drivers/__init__.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/editor.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/events_bus.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/locks.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/migrate.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/models.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/parser.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/providers/__init__.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/providers/base.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/providers/claude.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/providers/codex.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/providers/registry.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/pty_native.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/pty_screen_state.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/query_parser.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/recap_runner.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/reporter.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/android-chrome-192x192.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/android-chrome-512x512.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/app-icon.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/apple-touch-icon.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/assets/ChatView-BrP-ENHg.css +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/assets/fira-code-DGosTW8U.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/assets/index-Bo2bFwpv.css +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/assets/ioskeley-mono-C7VR2-sL.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/assets/jetbrains-mono-V6pRDFza.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/assets/julia-mono-Cbvba6sW.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/assets/libertinus-mono-Coz17E-O.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/assets/monaspace-argon-RS-2TGNg.woff2 +0 -0
- /agent_decoder-0.3.5/src/agent_decoder/static/assets/nativePtyFonts-BaXKtUsn.js → /agent_decoder-0.3.6/src/agent_decoder/static/assets/nativePtyFonts-BxhRYj2G.js +0 -0
- /agent_decoder-0.3.5/src/agent_decoder/static/assets/rolldown-runtime-S-ySWqyJ.js → /agent_decoder-0.3.6/src/agent_decoder/static/assets/rolldown-runtime-QTnfLwEv.js +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/assets/source-code-pro-DEXWLoHG.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/assets/spline-sans-mono-mUpA6Mve.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/assets/xanh-mono-Dsg19vWS.woff2 +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/favicon-16x16.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/favicon-32x32.png +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/favicon.ico +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/static/site.webmanifest +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/update_check.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/src/agent_decoder/watcher.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/__init__.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/analytics/__init__.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/analytics/test_aggregator.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/analytics/test_cost.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/analytics/test_extractor.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/analytics/test_fast_parity.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/analytics/test_integration.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/analytics/test_pricing.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/analytics/test_prompt.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/analytics/test_service.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/analytics/test_stats.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/analytics/test_tips.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/conftest.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/drivers/__init__.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/drivers/fake_cli.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/drivers/test_tmux_driver.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/fixtures/aaaaaaaa-0000-0000-0000-000000000001.jsonl +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/fixtures/bad-name.jsonl +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/fixtures/bbbbbbbb-0000-0000-0000-000000000002.jsonl +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/fixtures/btw_capture/live_zai_no_open_marker.bin +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/fixtures/btw_capture/multiline.bin +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/fixtures/codex/sample_rollout.jsonl +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/fixtures/fake_claude_tui.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/fixtures/session_with_usage.jsonl +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/fixtures/sidechain_session.jsonl +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/fixtures/simple_session.jsonl +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/providers/__init__.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/providers/test_claude_adapter.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/providers/test_codex_adapter.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/providers/test_registry.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_active_data_sources.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_analytics_ignores_ephemerals.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_auth_env.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_btw_capture.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_cli.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_codex_e2e.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_config_legacy_migration.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_db.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_db_session_meta.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_delete_sessions.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_dev_import_path.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_e2e.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_editor.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_ephemeral_messages.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_events_bus.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_export_ephemeral.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_locks.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_migrate.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_parser.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_pty_native.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_pty_runner.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_pty_runner_btw.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_pty_screen_state.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_query_parser.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_recaps.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_reporter.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_search_ephemeral.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_server_open_terminal.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_server_session_title.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/tests/test_update_check.py +0 -0
- {agent_decoder-0.3.5 → agent_decoder-0.3.6}/uv.lock +0 -0
|
@@ -52,5 +52,9 @@ demo-reel/
|
|
|
52
52
|
# Keep the built static assets — they ship with the package
|
|
53
53
|
!src/agent_decoder/static/
|
|
54
54
|
|
|
55
|
-
# Local scratch/handoff docs — never published
|
|
55
|
+
# Local scratch/handoff docs and agent config — never published
|
|
56
56
|
docs/archive/
|
|
57
|
+
/.handoff/
|
|
58
|
+
/.internal-archive/
|
|
59
|
+
/.claude/
|
|
60
|
+
/CLAUDE.md
|
|
@@ -164,3 +164,55 @@ See [`SECURITY.md`](SECURITY.md) for the disclosure process and full scope.
|
|
|
164
164
|
pre-warmed in the background after first paint.
|
|
165
165
|
- **GZip middleware.** FastAPI compresses JSON responses over a few hundred
|
|
166
166
|
bytes, which keeps session-detail payloads small on large conversations.
|
|
167
|
+
|
|
168
|
+
### Shared interactive execution
|
|
169
|
+
|
|
170
|
+
`ExecutionManager` is the single server owner of Claude and Codex terminals.
|
|
171
|
+
It shares the session lock table, active Native terminal, sizing configuration,
|
|
172
|
+
and shutdown lifecycle. `pty_runner.PtyManager` supplies Claude's established
|
|
173
|
+
input policy (ownership, input acknowledgments/watchdog, `/model`, permissions,
|
|
174
|
+
and ephemeral `/btw` capture); `driver_manager.DriverSessionPolicy` supplies
|
|
175
|
+
Codex's launch/resume, state polling, and rollout adoption. Neither policy is
|
|
176
|
+
instantiated as another manager. The former `DriverManager` import resolves to
|
|
177
|
+
`ExecutionManager` for compatibility. Native input, resize, snapshot, status,
|
|
178
|
+
and termination routes all use the same owner. Provider capability checks still
|
|
179
|
+
run before focus/submit; read-only providers cannot acquire a terminal.
|
|
180
|
+
|
|
181
|
+
Both providers use the isolated `agent-decoder` tmux socket by default.
|
|
182
|
+
`AGENT_DECODER_TMUX_SOCKET` overrides it for isolated installations/tests.
|
|
183
|
+
Claude falls back to direct PTY when tmux is absent; internal consumers can
|
|
184
|
+
explicitly request `terminal_backend="direct"`. Codex requires tmux and retains
|
|
185
|
+
its runtime capability gate. Claude launch environments use a private temporary
|
|
186
|
+
file consumed and unlinked before exec; secrets do not appear in launch argv or
|
|
187
|
+
tmux metadata.
|
|
188
|
+
|
|
189
|
+
Normal server shutdown **detaches** attach clients, stops local watchdogs and
|
|
190
|
+
capture readers, releases server-owned Claude sidecars, and leaves tmux harnesses
|
|
191
|
+
running. Browser disconnect/idle does not reap tmux harnesses. Explicit kill
|
|
192
|
+
**terminates** the harness; kill also resolves a persistent terminal after a
|
|
193
|
+
server restart without spawning or first focusing it. Direct PTY cannot detach
|
|
194
|
+
and therefore terminates on shutdown. Claude reattachment checks session/cwd
|
|
195
|
+
metadata against the pane PID plus the OS process start identity and executable.
|
|
196
|
+
A changed process or a fresh foreign sidecar prevents adoption. Binary,
|
|
197
|
+
permission, or model configuration changes require a verified owned respawn;
|
|
198
|
+
live `/model` updates the persisted launch configuration. These checks coordinate
|
|
199
|
+
same-user processes, not mutually untrusted users sharing a tmux server.
|
|
200
|
+
|
|
201
|
+
Tmux redraws terminal output, which is suitable for Native hydration but changes
|
|
202
|
+
the escape sequences Claude's `/btw` parser needs. Claude therefore also uses
|
|
203
|
+
`pipe-pane` into a private Unix datagram receiver for the original byte stream.
|
|
204
|
+
The relay drains without waiting for the application; datagrams and receiver
|
|
205
|
+
work per loop turn are bounded. Sequence gaps or capture-buffer overflow fail
|
|
206
|
+
the pending ephemeral response instead of persisting partial text. A capture
|
|
207
|
+
owner token prevents an old client from stopping a replacement relay. Detach
|
|
208
|
+
removes the local socket and stops its relay; after a crash an orphaned relay
|
|
209
|
+
keeps draining/dropping until reattachment replaces it.
|
|
210
|
+
|
|
211
|
+
Limitations: pending ephemeral `/btw` output is not recovered across a server
|
|
212
|
+
crash/restart, and output during detach is not retained as a raw byte history;
|
|
213
|
+
reattachment hydrates the tmux screen while JSONL remains the content source.
|
|
214
|
+
A crashed process may leave a private temporary socket directory until OS temp
|
|
215
|
+
cleanup. Deliberately replacing/execing a managed pane into a different executable
|
|
216
|
+
requires explicit recovery rather than automatic ownership adoption. Real
|
|
217
|
+
provider auth/network smoke tests remain opt-in; behavioral tests use synthetic
|
|
218
|
+
CLIs and unique tmux sockets.
|
|
@@ -6,6 +6,25 @@ The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.3.6] - 2026-11-06
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Durable agent runs and a cross-provider Runs screen** (#52). Each managed execution attempt is recorded in an `agent_runs` table that is independent of the rebuildable session index; no screen or exit code is treated as proof of completion. Read it at `GET /api/runs` and `GET /api/runs/{id}`.
|
|
14
|
+
- **Unified Claude and Codex execution ownership** with persistent tmux terminals (#51), so both providers share one execution manager.
|
|
15
|
+
- **Artifacts panel** listing a session's images and written files.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Share one syntax highlighter and cache rendered markdown, and toggle the artifacts panel without re-rendering the chat.
|
|
20
|
+
- Local agent and handoff files (`.handoff/`, `.claude/`, `CLAUDE.md`, ...) are excluded from sdist builds and gitignored.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **Periodic rescan no longer re-parses every Codex transcript each minute.** Codex rollouts are named `rollout-<timestamp>-<uuid>` but indexed under the session id inside the file, so the "has this file changed?" lookup by file name missed most of them and the server re-parsed gigabytes of unchanged transcripts on every pass (a ~14 s CPU spike every 60 s, plus `message_count` reset to 0 on large rollouts). Unchanged files are now skipped by path, including rollouts that share a session id.
|
|
25
|
+
- Dangling symlinks under `~/.claude/projects` are skipped quietly instead of logging a "No such file" warning on every rescan.
|
|
26
|
+
- Native terminal no longer snaps back to the bottom and wipes scrollback when a full-screen clear (`ESC[3J`) arrives while you are scrolled up. A real `/clear` at the bottom still clears. An ignored sequence is logged to the browser console (`agent-decoder: ignored ESC[3J …`) so the cause can be confirmed in real use.
|
|
27
|
+
|
|
9
28
|
## [0.3.5] - 2026-10-24
|
|
10
29
|
|
|
11
30
|
### Changed
|
|
@@ -253,7 +272,8 @@ Initial public release.
|
|
|
253
272
|
|
|
254
273
|
- License changed from MIT to [FSL-1.1-Apache-2.0](LICENSE). All prior unreleased history was developed under MIT; the 0.1.0 release and everything after it ship under FSL-1.1-Apache-2.0, which converts to Apache 2.0 two years after each release.
|
|
255
274
|
|
|
256
|
-
[Unreleased]: https://github.com/Comradery64/Agent-Decoder/compare/v0.3.
|
|
275
|
+
[Unreleased]: https://github.com/Comradery64/Agent-Decoder/compare/v0.3.6...HEAD
|
|
276
|
+
[0.3.6]: https://github.com/Comradery64/Agent-Decoder/compare/v0.3.5...v0.3.6
|
|
257
277
|
[0.3.5]: https://github.com/Comradery64/Agent-Decoder/compare/v0.3.2...v0.3.5
|
|
258
278
|
[0.3.2]: https://github.com/Comradery64/Agent-Decoder/compare/v0.3.1.3...v0.3.2
|
|
259
279
|
[0.3.1.2]: https://github.com/Comradery64/Agent-Decoder/compare/v0.3.1.1...v0.3.1.2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agent-decoder
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: A local web UI for browsing and analyzing AI coding assistant chat history
|
|
5
5
|
Project-URL: Homepage, https://github.com/Comradery64/Agent-Decoder
|
|
6
6
|
Project-URL: Repository, https://github.com/Comradery64/Agent-Decoder
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Managed runs
|
|
2
|
+
|
|
3
|
+
Open **Runs** in the sidebar (`#/runs`) to inspect managed Claude and Codex execution attempts. Search across session ID, directory, project, model and branch; filter by provider, state or exact project ID. Results use pages of 50. A run detail URL loads independently of the current page. Source links open the existing chat with its Decoded / Native controls; missing indexed chats retain their run history and show an unavailable-source explanation.
|
|
4
|
+
|
|
5
|
+
A run represents **one managed process attempt**, spanning multiple prompts. It is not a task or an individual conversation turn. Historical indexed sessions do not become runs until managed execution is requested. Native rollout adoption changes the session ID while keeping the attempt ID. Reattaching to a verified surviving tmux process also keeps the attempt ID. A replaced or confirmed-absent process closes the old attempt with an unknown outcome (`blocked`, with `ended_at`) and creates a fresh attempt. Terminal records cannot be updated by later observations.
|
|
6
|
+
|
|
7
|
+
States are `queued`, `starting`, `running`, `awaiting_input`, `blocked`, `completed`, `failed`, and `canceled`. Launch begins at `starting`; an attached transport remains `blocked` pending screen evidence. Successful decoded submission or native Enter records `running` with explicit input provenance. Native Enter does not prove what command was entered. Provider screen observations may indicate `awaiting_input`, `running`, or `blocked`, but are advisory: Claude's classifier defaults to idle, and native prompts are not reliable completion signals. Last-observed timestamps reflect real attach, input or screen observations, not a fabricated heartbeat. Old observations can be stale even if a transport remains attached.
|
|
8
|
+
|
|
9
|
+
Explicit termination records `canceled` after the termination operation returns. Confirmed launch failure records `failed`. A capture/attach failure with a verified surviving pane leaves an active, recoverable `blocked` attempt. Disconnect or server restart marks an attempt `blocked` with an unobserved/detached explanation. Shutdown detaches tmux attempts; direct PTY shutdown closes an unknown-outcome attempt because its process is terminated. No prompt, idle screen, process exit (including exit 0), or disconnect records successful completion. There is deliberately no “finish” operation: this slice does not infer or automate task outcomes. `completed` and `queued` are contract foundations for later explicit outcome/queue features.
|
|
10
|
+
|
|
11
|
+
The `agent_runs` SQLite table is independent of the rebuildable session index and has no cascading foreign key. Source indexing fills missing metadata while preserving recorded execution model/context. `task_id` and `parent_run_id` are explicitly nullable foundations; no task management, queue launch, or approval automation is implemented.
|
|
12
|
+
|
|
13
|
+
`GET /api/runs` returns typed `AgentRun[]`; optional `provider`, `state`, `project`, and `q` filters apply before `limit` (1–500, default 200) and `offset`. `GET /api/runs/{run_id}` returns one record or 404. Both endpoints only inspect durable records and indexed metadata; they never start providers or claim execution locks.
|
|
14
|
+
|
|
15
|
+
The execution manager writes lifecycle records before publishing `runs_changed` SSE notifications. Screen observations enter a manager-owned buffer directly from provider policy, rather than through a lossy SSE subscriber; the latest advisory observation is persisted every two seconds. The UI refreshes on confirmed-write notifications/reconnect and every 15 seconds to recover missed notifications. This is current-state history, not a complete event audit log. A partial unique index and immediate SQLite transaction prevent duplicate active attempts for a provider/session across independent store instances. Execution lifecycle operations and observation writes are serialized by the shared manager.
|
|
@@ -34,12 +34,14 @@ const settingsModalImport = () => import("./components/Settings/SettingsModal");
|
|
|
34
34
|
// render a reload prompt in-place instead of crashing the whole ErrorBoundary.
|
|
35
35
|
const Sidebar = lazyWithRetry(() => import("./components/Sidebar/Sidebar"));
|
|
36
36
|
const ChatView = lazyWithRetry(chatViewImport);
|
|
37
|
+
const Runs = lazyWithRetry(() => import("./components/Runs"));
|
|
37
38
|
const Dashboard = lazyWithRetry(() => import("./components/Dashboard/Dashboard"));
|
|
38
39
|
const SettingsModal = lazyWithRetry(settingsModalImport);
|
|
39
40
|
const SearchOverlay = lazyWithRetry(searchOverlayImport);
|
|
40
41
|
const HelpPopup = lazyWithRetry(() => import("./components/Sidebar/HelpPopup"));
|
|
41
42
|
const ShortcutsPopup = lazyWithRetry(() => import("./components/Sidebar/ShortcutsPopup"));
|
|
42
43
|
const FileViewer = lazyWithRetry(() => import("./components/FileViewer/FileViewer"));
|
|
44
|
+
const ArtifactsPanel = lazyWithRetry(() => import("./components/ArtifactsPanel/ArtifactsPanel"));
|
|
43
45
|
|
|
44
46
|
export default function App() {
|
|
45
47
|
// Five individual selectors instead of `useAppStore()` without a selector —
|
|
@@ -275,6 +277,9 @@ export default function App() {
|
|
|
275
277
|
} else if (e.key.toLowerCase() === "e" && e.metaKey) {
|
|
276
278
|
e.preventDefault();
|
|
277
279
|
useAppStore.getState().toggleResultsExpanded();
|
|
280
|
+
} else if (e.key.toLowerCase() === "a" && e.metaKey && e.shiftKey) {
|
|
281
|
+
e.preventDefault();
|
|
282
|
+
useAppStore.getState().toggleArtifacts();
|
|
278
283
|
} else if (e.key.toLowerCase() === "b" && e.metaKey) {
|
|
279
284
|
e.preventDefault();
|
|
280
285
|
useAppStore.getState().toggleSidebar();
|
|
@@ -328,9 +333,16 @@ export default function App() {
|
|
|
328
333
|
<Sidebar />
|
|
329
334
|
<div style={{ flex: 1, display: "flex", minWidth: 200, overflow: "hidden" }}>
|
|
330
335
|
<ErrorBoundary>
|
|
331
|
-
{chatIdFromUrl ? <ChatView /> : <Dashboard />}
|
|
336
|
+
{route.startsWith("/runs") ? <Runs /> : chatIdFromUrl ? <ChatView /> : <Dashboard />}
|
|
332
337
|
</ErrorBoundary>
|
|
333
338
|
</div>
|
|
339
|
+
{chatIdFromUrl && (
|
|
340
|
+
<ErrorBoundary>
|
|
341
|
+
<React.Suspense fallback={null}>
|
|
342
|
+
<ArtifactsPanel />
|
|
343
|
+
</React.Suspense>
|
|
344
|
+
</ErrorBoundary>
|
|
345
|
+
)}
|
|
334
346
|
{viewingFilePath && (
|
|
335
347
|
<ErrorBoundary>
|
|
336
348
|
<React.Suspense fallback={<DelayedSkeleton />}>
|
|
@@ -137,6 +137,8 @@ function patch<T>(path: string, body: unknown): Promise<T> {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
export const api = {
|
|
140
|
+
getRuns: (params = "") => get<import("./types").AgentRun[]>(`/api/runs${params}`),
|
|
141
|
+
getRun: (id: string) => get<import("./types").AgentRun>(`/api/runs/${encodeURIComponent(id)}`),
|
|
140
142
|
getProjects: () => get<Project[]>("/api/projects"),
|
|
141
143
|
getProjectSessions: (projectId: string) =>
|
|
142
144
|
get<Session[]>(`/api/projects/${encodeURIComponent(projectId)}/sessions`),
|
|
@@ -591,3 +591,25 @@ export interface DashboardData {
|
|
|
591
591
|
total_messages: number;
|
|
592
592
|
tips: DashboardTip[];
|
|
593
593
|
}
|
|
594
|
+
|
|
595
|
+
/** A managed execution attempt; independent of indexed session lifetime. */
|
|
596
|
+
export interface AgentRun {
|
|
597
|
+
id: string;
|
|
598
|
+
provider: string;
|
|
599
|
+
native_session_id: string;
|
|
600
|
+
model: string | null;
|
|
601
|
+
project_id: string | null;
|
|
602
|
+
cwd: string | null;
|
|
603
|
+
git_branch: string | null;
|
|
604
|
+
is_worktree: boolean;
|
|
605
|
+
task_id: string | null;
|
|
606
|
+
parent_run_id: string | null;
|
|
607
|
+
state: "queued" | "starting" | "running" | "awaiting_input" | "blocked" | "completed" | "failed" | "canceled";
|
|
608
|
+
reason: string;
|
|
609
|
+
provenance: string;
|
|
610
|
+
created_at: string;
|
|
611
|
+
updated_at: string;
|
|
612
|
+
last_observed_at: string | null;
|
|
613
|
+
ended_at: string | null;
|
|
614
|
+
source_available: boolean;
|
|
615
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
import { useAppStore } from "../../store";
|
|
3
|
+
import { useSessionDetail } from "../Messages/hooks/useSessionDetail";
|
|
4
|
+
import { ScrollContainer } from "../ScrollContainer";
|
|
5
|
+
import { type Artifact, type ArtifactGroup, extractArtifacts } from "../../utils/artifacts";
|
|
6
|
+
import { blobUrl } from "../../utils/fileType";
|
|
7
|
+
|
|
8
|
+
const WIDTH = 320;
|
|
9
|
+
|
|
10
|
+
const FILTERS: Array<{ id: ArtifactGroup | "all"; label: string }> = [
|
|
11
|
+
{ id: "all", label: "All" },
|
|
12
|
+
{ id: "images", label: "Images" },
|
|
13
|
+
{ id: "docs", label: "Docs" },
|
|
14
|
+
{ id: "media", label: "Media" },
|
|
15
|
+
{ id: "code", label: "Code" },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
function basename(path: string): string {
|
|
19
|
+
return path.slice(path.lastIndexOf("/") + 1) || path;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function IconFile() {
|
|
23
|
+
return (
|
|
24
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
25
|
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
|
26
|
+
<polyline points="14 2 14 8 20 8" />
|
|
27
|
+
</svg>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ArtifactCard({ artifact }: { artifact: Artifact }) {
|
|
32
|
+
const setViewingFilePath = useAppStore((s) => s.setViewingFilePath);
|
|
33
|
+
const setPendingScrollMessageId = useAppStore((s) => s.setPendingScrollMessageId);
|
|
34
|
+
const jump = () => setPendingScrollMessageId(artifact.messageId);
|
|
35
|
+
// File artifacts open in the file viewer; message images jump to their turn.
|
|
36
|
+
const open = artifact.kind === "file" ? () => setViewingFilePath(artifact.path) : jump;
|
|
37
|
+
const thumb =
|
|
38
|
+
artifact.kind === "image" ? artifact.src
|
|
39
|
+
: artifact.category === "image" ? blobUrl(artifact.path)
|
|
40
|
+
: null;
|
|
41
|
+
const label = artifact.kind === "file" ? basename(artifact.path) : "Image";
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div
|
|
45
|
+
style={{
|
|
46
|
+
border: "1px solid var(--border-subtle)",
|
|
47
|
+
borderRadius: "var(--radius-md)",
|
|
48
|
+
overflow: "hidden",
|
|
49
|
+
background: "var(--bg-tool-block)",
|
|
50
|
+
}}
|
|
51
|
+
>
|
|
52
|
+
<button
|
|
53
|
+
type="button"
|
|
54
|
+
onClick={open}
|
|
55
|
+
title={artifact.kind === "file" ? artifact.path : "Jump to message"}
|
|
56
|
+
style={{
|
|
57
|
+
display: "flex",
|
|
58
|
+
alignItems: "center",
|
|
59
|
+
gap: "8px",
|
|
60
|
+
width: "100%",
|
|
61
|
+
padding: thumb ? 0 : "10px 12px",
|
|
62
|
+
background: "none",
|
|
63
|
+
border: "none",
|
|
64
|
+
cursor: "pointer",
|
|
65
|
+
color: "var(--text-secondary)",
|
|
66
|
+
textAlign: "left",
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
{thumb ? (
|
|
70
|
+
<img
|
|
71
|
+
src={thumb}
|
|
72
|
+
alt={label}
|
|
73
|
+
loading="lazy"
|
|
74
|
+
style={{ display: "block", width: "100%", maxHeight: 360, objectFit: "contain", background: "var(--bg-base)" }}
|
|
75
|
+
/>
|
|
76
|
+
) : (
|
|
77
|
+
<>
|
|
78
|
+
<IconFile />
|
|
79
|
+
<span style={{ fontFamily: "var(--font-mono)", fontSize: "12px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
|
80
|
+
{label}
|
|
81
|
+
</span>
|
|
82
|
+
</>
|
|
83
|
+
)}
|
|
84
|
+
</button>
|
|
85
|
+
<div
|
|
86
|
+
style={{
|
|
87
|
+
display: "flex",
|
|
88
|
+
alignItems: "center",
|
|
89
|
+
justifyContent: "space-between",
|
|
90
|
+
gap: "8px",
|
|
91
|
+
padding: "4px 8px 4px 12px",
|
|
92
|
+
borderTop: "1px solid var(--border-subtle)",
|
|
93
|
+
fontSize: "11px",
|
|
94
|
+
color: "var(--text-tertiary)",
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
|
|
98
|
+
{thumb ? label : artifact.group}
|
|
99
|
+
</span>
|
|
100
|
+
<button
|
|
101
|
+
type="button"
|
|
102
|
+
onClick={jump}
|
|
103
|
+
title="Jump to message"
|
|
104
|
+
style={{ background: "none", border: "none", cursor: "pointer", color: "inherit", fontSize: "11px", padding: "2px 4px", flexShrink: 0 }}
|
|
105
|
+
>
|
|
106
|
+
Show in chat ↗
|
|
107
|
+
</button>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export default function ArtifactsPanel() {
|
|
114
|
+
// Open state is read here, not in App, so toggling re-renders only this
|
|
115
|
+
// panel instead of the whole chat.
|
|
116
|
+
const open = useAppStore((s) => s.artifactsOpen);
|
|
117
|
+
const sessionId = useAppStore((s) => s.selectedSessionId);
|
|
118
|
+
// Re-key per session so the filter resets when switching chats.
|
|
119
|
+
return open && sessionId ? <ArtifactsList key={sessionId} sessionId={sessionId} /> : null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function ArtifactsList({ sessionId }: { sessionId: string }) {
|
|
123
|
+
const { detail } = useSessionDetail(sessionId);
|
|
124
|
+
const toggleArtifacts = useAppStore((s) => s.toggleArtifacts);
|
|
125
|
+
const [filter, setFilter] = useState<ArtifactGroup | "all">("all");
|
|
126
|
+
const artifacts = useMemo(() => extractArtifacts(detail?.messages ?? []), [detail?.messages]);
|
|
127
|
+
const present = new Set(artifacts.map((a) => a.group));
|
|
128
|
+
const shown = filter === "all" ? artifacts : artifacts.filter((a) => a.group === filter);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<aside
|
|
132
|
+
aria-label="Artifacts"
|
|
133
|
+
style={{
|
|
134
|
+
width: WIDTH,
|
|
135
|
+
flexShrink: 0,
|
|
136
|
+
display: "flex",
|
|
137
|
+
flexDirection: "column",
|
|
138
|
+
borderLeft: "1px solid var(--border-subtle)",
|
|
139
|
+
background: "var(--bg-base)",
|
|
140
|
+
minHeight: 0,
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
143
|
+
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 12px 8px 16px" }}>
|
|
144
|
+
<span style={{ fontSize: "13px", fontWeight: 600, color: "var(--text-primary)" }}>
|
|
145
|
+
Artifacts <span style={{ color: "var(--text-tertiary)", fontWeight: 400 }}>{artifacts.length}</span>
|
|
146
|
+
</span>
|
|
147
|
+
<button
|
|
148
|
+
type="button"
|
|
149
|
+
onClick={toggleArtifacts}
|
|
150
|
+
aria-label="Close artifacts"
|
|
151
|
+
title="Close artifacts"
|
|
152
|
+
style={{ background: "none", border: "none", cursor: "pointer", color: "var(--text-tertiary)", fontSize: "16px", padding: "0 4px" }}
|
|
153
|
+
>
|
|
154
|
+
×
|
|
155
|
+
</button>
|
|
156
|
+
</div>
|
|
157
|
+
<div style={{ display: "flex", flexWrap: "wrap", gap: "4px", padding: "0 12px 8px 16px" }}>
|
|
158
|
+
{FILTERS.filter((f) => f.id === "all" || present.has(f.id)).map((f) => (
|
|
159
|
+
<button
|
|
160
|
+
key={f.id}
|
|
161
|
+
type="button"
|
|
162
|
+
onClick={() => setFilter(f.id)}
|
|
163
|
+
aria-pressed={filter === f.id}
|
|
164
|
+
style={{
|
|
165
|
+
fontSize: "11px",
|
|
166
|
+
padding: "3px 8px",
|
|
167
|
+
borderRadius: "999px",
|
|
168
|
+
border: "1px solid var(--border-subtle)",
|
|
169
|
+
cursor: "pointer",
|
|
170
|
+
background: filter === f.id ? "var(--bg-sidebar-hover)" : "none",
|
|
171
|
+
color: filter === f.id ? "var(--text-primary)" : "var(--text-tertiary)",
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
{f.label}
|
|
175
|
+
</button>
|
|
176
|
+
))}
|
|
177
|
+
</div>
|
|
178
|
+
<ScrollContainer style={{ flex: 1, minHeight: 0 }}>
|
|
179
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "10px", padding: "4px 12px 16px 16px" }}>
|
|
180
|
+
{shown.length === 0 ? (
|
|
181
|
+
<div style={{ fontSize: "12px", color: "var(--text-tertiary)", padding: "24px 0", textAlign: "center" }}>
|
|
182
|
+
{detail ? "No artifacts in this session yet." : "Loading…"}
|
|
183
|
+
</div>
|
|
184
|
+
) : (
|
|
185
|
+
shown.map((a) => <ArtifactCard key={a.key} artifact={a} />)
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
188
|
+
</ScrollContainer>
|
|
189
|
+
</aside>
|
|
190
|
+
);
|
|
191
|
+
}
|
{agent_decoder-0.3.5 → agent_decoder-0.3.6}/frontend/src/components/ChatView/ConversationHeader.tsx
RENAMED
|
@@ -3,7 +3,7 @@ import type { PtyOwnership, Session } from "../../api/types";
|
|
|
3
3
|
import { api } from "../../api/client";
|
|
4
4
|
import { useAppStore } from "../../store";
|
|
5
5
|
import { emit } from "../../utils/events";
|
|
6
|
-
import { IconTerminal } from "../ui/icons";
|
|
6
|
+
import { IconGallery, IconTerminal } from "../ui/icons";
|
|
7
7
|
|
|
8
8
|
// Small status dot for the Phase-0 ownership badge. Inline-rendered
|
|
9
9
|
// next to the model label so it doesn't disturb existing header layout.
|
|
@@ -98,6 +98,8 @@ export function ConversationHeader({
|
|
|
98
98
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
99
99
|
const remoteClient = useAppStore((s) => s.hostInfo?.is_remote_client === true);
|
|
100
100
|
const terminalDisabled = !session || session.is_fork || remoteClient;
|
|
101
|
+
const artifactsOpen = useAppStore((s) => s.artifactsOpen);
|
|
102
|
+
const toggleArtifacts = useAppStore((s) => s.toggleArtifacts);
|
|
101
103
|
const terminalTitle = session?.is_fork
|
|
102
104
|
? "Open in terminal (fork — not resumable)"
|
|
103
105
|
: remoteClient
|
|
@@ -286,6 +288,24 @@ export function ConversationHeader({
|
|
|
286
288
|
</span>
|
|
287
289
|
)}
|
|
288
290
|
|
|
291
|
+
<button
|
|
292
|
+
onClick={toggleArtifacts}
|
|
293
|
+
title="Artifacts (⇧⌘A)"
|
|
294
|
+
aria-label="Toggle artifacts panel"
|
|
295
|
+
aria-pressed={artifactsOpen}
|
|
296
|
+
style={{ ...btnBase, color: artifactsOpen ? "var(--text-primary)" : "var(--text-tertiary)" }}
|
|
297
|
+
onMouseEnter={(e) => {
|
|
298
|
+
(e.currentTarget as HTMLButtonElement).style.color = "var(--text-primary)";
|
|
299
|
+
(e.currentTarget as HTMLButtonElement).style.background = "var(--bg-sidebar-hover)";
|
|
300
|
+
}}
|
|
301
|
+
onMouseLeave={(e) => {
|
|
302
|
+
(e.currentTarget as HTMLButtonElement).style.color = artifactsOpen ? "var(--text-primary)" : "var(--text-tertiary)";
|
|
303
|
+
(e.currentTarget as HTMLButtonElement).style.background = "none";
|
|
304
|
+
}}
|
|
305
|
+
>
|
|
306
|
+
<IconGallery />
|
|
307
|
+
</button>
|
|
308
|
+
|
|
289
309
|
{/* Export dropdown */}
|
|
290
310
|
{session && (
|
|
291
311
|
<div ref={dropdownRef} style={{ position: "relative" }}>
|
|
@@ -74,6 +74,8 @@ const { fitDimensions, configSource, fitAddonInstances, terminalInstances, MockF
|
|
|
74
74
|
// xterm exposes a unicode service; xtermTerminal.ts sets activeVersion and
|
|
75
75
|
// the Unicode11 addon registers a provider against it.
|
|
76
76
|
unicode = { activeVersion: "6", register: vi.fn() };
|
|
77
|
+
// createNativeTerminal registers a CSI handler (ESC[3J guard) on the parser.
|
|
78
|
+
parser = { registerCsiHandler: vi.fn() };
|
|
77
79
|
|
|
78
80
|
constructor(options: Record<string, unknown>) {
|
|
79
81
|
this.options = options;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
import { createNativeTerminal, type NativeTerminalOptions } from "../xtermTerminal";
|
|
4
|
+
|
|
5
|
+
const w = (t: ReturnType<typeof createNativeTerminal>, d: string) =>
|
|
6
|
+
new Promise<void>((r) => t.write(d, () => r()));
|
|
7
|
+
|
|
8
|
+
async function scrolledTerminal() {
|
|
9
|
+
const t = createNativeTerminal({
|
|
10
|
+
cols: 120,
|
|
11
|
+
rows: 30,
|
|
12
|
+
scrollback: 5000,
|
|
13
|
+
allowProposedApi: true,
|
|
14
|
+
} as NativeTerminalOptions);
|
|
15
|
+
let s = "";
|
|
16
|
+
for (let i = 0; i < 400; i++) s += `line ${i}\r\n`;
|
|
17
|
+
await w(t, s);
|
|
18
|
+
return t;
|
|
19
|
+
}
|
|
20
|
+
const pos = (t: ReturnType<typeof createNativeTerminal>) => {
|
|
21
|
+
const b = t.buffer.active;
|
|
22
|
+
return { y: b.viewportY, base: b.baseY, scrollback: b.length - t.rows };
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Regression: xterm honors ESC[3J (erase scrollback) by wiping history AND
|
|
26
|
+
// resetting the viewport to the bottom. A TUI that emits a full clear on a tall
|
|
27
|
+
// repaint therefore yanks a reader who scrolled up back down "a few moments"
|
|
28
|
+
// later and destroys what they were reading.
|
|
29
|
+
describe("ESC[3J (erase scrollback) while the reader is scrolled up", () => {
|
|
30
|
+
it("is ignored: position and scrollback are preserved", async () => {
|
|
31
|
+
const t = await scrolledTerminal();
|
|
32
|
+
t.scrollLines(-100);
|
|
33
|
+
const before = pos(t);
|
|
34
|
+
await w(t, "\x1b[2J\x1b[3J\x1b[Hrepaint");
|
|
35
|
+
const after = pos(t);
|
|
36
|
+
expect(after.y).toBe(before.y);
|
|
37
|
+
expect(after.scrollback).toBeGreaterThanOrEqual(before.scrollback);
|
|
38
|
+
expect(after.y).toBeLessThan(after.base); // still not at the bottom
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("is ignored even when the sequence is split across two writes", async () => {
|
|
42
|
+
const t = await scrolledTerminal();
|
|
43
|
+
t.scrollLines(-100);
|
|
44
|
+
const before = pos(t);
|
|
45
|
+
await w(t, "\x1b[3");
|
|
46
|
+
await w(t, "Jrepaint");
|
|
47
|
+
expect(pos(t).y).toBe(before.y);
|
|
48
|
+
expect(pos(t).scrollback).toBeGreaterThanOrEqual(before.scrollback);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("still clears scrollback when the reader is at the bottom (faithful /clear)", async () => {
|
|
52
|
+
const t = await scrolledTerminal(); // at the bottom: never scrolled up
|
|
53
|
+
await w(t, "\x1b[2J\x1b[3J\x1b[Hfresh");
|
|
54
|
+
expect(pos(t).scrollback).toBe(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("does not affect other erase sequences", async () => {
|
|
58
|
+
const t = await scrolledTerminal();
|
|
59
|
+
t.scrollLines(-100);
|
|
60
|
+
const before = pos(t);
|
|
61
|
+
await w(t, "\x1b[2J\x1b[Hrepaint");
|
|
62
|
+
expect(pos(t).y).toBe(before.y);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -10,7 +10,23 @@ export type NativeTerminalOptions = ITerminalOptions;
|
|
|
10
10
|
export type { ITheme };
|
|
11
11
|
|
|
12
12
|
export function createNativeTerminal(options: NativeTerminalOptions): NativeTerminal {
|
|
13
|
-
|
|
13
|
+
const terminal = new Terminal(options);
|
|
14
|
+
// ESC[3J ("erase scrollback") makes xterm wipe the history AND reset the
|
|
15
|
+
// viewport to the bottom. A TUI that does a full clear on a tall repaint
|
|
16
|
+
// would otherwise yank a reader who scrolled up back down a few moments
|
|
17
|
+
// later and destroy what they were reading. Swallow it only while scrolled
|
|
18
|
+
// up; at the bottom it passes through, so a real /clear still clears. Using
|
|
19
|
+
// the parser hook (not a byte filter) means sequences split across PTY
|
|
20
|
+
// chunks are handled by xterm itself.
|
|
21
|
+
terminal.parser.registerCsiHandler({ final: "J" }, (params) => {
|
|
22
|
+
if (params[0] !== 3) return false;
|
|
23
|
+
const b = terminal.buffer.active;
|
|
24
|
+
const scrolledUp = b.viewportY < b.baseY;
|
|
25
|
+
// Diagnostic (no content): confirms in DevTools that the TUI really sends it.
|
|
26
|
+
if (scrolledUp) console.info("agent-decoder: ignored ESC[3J (erase scrollback) while scrolled up");
|
|
27
|
+
return scrolledUp; // true = handled (swallowed)
|
|
28
|
+
});
|
|
29
|
+
return terminal;
|
|
14
30
|
}
|
|
15
31
|
|
|
16
32
|
export function createNativeFitAddon(): NativeFitAddon {
|
|
@@ -226,19 +226,41 @@ interface TextBlockProps {
|
|
|
226
226
|
style?: CSSProperties;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
// One highlighter shared by every block. rehype-highlight builds a lowlight
|
|
230
|
+
// instance (registering ~37 languages) each time its attacher runs, and
|
|
231
|
+
// react-markdown runs attachers on every render — that alone cost ~0.5s per
|
|
232
|
+
// session switch on long chats.
|
|
233
|
+
const highlight = rehypeHighlight({ detect: true });
|
|
234
|
+
const remarkPlugins = [remarkGfm];
|
|
235
|
+
const rehypePlugins = [() => highlight];
|
|
236
|
+
|
|
237
|
+
// Rendered markdown keyed by source text, so re-renders and switching back to
|
|
238
|
+
// a session skip parsing + highlighting. ReactMarkdown (sync) is a pure
|
|
239
|
+
// function, so its output is safe to reuse. Map order gives LRU eviction.
|
|
240
|
+
const MAX_CACHED = 3000;
|
|
241
|
+
const renderedByText = new Map<string, ReactElement>();
|
|
242
|
+
|
|
243
|
+
function renderMarkdown(text: string): ReactElement {
|
|
244
|
+
let el = renderedByText.get(text);
|
|
245
|
+
if (el) {
|
|
246
|
+
renderedByText.delete(text);
|
|
247
|
+
} else {
|
|
248
|
+
el = ReactMarkdown({ remarkPlugins, rehypePlugins, components, children: text });
|
|
249
|
+
if (renderedByText.size >= MAX_CACHED) {
|
|
250
|
+
renderedByText.delete(renderedByText.keys().next().value!);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
renderedByText.set(text, el);
|
|
254
|
+
return el;
|
|
255
|
+
}
|
|
256
|
+
|
|
229
257
|
export function TextBlock({ text, className, style }: TextBlockProps) {
|
|
230
258
|
return (
|
|
231
259
|
<div
|
|
232
260
|
className={["prose-content", className].filter(Boolean).join(" ")}
|
|
233
261
|
style={style}
|
|
234
262
|
>
|
|
235
|
-
|
|
236
|
-
remarkPlugins={[remarkGfm]}
|
|
237
|
-
rehypePlugins={[[rehypeHighlight, { detect: true, ignoreMissing: true }]]}
|
|
238
|
-
components={components}
|
|
239
|
-
>
|
|
240
|
-
{text}
|
|
241
|
-
</ReactMarkdown>
|
|
263
|
+
{renderMarkdown(text)}
|
|
242
264
|
</div>
|
|
243
265
|
);
|
|
244
266
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.runs-page { padding: 28px; overflow: auto; flex: 1; min-width: 0; color: var(--text-primary); font-family: var(--font-ui); }
|
|
2
|
+
.runs-page header, .runs-filters, .runs-list, .runs-pagination, .run-detail { max-width: 1100px; margin-inline: auto; }
|
|
3
|
+
.runs-page header p, .run-muted, .runs-page small, .run-detail dt { color: var(--text-secondary); }
|
|
4
|
+
.runs-page h1 { font-size: 26px; margin-top: 0; }
|
|
5
|
+
.runs-page h2 { font-size: 18px; margin: 0; }
|
|
6
|
+
.runs-page p { line-height: 1.5; }
|
|
7
|
+
.runs-page a { color: var(--text-accent); text-underline-offset: 3px; }
|
|
8
|
+
.runs-filters { display: flex; align-items: end; gap: 12px; flex-wrap: wrap; margin-block: 24px; }
|
|
9
|
+
.runs-filters label { display: grid; gap: 6px; flex: 1 1 140px; font-size: 12px; }
|
|
10
|
+
.runs-page input, .runs-page select, .runs-page button { font: inherit; color: var(--text-primary); background: var(--bg-input); border: 1px solid var(--border-default); border-radius: var(--radius-sm); padding: 8px 10px; min-height: 36px; min-width: 0; }
|
|
11
|
+
.runs-page input, .runs-page select { width: 100%; box-sizing: border-box; }
|
|
12
|
+
.runs-page button { cursor: pointer; }
|
|
13
|
+
.runs-page button:hover:not(:disabled) { background: var(--bg-sidebar-hover); }
|
|
14
|
+
.runs-page button:disabled { opacity: .45; cursor: default; }
|
|
15
|
+
.runs-page :focus-visible { outline: 2px solid var(--border-accent); outline-offset: 2px; }
|
|
16
|
+
.runs-list { display: grid; gap: 12px; }
|
|
17
|
+
.run-card { border: 1px solid var(--border-default); border-radius: var(--radius-md); padding: 18px; overflow-wrap: anywhere; background: var(--bg-base); }
|
|
18
|
+
.run-card-title { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px; }
|
|
19
|
+
.run-state { font-size: 12px; border: 1px solid var(--border-default); padding: 3px 8px; border-radius: 20px; }
|
|
20
|
+
.run-state-blocked, .run-state-failed { color: var(--tool-error-text); }
|
|
21
|
+
.run-detail { box-sizing: border-box; margin-bottom: 20px; }
|
|
22
|
+
.run-detail dl { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px 24px; }
|
|
23
|
+
.run-detail dt { font-size: 12px; margin-bottom: 4px; }
|
|
24
|
+
.run-detail dd { margin: 0; font-size: 13px; }
|
|
25
|
+
.runs-pagination { display: flex; align-items: center; justify-content: center; gap: 16px; padding-block: 20px; }
|
|
26
|
+
@media (max-width: 600px) { .runs-page { padding: 16px; } .run-card { padding: 14px; } .run-detail dl { grid-template-columns: 1fr; } }
|