cli-jaw 2.17.62 → 2.17.63
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/README.ja.md +17 -17
- package/README.ko.md +17 -17
- package/README.md +17 -17
- package/README.zh-CN.md +17 -17
- package/dist/bin/commands/tui/input-handler.js +19 -0
- package/dist/bin/commands/tui/input-handler.js.map +1 -1
- package/dist/bin/commands/tui/overlays.js +8 -4
- package/dist/bin/commands/tui/overlays.js.map +1 -1
- package/dist/bin/star-prompt.js +1 -1
- package/dist/bin/star-prompt.js.map +1 -1
- package/dist/server.js +14 -1
- package/dist/server.js.map +1 -1
- package/dist/src/agent/claude-runtime-pool.js +2 -1
- package/dist/src/agent/claude-runtime-pool.js.map +1 -1
- package/dist/src/agent/claude-runtime-run.js +28 -7
- package/dist/src/agent/claude-runtime-run.js.map +1 -1
- package/dist/src/agent/lifecycle-handler.js +34 -6
- package/dist/src/agent/lifecycle-handler.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-events.js +136 -9
- package/dist/src/agent/runtime/claude-sdk-events.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-history-loader.js +24 -0
- package/dist/src/agent/runtime/claude-sdk-history-loader.js.map +1 -0
- package/dist/src/agent/runtime/claude-sdk-hooks.js +10 -4
- package/dist/src/agent/runtime/claude-sdk-hooks.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-messages.js +25 -2
- package/dist/src/agent/runtime/claude-sdk-messages.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-options.js +36 -3
- package/dist/src/agent/runtime/claude-sdk-options.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-permissions.js +32 -9
- package/dist/src/agent/runtime/claude-sdk-permissions.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-session.js +165 -17
- package/dist/src/agent/runtime/claude-sdk-session.js.map +1 -1
- package/dist/src/agent/runtime/claude-sdk-version.js +8 -0
- package/dist/src/agent/runtime/claude-sdk-version.js.map +1 -0
- package/dist/src/agent/spawn/backend-codex-app.js +619 -0
- package/dist/src/agent/spawn/backend-codex-app.js.map +1 -0
- package/dist/src/agent/spawn/backend-context.js +2 -0
- package/dist/src/agent/spawn/backend-context.js.map +1 -0
- package/dist/src/agent/spawn/backend-copilot.js +385 -0
- package/dist/src/agent/spawn/backend-copilot.js.map +1 -0
- package/dist/src/agent/spawn/backend-native-acp.js +379 -0
- package/dist/src/agent/spawn/backend-native-acp.js.map +1 -0
- package/dist/src/agent/spawn/backend-pi.js +565 -0
- package/dist/src/agent/spawn/backend-pi.js.map +1 -0
- package/dist/src/agent/spawn/types.js +2 -0
- package/dist/src/agent/spawn/types.js.map +1 -0
- package/dist/src/agent/spawn.js +44 -1858
- package/dist/src/agent/spawn.js.map +1 -1
- package/dist/src/cli/claude-default-model-boot.js +36 -0
- package/dist/src/cli/claude-default-model-boot.js.map +1 -0
- package/dist/src/cli/claude-model-discovery.js +13 -1
- package/dist/src/cli/claude-model-discovery.js.map +1 -1
- package/dist/src/cli/claude-models.js +7 -1
- package/dist/src/cli/claude-models.js.map +1 -1
- package/dist/src/cli/commands.js +5 -15
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/cli/tui/composer.js +3 -3
- package/dist/src/cli/tui/composer.js.map +1 -1
- package/dist/src/cli/tui/skill-mention.js +15 -0
- package/dist/src/cli/tui/skill-mention.js.map +1 -0
- package/dist/src/code-mode/manager.js +174 -9
- package/dist/src/code-mode/manager.js.map +1 -1
- package/dist/src/code-mode/providers/catalog.js +5 -3
- package/dist/src/code-mode/providers/catalog.js.map +1 -1
- package/dist/src/code-mode/providers/claude-history.js +132 -0
- package/dist/src/code-mode/providers/claude-history.js.map +1 -0
- package/dist/src/code-mode/providers/claude.js +74 -7
- package/dist/src/code-mode/providers/claude.js.map +1 -1
- package/dist/src/code-mode/providers/codex-app.js +7 -1
- package/dist/src/code-mode/providers/codex-app.js.map +1 -1
- package/dist/src/code-mode/session.js +158 -11
- package/dist/src/code-mode/session.js.map +1 -1
- package/dist/src/code-mode/store.js +416 -28
- package/dist/src/code-mode/store.js.map +1 -1
- package/dist/src/command-contract/catalog.js +3 -22
- package/dist/src/command-contract/catalog.js.map +1 -1
- package/dist/src/core/codex-config.js +3 -62
- package/dist/src/core/codex-config.js.map +1 -1
- package/dist/src/core/config.js +116 -14
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/runtime-settings.js +20 -10
- package/dist/src/core/runtime-settings.js.map +1 -1
- package/dist/src/core/settings-watch.js +1 -0
- package/dist/src/core/settings-watch.js.map +1 -1
- package/dist/src/core/skill-mentions.js +83 -0
- package/dist/src/core/skill-mentions.js.map +1 -0
- package/dist/src/manager/registry.js +5 -1
- package/dist/src/manager/registry.js.map +1 -1
- package/dist/src/manager/routes/desktop-status.js +1 -1
- package/dist/src/manager/routes/desktop-status.js.map +1 -1
- package/dist/src/orchestrator/pipeline.js +11 -1
- package/dist/src/orchestrator/pipeline.js.map +1 -1
- package/dist/src/prompt/builder.js +10 -4
- package/dist/src/prompt/builder.js.map +1 -1
- package/dist/src/prompt/templates/a1-system.md +1 -1
- package/dist/src/routes/code-native.js +51 -3
- package/dist/src/routes/code-native.js.map +1 -1
- package/dist/src/routes/settings.js +1 -4
- package/dist/src/routes/settings.js.map +1 -1
- package/dist/src/routes/skills.js +7 -0
- package/dist/src/routes/skills.js.map +1 -1
- package/dist/src/shared/skill-mention.js +61 -0
- package/dist/src/shared/skill-mention.js.map +1 -0
- package/dist/src/shared/tool-input.js +137 -0
- package/dist/src/shared/tool-input.js.map +1 -0
- package/package.json +7 -5
- package/public/css/activity.css +83 -20
- package/public/css/layout.css +2 -2
- package/public/css/sidebar.css +18 -0
- package/public/css/variables.css +23 -2
- package/public/dist/.vite/manifest.json +302 -334
- package/public/dist/assets/Activity-DPLlOJ46.js +1 -0
- package/public/dist/assets/AdvancedExport-BgOgGvc5.js +1 -0
- package/public/dist/assets/Agent-O_U8ny90.js +1 -0
- package/public/dist/assets/Browser-m6VynQXV.js +1 -0
- package/public/dist/assets/BrowserPanel-Bb-Bge6B.js +3 -0
- package/public/dist/assets/ChannelEnablementControl-BSuKQhTW.js +85 -0
- package/public/dist/assets/ChannelSetupEntry-DHLoZEzJ.js +1 -0
- package/public/dist/assets/ChannelsDiscord-B-G8vsYy.js +1 -0
- package/public/dist/assets/ChannelsSlack-oPuh922z.js +1 -0
- package/public/dist/assets/ChannelsTelegram-PmTv43KQ.js +1 -0
- package/public/dist/assets/CodeCanvas-CVbdp0S-.js +3 -0
- package/public/dist/assets/DashboardMeta-BqEiJrvr.js +1 -0
- package/public/dist/assets/DesignPanel-BOb-HMbl.js +2 -0
- package/public/dist/assets/{Developer-BdKPcw8Z.js → Developer-CbySB7_Z.js} +1 -1
- package/public/dist/assets/{DiffPanel-DtJuvU5y.js → DiffPanel-D9Cch5FG.js} +1 -1
- package/public/dist/assets/Display-C0Zdamcb.js +1 -0
- package/public/dist/assets/Display-DU4_76cS.js +1 -0
- package/public/dist/assets/DocPanel-DvtFw8AP.js +1 -0
- package/public/dist/assets/{Embedding-DvdTj1BF.js → Embedding-CCFMOB_2.js} +1 -1
- package/public/dist/assets/Employees-BanvF58d.js +1 -0
- package/public/dist/assets/FolderPanel-CJvwRwIV.js +3 -0
- package/public/dist/assets/Heartbeat-uyi_MpUd.js +1 -0
- package/public/dist/assets/InlineWarn-Cq4z28F3.js +1 -0
- package/public/dist/assets/MarkdownRenderer-Cc3DeHvm.js +1 -0
- package/public/dist/assets/{MarkdownRenderer-DhonsAPC.js → MarkdownRenderer-DzN66XVb.js} +1 -1
- package/public/dist/assets/Mcp-rjPoL_Zo.js +4 -0
- package/public/dist/assets/Memory-C9I33pIi.js +1 -0
- package/public/dist/assets/{MilkdownWysiwygEditor-D6JaLDE4.js → MilkdownWysiwygEditor-xObhviw7.js} +1 -1
- package/public/dist/assets/ModelProvider-fiUktMaO.js +1 -0
- package/public/dist/assets/Network-CuqFrOvR.js +1 -0
- package/public/dist/assets/NotesGraphWorkspace-DO9LtVGF.js +1 -0
- package/public/dist/assets/PanelLayoutProvider-DRZ4QkBC.js +1 -0
- package/public/dist/assets/Permissions-BltXiZnz.js +1 -0
- package/public/dist/assets/{Permissions-BwiMEoTV.js → Permissions-DcCbFMIq.js} +1 -1
- package/public/dist/assets/Profile-DUL7-RE_.js +1 -0
- package/public/dist/assets/Prompts-C9vY8ivQ.js +1 -0
- package/public/dist/assets/SpeechKeys-BJ8V7U5E.js +1 -0
- package/public/dist/assets/TelegramHub-DtyjLGFR.js +1 -0
- package/public/dist/assets/{TerminalPanel-Bu9i-P2f.js → TerminalPanel-c1xhiy53.js} +1 -1
- package/public/dist/assets/app-BjKzqXCv.css +1 -0
- package/public/dist/assets/app-CZwx0V6K.js +59 -0
- package/public/dist/assets/bgtask-badge-CnVkcp3c.js +1 -0
- package/public/dist/assets/{bgtask-badge-DfiHuzxT.js → bgtask-badge-Cp26S8Uq.js} +1 -1
- package/public/dist/assets/client-platform-WO8m6kPQ.js +1 -0
- package/public/dist/assets/{employees-BQaJNp-u.js → employees-CsvI0_5V.js} +1 -1
- package/public/dist/assets/error-normalize-clOi-AAA.js +1 -0
- package/public/dist/assets/fields-CicOWY-X.js +1 -0
- package/public/dist/assets/iframe-renderer-DWx9v2Iz.js +1 -0
- package/public/dist/assets/iframe-renderer-DmHYOI1A.js +142 -0
- package/public/dist/assets/{react-C_pPTFsM.js → jsx-runtime-CkEUZM6i.js} +1 -1
- package/public/dist/assets/manager-CCXm5Yfe.js +5 -0
- package/public/dist/assets/manager-D9rhKFKc.css +1 -0
- package/public/dist/assets/manager-shortcuts-Cqmr0mEq.js +1 -0
- package/public/dist/assets/{memory-EG1B51up.js → memory-C6qtAMdJ.js} +1 -1
- package/public/dist/assets/memory-PPofQPeB.js +1 -0
- package/public/dist/assets/message-history-DcjBuubA.js +4 -0
- package/public/dist/assets/message-history-bw4qF01Z.js +1 -0
- package/public/dist/assets/page-shell-DfinU3mb.js +1 -0
- package/public/dist/assets/react-dom-URBLzV3w.js +1 -0
- package/public/dist/assets/{render-Dp07lZAJ.js → render-DfYro-fG.js} +1 -1
- package/public/dist/assets/{settings-jlI4X-zt.js → settings-CvsLWlJn.js} +38 -30
- package/public/dist/assets/settings-DMiQe469.js +1 -0
- package/public/dist/assets/settings-DgAlohx9.js +1 -0
- package/public/dist/assets/settings-core-BH7J_Rbm.js +1 -0
- package/public/dist/assets/{settings-core-B5Uf_lWY.js → settings-core-pnpw-eZv.js} +1 -1
- package/public/dist/assets/{settings-embedding-BfWzWKMM.js → settings-embedding-BRW0WZvh.js} +3 -3
- package/public/dist/assets/{settings-embedding-DhEdT86W.css → settings-embedding-CUQ8Is3y.css} +1 -1
- package/public/dist/assets/settings-types-CYKy0xz4.js +1 -0
- package/public/dist/assets/shared-D1begqvi.js +1 -0
- package/public/dist/assets/{sidebar-BUemG2ZN.js → sidebar-C_80w4iY.js} +3 -3
- package/public/dist/assets/{skills-CKUcEN9m.js → skills-D_2adqge.js} +1 -1
- package/public/dist/assets/skills-DeNVAVgt.js +1 -0
- package/public/dist/assets/{slash-commands-D34GgHFX.js → slash-commands-B_Hq5RkC.js} +2 -2
- package/public/dist/assets/slash-commands-Cj5qoRWc.js +1 -0
- package/public/dist/assets/tool-input-DI7uu8Ah.js +2 -0
- package/public/dist/assets/{trace-drawer-DBmK-5F_.js → trace-drawer-B6XuPodh.js} +1 -1
- package/public/dist/assets/ui-BbR_fFiK.js +1 -0
- package/public/dist/assets/{ui-vm2iuLcS.js → ui-D8xzm7DN.js} +1 -1
- package/public/dist/index.html +11 -3
- package/public/dist/manager/index.html +3 -3
- package/public/dist/settings/index.html +2 -2
- package/public/index.html +9 -1
- package/public/js/features/activity-history.ts +50 -21
- package/public/js/features/activity-rows.ts +158 -10
- package/public/js/features/activity-view.ts +5 -5
- package/public/js/features/chat.ts +2 -0
- package/public/js/features/employees.ts +1 -0
- package/public/js/features/resize-guard.ts +39 -0
- package/public/js/features/settings-cli-status.ts +49 -17
- package/public/js/features/settings-types.ts +39 -1
- package/public/js/features/skill-mentions.ts +143 -0
- package/public/js/main.ts +15 -0
- package/public/js/virtual-scroll.ts +67 -1
- package/public/locales/en.json +2 -0
- package/public/locales/ja.json +2 -0
- package/public/locales/ko.json +2 -0
- package/public/locales/zh.json +2 -0
- package/public/manager/src/AppChrome.tsx +2 -0
- package/public/manager/src/InstancePreview.tsx +1 -1
- package/public/manager/src/SidebarRailRouter.tsx +3 -1
- package/public/manager/src/browser-panel/BrowserPanel.tsx +47 -12
- package/public/manager/src/browser-panel/browser-address-state.ts +11 -0
- package/public/manager/src/client-platform.ts +18 -0
- package/public/manager/src/code/CodeCanvas.tsx +4 -2
- package/public/manager/src/code/CodeComposer.tsx +22 -10
- package/public/manager/src/code/CodeDraftEmptyState.tsx +64 -0
- package/public/manager/src/code/CodeSessionList.tsx +154 -40
- package/public/manager/src/code/CodeTranscript.tsx +104 -16
- package/public/manager/src/code/CodeWorkbench.tsx +21 -11
- package/public/manager/src/code/CodeWorkspaceHeader.tsx +5 -10
- package/public/manager/src/code/ComposerFooter.tsx +9 -1
- package/public/manager/src/code/code-composer.css +1 -1
- package/public/manager/src/code/code-controller-draft-storage.ts +16 -8
- package/public/manager/src/code/code-controller-drafts.ts +33 -6
- package/public/manager/src/code/code-controller-runtime.ts +189 -17
- package/public/manager/src/code/code-controller-types.ts +19 -1
- package/public/manager/src/code/code-recent-workspaces.ts +25 -0
- package/public/manager/src/code/code-session-client.ts +19 -1
- package/public/manager/src/code/code-session-sidebar.css +38 -0
- package/public/manager/src/code/code-session-state.ts +2 -0
- package/public/manager/src/code/code-types.ts +47 -0
- package/public/manager/src/code/code-workbench.css +36 -1
- package/public/manager/src/code/code.css +171 -7
- package/public/manager/src/code/session-order.ts +80 -0
- package/public/manager/src/code/tool-summary.ts +45 -11
- package/public/manager/src/folder-panel/folder-shortcuts.ts +5 -9
- package/public/manager/src/hooks/useCommandPalette.ts +3 -7
- package/public/manager/src/main.tsx +1 -0
- package/public/manager/src/manager-components.css +25 -23
- package/public/manager/src/manager-dashboard-reminders.css +2 -2
- package/public/manager/src/manager-layout.css +1 -1
- package/public/manager/src/manager-shortcut-runner.ts +9 -2
- package/public/manager/src/manager-shortcuts.ts +83 -21
- package/public/manager/src/panels/desktop-bridge.ts +4 -1
- package/public/manager/src/panels/panels.css +9 -0
- package/public/manager/src/preview.ts +19 -4
- package/public/manager/src/settings/pages/AdvancedExport.tsx +28 -30
- package/public/manager/src/settings/pages/Browser.tsx +45 -47
- package/public/manager/src/settings/pages/ChannelsDiscord.tsx +51 -26
- package/public/manager/src/settings/pages/ChannelsSlack.tsx +59 -17
- package/public/manager/src/settings/pages/ChannelsTelegram.tsx +51 -26
- package/public/manager/src/settings/pages/Display.tsx +2 -1
- package/public/manager/src/settings/pages/Employees.tsx +3 -3
- package/public/manager/src/settings/pages/Mcp.tsx +220 -200
- package/public/manager/src/settings/pages/Memory.tsx +35 -41
- package/public/manager/src/settings/pages/ModelProvider.tsx +23 -98
- package/public/manager/src/settings/pages/Network.tsx +5 -4
- package/public/manager/src/settings/pages/Permissions.tsx +38 -16
- package/public/manager/src/settings/pages/Prompts.tsx +12 -9
- package/public/manager/src/settings/pages/SpeechKeys.tsx +132 -139
- package/public/manager/src/settings/pages/TelegramHub.tsx +67 -20
- package/public/manager/src/settings/pages/components/AvatarCard.tsx +9 -8
- package/public/manager/src/settings/pages/components/ChannelSetupEntry.tsx +2 -2
- package/public/manager/src/settings/pages/components/EmployeeRow.tsx +6 -12
- package/public/manager/src/settings/pages/components/HealthBadge.tsx +11 -6
- package/public/manager/src/settings/pages/components/HeartbeatJobRow.tsx +35 -16
- package/public/manager/src/settings/pages/components/HeartbeatJobsSection.tsx +11 -11
- package/public/manager/src/settings/pages/components/HeartbeatMarkdownSection.tsx +7 -7
- package/public/manager/src/settings/pages/components/InlineWarn.tsx +4 -9
- package/public/manager/src/settings/pages/components/McpServerCard.tsx +39 -27
- package/public/manager/src/settings/pages/components/MemoryRow.tsx +21 -16
- package/public/manager/src/settings/pages/components/PerCliRow.tsx +122 -121
- package/public/manager/src/settings/pages/components/PiProfileDialog.tsx +9 -6
- package/public/manager/src/settings/pages/components/SlackSetup.tsx +8 -8
- package/public/manager/src/settings/pages/components/TransportStatusChips.tsx +14 -10
- package/public/manager/src/settings/pages/components/agent/AgentEmployeesSection.tsx +26 -26
- package/public/manager/src/settings/pages/components/agent/PermissionQuickSection.tsx +5 -5
- package/public/manager/src/settings/pages/components/agent/RuntimeEmployeeRow.tsx +14 -11
- package/public/manager/src/settings/pages/components/agent/RuntimeHeader.tsx +1 -3
- package/public/manager/src/settings/pages/components/agent/agent-meta.ts +0 -2
- package/public/manager/src/settings/pages/components/runtime-transport-field.tsx +2 -2
- package/public/manager/src/settings/pages/manager/Activity.tsx +6 -2
- package/public/manager/src/settings/pages/manager/Display.tsx +3 -8
- package/public/manager/src/settings/pages/manager/shared.tsx +44 -33
- package/public/manager/src/settings/pages/page-shell.tsx +66 -0
- package/public/manager/src/settings-agent.css +185 -36
- package/public/manager/src/settings-standalone.tsx +1 -0
- package/public/manager/src/settings-surface.css +96 -0
- package/public/manager/src/types.ts +1 -0
- package/scripts/ci/install-ripgrep.sh +35 -0
- package/scripts/collect-fresh-install-evidence.sh +2 -2
- package/scripts/install-risk-gate.mjs +2 -3
- package/scripts/install-wsl.sh +1 -1
- package/scripts/install.ps1 +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/release-1.6.0.sh +1 -1
- package/scripts/release-gates.mjs +48 -13
- package/scripts/release-preview.sh +2 -2
- package/scripts/restack-pr.sh +88 -0
- package/scripts/verify-electron-update-metadata.mjs +1 -1
- package/scripts/windows-support-contract.json +1 -1
- package/public/dist/assets/Activity-BUrp73Re.js +0 -1
- package/public/dist/assets/AdvancedExport-B7h_ZvBu.js +0 -1
- package/public/dist/assets/Agent-DnbZJP7b.js +0 -1
- package/public/dist/assets/Browser-CiBoK5Qx.js +0 -1
- package/public/dist/assets/BrowserPanel-SZAUc9T2.js +0 -3
- package/public/dist/assets/ChannelEnablementControl-Dl0uYx38.js +0 -85
- package/public/dist/assets/ChannelSetupEntry-DxtOrN01.js +0 -1
- package/public/dist/assets/ChannelsDiscord-BEGGwd_3.js +0 -1
- package/public/dist/assets/ChannelsSlack-BryC2Ahr.js +0 -1
- package/public/dist/assets/ChannelsTelegram-CGSpaImJ.js +0 -1
- package/public/dist/assets/CodeCanvas-DpkFi5Au.js +0 -2
- package/public/dist/assets/DashboardMeta-Dh-tasbY.js +0 -1
- package/public/dist/assets/DesignPanel-NLNK9wCo.js +0 -2
- package/public/dist/assets/Display-BHGFX3O0.js +0 -1
- package/public/dist/assets/Display-BW-_pKRf.js +0 -1
- package/public/dist/assets/DocPanel-BM41cYpS.js +0 -1
- package/public/dist/assets/Employees-BuFw6-fT.js +0 -1
- package/public/dist/assets/FolderPanel-C-eQT3Yq.js +0 -3
- package/public/dist/assets/Heartbeat-CIVtKWNc.js +0 -1
- package/public/dist/assets/InlineWarn-DIMbcbzk.js +0 -1
- package/public/dist/assets/MarkdownRenderer-DyVx6DxM.js +0 -1
- package/public/dist/assets/Mcp-uKjor7vh.js +0 -4
- package/public/dist/assets/Memory-BSDIXT0B.js +0 -1
- package/public/dist/assets/ModelProvider-DMh2tXUx.js +0 -1
- package/public/dist/assets/Network-4Pa6JkhK.js +0 -1
- package/public/dist/assets/NotesGraphWorkspace-CgdC-fM6.js +0 -1
- package/public/dist/assets/PanelLayoutProvider-mn8sI0h-.js +0 -1
- package/public/dist/assets/Permissions-CodIW-Yd.js +0 -1
- package/public/dist/assets/Profile-DPqrYA44.js +0 -1
- package/public/dist/assets/Prompts-CvAx7QqR.js +0 -1
- package/public/dist/assets/SpeechKeys-CVBCqoBn.js +0 -1
- package/public/dist/assets/TelegramHub-4zKwpk0P.js +0 -1
- package/public/dist/assets/app-ByMfeXp-.js +0 -55
- package/public/dist/assets/app-DAJWqs33.css +0 -1
- package/public/dist/assets/bgtask-badge-Ch88fnhf.js +0 -1
- package/public/dist/assets/error-normalize-CL69Bl7z.js +0 -1
- package/public/dist/assets/fields-mlGpofqQ.js +0 -1
- package/public/dist/assets/iframe-renderer-Bj1JolO8.js +0 -142
- package/public/dist/assets/iframe-renderer-DWnvUSrx.js +0 -1
- package/public/dist/assets/jsx-runtime-NZYk81nU.js +0 -1
- package/public/dist/assets/manager-Bi_xw7TW.js +0 -5
- package/public/dist/assets/manager-BnQHAKk6.css +0 -1
- package/public/dist/assets/manager-shortcuts-BDOzd8zm.js +0 -1
- package/public/dist/assets/memory-Bk_cb3sw.js +0 -1
- package/public/dist/assets/message-history-D2vniu04.js +0 -4
- package/public/dist/assets/message-history-GAtF_wbZ.js +0 -1
- package/public/dist/assets/page-shell-BeGD0rRQ.js +0 -1
- package/public/dist/assets/react-dom-DyUOYpVL.js +0 -1
- package/public/dist/assets/settings-W4KG9ivu.js +0 -1
- package/public/dist/assets/settings-WVGhtneN.js +0 -1
- package/public/dist/assets/settings-client-CmwRsVd1.js +0 -1
- package/public/dist/assets/settings-core-CgKGnMA5.js +0 -1
- package/public/dist/assets/settings-types-D3cd6SWy.js +0 -1
- package/public/dist/assets/shared-CiNP20HB.js +0 -1
- package/public/dist/assets/skills-Co7Mr-qP.js +0 -1
- package/public/dist/assets/slash-commands-BEr-vmIr.js +0 -1
- package/public/dist/assets/ui-TUyJsMUf.js +0 -1
- package/public/manager/src/settings/pages/components/EngineSubsection.tsx +0 -25
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { lazy, Suspense, useCallback, useMemo, useRef, useState, type KeyboardEvent } from 'react';
|
|
1
|
+
import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState, type KeyboardEvent } from 'react';
|
|
2
2
|
import type { CodeItem, CodeItemKind, CodeProviderId } from '../../../../src/code-mode/wire';
|
|
3
3
|
import { useCodeTranscriptVirtualRows } from './useCodeTranscriptVirtualRows';
|
|
4
4
|
import { useCodeTranscriptScroll } from './use-code-transcript-scroll';
|
|
5
5
|
import { useThrottledMarkdown } from './use-throttled-markdown';
|
|
6
6
|
import { CODE_RUNTIME_LABELS, codeItemStatus } from './code-types';
|
|
7
|
-
import { noteworthyStatus, toolSummary } from './tool-summary';
|
|
7
|
+
import { noteworthyStatus, toolInputDescription, toolInputDisplay, toolSummary } from './tool-summary';
|
|
8
|
+
import { copyText } from '../clipboard/copy-text';
|
|
8
9
|
import { PENDING_USER_ITEM_ID } from './pending-user-item';
|
|
9
10
|
|
|
10
11
|
const MarkdownRenderer = lazy(() => import('../notes/rendering/MarkdownRenderer').then(m => ({ default: m.MarkdownRenderer })));
|
|
12
|
+
export const CODE_ROLLBACK_CONFIRM = 'Later turns are removed from this conversation. Workspace files are not changed. No prompt is sent.';
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Turn boundaries are bookkeeping, not conversation. A one-line answer framed by
|
|
@@ -24,15 +26,22 @@ const HIDDEN_KINDS: ReadonlySet<CodeItemKind> = new Set<CodeItemKind>(['turn_sta
|
|
|
24
26
|
/**
|
|
25
27
|
* What a collapsible row measures before it has been measured. Collapsed is one
|
|
26
28
|
* line and exact. Expanded is a guess and deliberately only that: the panes are
|
|
27
|
-
* capped at
|
|
28
|
-
* with both is far taller, and the real number arrives from the
|
|
29
|
-
* a frame later. This only has to be closer than one line.
|
|
29
|
+
* capped at min(18rem,50dvh) each by `.code-tool-output` / `.code-tool-args`,
|
|
30
|
+
* so a call with both is far taller, and the real number arrives from the
|
|
31
|
+
* ResizeObserver a frame later. This only has to be closer than one line.
|
|
30
32
|
*/
|
|
31
33
|
const COLLAPSED_ROW_PX = 44;
|
|
32
|
-
const EXPANDED_ROW_PX =
|
|
34
|
+
const EXPANDED_ROW_PX = 480;
|
|
33
35
|
/** Same bound the scroll anchors use, for the same reason. */
|
|
34
36
|
const MAX_OPEN_ROWS = 64;
|
|
35
37
|
|
|
38
|
+
// Past ~15 wrapped lines the capped scroll height already engages; only then
|
|
39
|
+
// is a Show-all toggle worth showing (jsdom cannot measure overflow either, so
|
|
40
|
+
// this estimates by length the same way the activity rows do).
|
|
41
|
+
function isTallToolText(value: string): boolean {
|
|
42
|
+
return value.length > 1400 || value.split('\n').length > 14;
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
/**
|
|
37
46
|
* A failed call opens itself, because the reader has to see why it failed. That
|
|
38
47
|
* is a default, not a lock: once the reader has said what they want for this
|
|
@@ -52,11 +61,13 @@ function ItemMarkdown({ item, identity, onOpenLocalFile }: { item: CodeItem; ide
|
|
|
52
61
|
</Suspense>;
|
|
53
62
|
}
|
|
54
63
|
|
|
55
|
-
export function CodeTranscriptItem({ item, provider, sessionKey, workingDir = '', onOpenLocalFile, expanded, onExpandedChange }: {
|
|
64
|
+
export function CodeTranscriptItem({ item, provider, sessionKey, workingDir = '', onOpenLocalFile, expanded, onExpandedChange, onRollback }: {
|
|
56
65
|
item: CodeItem; provider: CodeProviderId; sessionKey: string; workingDir?: string;
|
|
57
66
|
onOpenLocalFile?: ((path: string) => void) | undefined;
|
|
58
67
|
expanded?: boolean;
|
|
59
68
|
onExpandedChange?: ((itemId: string, open: boolean) => void) | undefined;
|
|
69
|
+
/** Present only on a row the conversation may be rolled back to; asks before calling. */
|
|
70
|
+
onRollback?: ((itemId: string) => void) | undefined;
|
|
60
71
|
}) {
|
|
61
72
|
const tool = item.kind === 'tool_call' || item.kind === 'file_change';
|
|
62
73
|
const reasoning = item.kind === 'reasoning';
|
|
@@ -78,6 +89,53 @@ export function CodeTranscriptItem({ item, provider, sessionKey, workingDir = ''
|
|
|
78
89
|
// the duplicate back. Failure and cancellation still get a word, because
|
|
79
90
|
// those change what to do next.
|
|
80
91
|
const toolNote = running ? null : note;
|
|
92
|
+
const description = tool ? toolInputDescription(item.tool?.input) : '';
|
|
93
|
+
const inputDisplay = tool ? toolInputDisplay(item.tool?.input) : null;
|
|
94
|
+
const toolBlocks: Array<{ key: string; label: string; content: string; className: string; copyable: boolean }> = [];
|
|
95
|
+
if (tool) {
|
|
96
|
+
if (inputDisplay) toolBlocks.push({ key: 'input', label: 'Input', content: inputDisplay.content, className: 'code-tool-args', copyable: true });
|
|
97
|
+
if (inputDisplay?.parameters) toolBlocks.push({ key: 'params', label: 'Parameters', content: inputDisplay.parameters, className: 'code-tool-json', copyable: false });
|
|
98
|
+
if (inputDisplay?.description) toolBlocks.push({ key: 'description', label: 'Description', content: inputDisplay.description, className: 'code-tool-text', copyable: false });
|
|
99
|
+
if (item.tool?.output !== undefined) toolBlocks.push({ key: 'output', label: 'Output', content: item.tool.output, className: 'code-tool-output', copyable: false });
|
|
100
|
+
}
|
|
101
|
+
const toolBodyTall = toolBlocks.some(block => isTallToolText(block.content))
|
|
102
|
+
|| (item.tool?.detail !== undefined && isTallToolText(item.tool.detail))
|
|
103
|
+
|| (item.text !== undefined && isTallToolText(item.text));
|
|
104
|
+
const [showAll, setShowAll] = useState(false);
|
|
105
|
+
const bodyRef = useRef<HTMLDivElement | null>(null);
|
|
106
|
+
const [measuredTall, setMeasuredTall] = useState<boolean | null>(null);
|
|
107
|
+
// A measurement describes the body it was taken from, and the estimate is
|
|
108
|
+
// what moves when that body changes, so a number taken while the content was
|
|
109
|
+
// taller is dropped instead of deciding for the shorter content in its place.
|
|
110
|
+
useEffect(() => { setMeasuredTall(null); }, [toolBodyTall]);
|
|
111
|
+
// The toggle measures real overflow once the body lays out (browser); where
|
|
112
|
+
// layout cannot be measured (jsdom reports 0 everywhere) the character
|
|
113
|
+
// estimate stands in, the same rule the activity rows use. Every capped pane
|
|
114
|
+
// is measured, not only the <pre> ones: the call's own detail is a paragraph
|
|
115
|
+
// under the same cap. While expanded the cap is off, so measuring is skipped
|
|
116
|
+
// — the button must stay to toggle back — and closing the body resets the
|
|
117
|
+
// measure for the next open.
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (!open || showAll) return;
|
|
120
|
+
const panes = bodyRef.current?.querySelectorAll('pre, .code-tool-body > .code-tool-text');
|
|
121
|
+
if (!panes || panes.length === 0) { setMeasuredTall(null); return; }
|
|
122
|
+
const list = [...panes];
|
|
123
|
+
const measured = list.some(pane => pane.scrollHeight > pane.clientHeight + 2);
|
|
124
|
+
setMeasuredTall(measured || (list.every(pane => pane.scrollHeight === 0) && toolBodyTall));
|
|
125
|
+
}, [open, showAll, toolBodyTall]);
|
|
126
|
+
const toolBodyOverflowing = measuredTall ?? toolBodyTall;
|
|
127
|
+
// Content that no longer overflows has nothing left to disclose: leaving the
|
|
128
|
+
// disclosure set would hold a "Show less" button, and an uncapped body, over
|
|
129
|
+
// a call that already fits.
|
|
130
|
+
useEffect(() => { if (!toolBodyOverflowing) setShowAll(false); }, [toolBodyOverflowing]);
|
|
131
|
+
const [copied, setCopied] = useState(false);
|
|
132
|
+
const copiedTimerRef = useRef<number | null>(null);
|
|
133
|
+
useEffect(() => () => { if (copiedTimerRef.current !== null) window.clearTimeout(copiedTimerRef.current); }, []);
|
|
134
|
+
const markCopied = useCallback(() => {
|
|
135
|
+
setCopied(true);
|
|
136
|
+
if (copiedTimerRef.current !== null) window.clearTimeout(copiedTimerRef.current);
|
|
137
|
+
copiedTimerRef.current = window.setTimeout(() => { copiedTimerRef.current = null; setCopied(false); }, 1200);
|
|
138
|
+
}, []);
|
|
81
139
|
const label = user ? 'You' : assistant ? CODE_RUNTIME_LABELS[provider] : reasoning ? 'Reasoning'
|
|
82
140
|
: item.kind === 'turn_started' ? 'Turn started' : item.kind === 'session_runtime' ? 'Runtime'
|
|
83
141
|
: item.kind === 'permission_request' ? 'Permission record' : item.kind === 'notice' ? 'Notice' : status;
|
|
@@ -86,36 +144,60 @@ export function CodeTranscriptItem({ item, provider, sessionKey, workingDir = ''
|
|
|
86
144
|
{tool ? <details className={`code-tool-card code-tool-${item.status}`} open={open}
|
|
87
145
|
onToggle={event => onExpandedChange?.(item.itemId, event.currentTarget.open)}>
|
|
88
146
|
<summary className="code-tool-summary"><span className="code-tool-chevron" aria-hidden="true">›</span>
|
|
89
|
-
<span className=
|
|
147
|
+
<span className="code-tool-summary-text">
|
|
148
|
+
<span className={`code-tool-name${running ? ' code-tool-name-running' : ''}`}>{toolSummary(item, workingDir)}</span>
|
|
149
|
+
{description !== '' && <span className="code-tool-desc">{description}</span>}
|
|
150
|
+
</span>
|
|
90
151
|
{toolNote && <span className="code-tool-status">{toolNote}</span>}</summary>
|
|
91
152
|
{/* Built only while open: a collapsed call's output can be megabytes,
|
|
92
153
|
and constructing it costs the same whether or not it is painted. */}
|
|
93
|
-
{open &&
|
|
154
|
+
{open && <div ref={bodyRef} className="code-tool-body" data-expanded={showAll}>
|
|
94
155
|
{item.tool?.detail !== undefined && <p className="code-tool-text">{item.tool.detail}</p>}
|
|
95
|
-
{
|
|
96
|
-
|
|
156
|
+
{toolBlocks.map(block => <section key={block.key} className="code-tool-section">
|
|
157
|
+
<div className="code-tool-section-head">
|
|
158
|
+
<span className="code-tool-section-label">{block.label}</span>
|
|
159
|
+
{block.copyable && <button type="button" className={`code-tool-copy${copied ? ' copied' : ''}`}
|
|
160
|
+
aria-label={`Copy ${block.label.toLowerCase()}`}
|
|
161
|
+
onClick={() => void copyText(block.content).then(result => { if (result.ok) markCopied(); })}>
|
|
162
|
+
{copied ? 'Copied' : 'Copy'}
|
|
163
|
+
</button>}
|
|
164
|
+
</div>
|
|
165
|
+
<pre className={block.className}>{block.content}</pre>
|
|
166
|
+
</section>)}
|
|
97
167
|
{item.text !== undefined && <pre className="code-tool-text">{item.text}</pre>}
|
|
98
|
-
|
|
168
|
+
{running && item.tool?.output === undefined && item.tool?.detail === undefined
|
|
169
|
+
&& <p className="code-tool-waiting">Waiting for output…</p>}
|
|
170
|
+
{toolBodyOverflowing && <button type="button" className="code-tool-toggle" aria-expanded={showAll}
|
|
171
|
+
onClick={() => setShowAll(value => !value)}>{showAll ? 'Show less' : 'Show all'}</button>}
|
|
172
|
+
</div>}
|
|
99
173
|
</details> : reasoning ? <details className="code-thinking" open={open}
|
|
100
174
|
onToggle={event => onExpandedChange?.(item.itemId, event.currentTarget.open)}>
|
|
101
175
|
<summary className={`code-thinking-summary${item.status === 'running' ? ' code-tool-name-running' : ''}`}>{item.status === 'running' ? 'Thinking…' : 'Reasoning'}</summary>
|
|
102
176
|
{open && <div className="code-thinking-text">{item.text}</div>}
|
|
103
177
|
</details> : <>
|
|
104
178
|
<span className="code-message-role">{label}{assistant && item.phase === 'commentary' ? ' · Commentary' : ''}
|
|
105
|
-
{note && ` · ${unsent ? 'Sending' : note}`}
|
|
179
|
+
{note && ` · ${unsent ? 'Sending' : note}`}
|
|
180
|
+
{/* A follow-up no native result consumed before the turn ended. */}
|
|
181
|
+
{user && !unsent && item.phase === 'unknown' && ' · Delivery not confirmed'}</span>
|
|
106
182
|
<div className="code-message-text">{assistant ? <ItemMarkdown item={item} identity={`${sessionKey}:${item.itemId}`} onOpenLocalFile={onOpenLocalFile} />
|
|
107
183
|
: <span className="code-plain-text">{item.text ?? item.permission?.title ?? ''}</span>}</div>
|
|
108
184
|
{item.permission?.detail && <p className="code-plain-text">{item.permission.detail}</p>}
|
|
185
|
+
{onRollback && user && !unsent && <button type="button" className="code-message-rollback"
|
|
186
|
+
onClick={() => { if (window.confirm(CODE_ROLLBACK_CONFIRM)) onRollback(item.itemId); }}>Roll back conversation to here</button>}
|
|
109
187
|
</>}
|
|
110
188
|
{(assistant || reasoning) && (item.status === 'cancelled' || item.status === 'error') && <span className="code-partial-label">Partial output · {status}</span>}
|
|
111
189
|
{item.truncation && <p className="code-truncation" role="note">Output truncated: {item.truncation.storedChars.toLocaleString()} of {item.truncation.sourceChars.toLocaleString()} characters retained.</p>}
|
|
112
190
|
</article>;
|
|
113
191
|
}
|
|
114
192
|
|
|
115
|
-
export function CodeTranscript({ items, provider, sessionKey, workingDir, loading, hasOlderHistory, loadOlderHistory, permissionCount, onOpenLocalFile
|
|
116
|
-
|
|
193
|
+
export function CodeTranscript({ items, provider, sessionKey, workingDir, loading, hasOlderHistory, loadOlderHistory, permissionCount, onOpenLocalFile, working = false,
|
|
194
|
+
rollbackRows, onRollback }: {
|
|
195
|
+
items: CodeItem[]; provider: CodeProviderId; sessionKey: string; workingDir: string; loading: boolean; working?: boolean;
|
|
117
196
|
hasOlderHistory: boolean; loadOlderHistory(): Promise<void>; permissionCount: number;
|
|
118
197
|
onOpenLocalFile?: ((path: string) => void) | undefined;
|
|
198
|
+
/** Opaque user row ids that may be rollback targets; the transcript never sees native ids. */
|
|
199
|
+
rollbackRows?: ReadonlySet<string> | undefined;
|
|
200
|
+
onRollback?: ((itemId: string) => void) | undefined;
|
|
119
201
|
}) {
|
|
120
202
|
const transcriptRef = useRef<HTMLDivElement>(null);
|
|
121
203
|
const visible = useMemo(() => items.filter(item => !HIDDEN_KINDS.has(item.kind)), [items]);
|
|
@@ -209,10 +291,16 @@ export function CodeTranscript({ items, provider, sessionKey, workingDir, loadin
|
|
|
209
291
|
<CodeTranscriptItem item={item} provider={provider} sessionKey={sessionKey}
|
|
210
292
|
workingDir={workingDir} onOpenLocalFile={onOpenLocalFile}
|
|
211
293
|
expanded={isRowOpen(openRows, sessionKey, item)}
|
|
212
|
-
onExpandedChange={setExpanded}
|
|
294
|
+
onExpandedChange={setExpanded}
|
|
295
|
+
onRollback={rollbackRows?.has(item.itemId) ? onRollback : undefined} />
|
|
213
296
|
</div> : null;
|
|
214
297
|
})}
|
|
215
298
|
</div>}
|
|
299
|
+
{/* From Send until the turn ends. A running tool or reasoning row already
|
|
300
|
+
animates, so the line would only repeat it. */}
|
|
301
|
+
{working && visible[visible.length - 1]?.status !== 'running'
|
|
302
|
+
&& <div className="code-transcript-working" role="status" aria-live="polite">
|
|
303
|
+
<span className="code-session-spinner" aria-hidden="true" />Working…</div>}
|
|
216
304
|
</div>
|
|
217
305
|
</>;
|
|
218
306
|
}
|
|
@@ -2,12 +2,13 @@ import { useCallback, useRef, useState } from 'react';
|
|
|
2
2
|
import type { CodeControllerModel } from './code-controller-types';
|
|
3
3
|
import { CodeComposer } from './CodeComposer';
|
|
4
4
|
import { ComposerFooter } from './ComposerFooter';
|
|
5
|
+
import { CodeDraftEmptyState } from './CodeDraftEmptyState';
|
|
5
6
|
import { CodePermissionQueue } from './CodePermissionQueue';
|
|
6
7
|
import { CodeTranscript } from './CodeTranscript';
|
|
7
8
|
import { CodeToastHost } from './CodeToastHost';
|
|
8
9
|
import { applyCodeNotice, dismissCodeToast, type CodeNotice, type CodeToast } from './code-toasts';
|
|
9
10
|
import { CodeWorkspaceHeader } from './CodeWorkspaceHeader';
|
|
10
|
-
import { codeCanResume } from './code-types';
|
|
11
|
+
import { codeCanResume, codeCanRollback, codeResendCopy, codeRollbackRows } from './code-types';
|
|
11
12
|
|
|
12
13
|
type Props = { controller: CodeControllerModel; endpointKey: string; onOpenLocalFile?: ((path: string) => void) | undefined };
|
|
13
14
|
export function CodeWorkbench({ controller: c, endpointKey, onOpenLocalFile }: Props) {
|
|
@@ -26,15 +27,20 @@ export function CodeWorkbench({ controller: c, endpointKey, onOpenLocalFile }: P
|
|
|
26
27
|
const dismiss = useCallback((id: string) => setToasts(current => dismissCodeToast(current, id)), []);
|
|
27
28
|
const archived = c.session?.archivedAt != null;
|
|
28
29
|
const stopping = c.session?.status === 'stopping' || c.operation.kind === 'stopping';
|
|
29
|
-
const busy = c.busy || stopping || c.session?.status === 'starting' || c.session?.status === 'streaming';
|
|
30
|
+
const busy = c.busy || c.working || stopping || c.session?.status === 'starting' || c.session?.status === 'streaming';
|
|
30
31
|
const provider = c.catalog?.providers.find(p => p.id === c.selection.provider);
|
|
31
32
|
const unknownSend = c.operation.kind === 'unknown-send';
|
|
32
|
-
|
|
33
|
+
// A streaming Claude turn takes one follow-up beside Stop; any other busy session offers Stop only.
|
|
34
|
+
const canSend = !c.steering && (c.followUp ? !c.followUpSent && !stopping && !c.pending && !archived && c.operation.kind === 'idle'
|
|
35
|
+
: !busy && !c.pending && !c.creationUnknown && !archived && c.operation.kind === 'idle' && !!provider?.available
|
|
33
36
|
&& !!c.selection.cwd.trim() && !!c.selection.model.trim()
|
|
34
|
-
|
|
37
|
+
// A suspended or recoverably failed session can be sent to: send attaches it first.
|
|
38
|
+
&& (c.selectedId === null || (c.synced && (c.session?.status === 'idle' || (!!c.session && codeCanResume(c.session))))));
|
|
35
39
|
const canStop = !!c.session?.turnId && c.session.capabilities.interrupt && busy && !stopping;
|
|
36
40
|
const canResume = !!c.session && codeCanResume(c.session) && c.synced && !c.pending && !unknownSend;
|
|
37
41
|
const error = c.operation.error || c.error || (actionError?.key === sessionKey ? actionError.message : null);
|
|
42
|
+
const rollbackRows = codeCanRollback(c.session, c.synced, c.operation.kind === 'idle' && !c.steering)
|
|
43
|
+
? codeRollbackRows(c.items, c.session?.rollback?.sinceSequence) : undefined;
|
|
38
44
|
const terminal = [...c.items].reverse().find(item => item.kind === 'turn_cancelled' || item.kind === 'turn_failed' || item.kind === 'turn_completed');
|
|
39
45
|
const failedInput = terminal && terminal.kind !== 'turn_completed'
|
|
40
46
|
? c.items.find(item => item.kind === 'user_message' && item.turnId === terminal.turnId)?.text : undefined;
|
|
@@ -67,12 +73,12 @@ export function CodeWorkbench({ controller: c, endpointKey, onOpenLocalFile }: P
|
|
|
67
73
|
<button type="button" onClick={c.startAnotherSession}>Start another session</button>
|
|
68
74
|
</section>}
|
|
69
75
|
{unknownSend && <section className="code-recovery-strip"
|
|
70
|
-
aria-label={c.resendRequired ? 'Send not started' : 'Unconfirmed send'}>
|
|
71
|
-
<strong>{c.resendRequired ?
|
|
76
|
+
aria-label={c.resendRequired ? (c.resendReason === 'rolled-back' ? 'Send rolled back' : 'Send not started') : 'Unconfirmed send'}>
|
|
77
|
+
<strong>{c.resendRequired ? codeResendCopy(c.resendReason ?? undefined).heading : 'Send outcome not confirmed'}</strong>
|
|
72
78
|
{/* Once the server has spent the key, acceptance is no longer unknown,
|
|
73
79
|
so this must stop offering the weaker, hedged explanation. */}
|
|
74
80
|
<p>{c.resendRequired
|
|
75
|
-
?
|
|
81
|
+
? codeResendCopy(c.resendReason ?? undefined).detail
|
|
76
82
|
: 'Retry uses the original request. It may submit it if the server has not already accepted it.'}</p>
|
|
77
83
|
<pre className="code-retry-preview" aria-label="Original prompt">{c.retryText}</pre>
|
|
78
84
|
<button type="button" disabled={!c.canRetrySameSend || retrying === sessionKey} onClick={() => void retry()}>
|
|
@@ -90,9 +96,12 @@ export function CodeWorkbench({ controller: c, endpointKey, onOpenLocalFile }: P
|
|
|
90
96
|
{/* Directly above the transcript, so a notice sits over the conversation
|
|
91
97
|
rather than over the workspace header and its picker. */}
|
|
92
98
|
<CodeToastHost toasts={toasts} onDismiss={dismiss} />
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
{c.selectedId === null && c.items.length === 0
|
|
100
|
+
? <CodeDraftEmptyState controller={c} />
|
|
101
|
+
: <CodeTranscript items={c.items} provider={c.session?.provider ?? c.selection.provider} sessionKey={sessionKey}
|
|
102
|
+
workingDir={c.session?.cwd ?? c.selection.cwd} loading={c.loading} hasOlderHistory={c.hasOlderHistory}
|
|
103
|
+
loadOlderHistory={c.loadOlderHistory} permissionCount={c.permissions.length} onOpenLocalFile={onOpenLocalFile}
|
|
104
|
+
working={c.working} rollbackRows={rollbackRows} onRollback={itemId => perform(() => c.rollbackSession(itemId))} />}
|
|
96
105
|
<CodePermissionQueue permissions={c.permissions} operations={c.permissionOperations} session={c.session} synced={c.synced} onAnswer={c.answer} />
|
|
97
106
|
<div className="code-composer-dock">
|
|
98
107
|
{failedInput !== undefined && !busy && !archived && <div className="code-input-recovery">
|
|
@@ -102,7 +111,8 @@ export function CodeWorkbench({ controller: c, endpointKey, onOpenLocalFile }: P
|
|
|
102
111
|
</div>}
|
|
103
112
|
<div className="code-composer-surface" aria-label="Code composer controls">
|
|
104
113
|
<CodeComposer key={`composer:${sessionKey}`} inputText={c.input} canSend={canSend} busy={busy} canStop={canStop} stopping={stopping}
|
|
105
|
-
pending={c.pending} readOnly={archived} onInputChange={c.setInput} onSubmit={c.send} onStop={c.stop}
|
|
114
|
+
pending={c.pending} followUp={c.followUp} followUpSent={c.followUpSent} steering={c.steering} readOnly={archived} autoFocus={c.selectedId === null} onInputChange={c.setInput} onSubmit={c.send} onStop={c.stop}
|
|
115
|
+
{...(c.operation.kind === 'rolling-back' ? { pendingLabel: 'Rolling back conversation…' } : {})} />
|
|
106
116
|
<ComposerFooter key={`footer:${sessionKey}`} controller={c} onNotice={notify} />
|
|
107
117
|
</div>
|
|
108
118
|
</div>
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
1
|
import type { CodeControllerModel } from './code-controller-types';
|
|
3
2
|
import { CODE_RUNTIME_LABELS, CODE_SESSION_LABELS } from './code-types';
|
|
4
3
|
|
|
5
4
|
export function CodeWorkspaceHeader({ controller: c }: { controller: CodeControllerModel }) {
|
|
6
|
-
const [picking, setPicking] = useState(false);
|
|
7
|
-
const [error, setError] = useState<string | null>(null);
|
|
8
5
|
const cwd = c.session?.cwd ?? c.selection.cwd;
|
|
9
6
|
const frozen = c.selectedId !== null || c.pending || c.creationUnknown || c.operation.kind !== 'idle';
|
|
10
|
-
|
|
7
|
+
// Shared with CodeDraftEmptyState's picker so only one folder dialog can be open.
|
|
8
|
+
const picking = c.workspacePicking;
|
|
9
|
+
function pick() {
|
|
11
10
|
if (frozen || picking) return;
|
|
12
|
-
|
|
13
|
-
try { await c.pickWorkspace(); }
|
|
14
|
-
catch (err) { setError(err instanceof Error ? err.message : String(err)); }
|
|
15
|
-
finally { setPicking(false); }
|
|
11
|
+
void c.pickWorkspace();
|
|
16
12
|
}
|
|
17
13
|
return <header className="code-workspace-header">
|
|
18
14
|
<div className="code-session-header">
|
|
@@ -23,7 +19,7 @@ export function CodeWorkspaceHeader({ controller: c }: { controller: CodeControl
|
|
|
23
19
|
<div className="code-workspace-primary">
|
|
24
20
|
{frozen ? <span className="code-workspace-chip" title={cwd}>{cwd || 'Workspace not set'}</span>
|
|
25
21
|
: <button type="button" className="code-workspace-picker" aria-label="Choose Code workspace" disabled={picking}
|
|
26
|
-
title={cwd || 'Choose a folder'} onClick={
|
|
22
|
+
title={cwd || 'Choose a folder'} onClick={pick}>
|
|
27
23
|
<span className="code-workspace-picker-label">{picking ? 'Choosing folder…' : cwd || 'Choose workspace'}</span>
|
|
28
24
|
</button>}
|
|
29
25
|
{c.gitInfo?.isRepo && <>
|
|
@@ -32,7 +28,6 @@ export function CodeWorkspaceHeader({ controller: c }: { controller: CodeControl
|
|
|
32
28
|
{c.gitInfo.status.dirty ? `${c.gitInfo.status.changed} changed · ${c.gitInfo.status.untracked} untracked` : 'clean'}
|
|
33
29
|
</span>}
|
|
34
30
|
</>}
|
|
35
|
-
{error && <span className="code-action-error" role="alert">{error}</span>}
|
|
36
31
|
</div>
|
|
37
32
|
</header>;
|
|
38
33
|
}
|
|
@@ -121,6 +121,9 @@ export function ComposerFooter({ controller: c, onNotice }: {
|
|
|
121
121
|
const selection = c.selection;
|
|
122
122
|
const provider = c.catalog?.providers.find(entry => entry.id === selection.provider);
|
|
123
123
|
const capabilities = c.session?.capabilities ?? provider?.capabilities;
|
|
124
|
+
// Claude switches permission modes live, so its menu follows today's catalog rather
|
|
125
|
+
// than the snapshot a session was created with; other runtimes keep their snapshot.
|
|
126
|
+
const permissionModes = (selection.provider === 'claude' ? provider?.capabilities : capabilities)?.permissionModes ?? [];
|
|
124
127
|
const locked = c.selectedId !== null;
|
|
125
128
|
const disabled = c.pending || c.busy || c.creationUnknown || c.operation.kind !== 'idle' || (locked && (!c.synced || c.session?.status !== 'idle' || c.session?.archivedAt !== null));
|
|
126
129
|
const [saving, setSaving] = useState(false);
|
|
@@ -183,7 +186,7 @@ export function ComposerFooter({ controller: c, onNotice }: {
|
|
|
183
186
|
}} />
|
|
184
187
|
<CodeFooterMenu label="Permission" value={selection.permissionMode} displayValue={CODE_POLICY_LABELS[selection.permissionMode]}
|
|
185
188
|
disabled={controlsDisabled || !capabilities?.permissions}
|
|
186
|
-
options={
|
|
189
|
+
options={permissionModes.map(value => ({ value, label: CODE_POLICY_LABELS[value], detail: CODE_POLICY_DETAILS[value] }))}
|
|
187
190
|
onChange={value => void change({ permissionMode: value })} />
|
|
188
191
|
<span className="code-composer-footer-spacer" aria-hidden="true" />
|
|
189
192
|
<ContextUsageMeter usage={c.session?.contextUsage} />
|
|
@@ -196,6 +199,11 @@ export function ComposerFooter({ controller: c, onNotice }: {
|
|
|
196
199
|
className="code-footer-effort" displayValue={selection.effort ?? 'Default'}
|
|
197
200
|
options={[{ value: '', label: 'Native default' }, ...modelEfforts.map(value => ({ value, label: value }))]}
|
|
198
201
|
disabled={controlsDisabled} onChange={value => void change({ effort: value || null })} />}
|
|
202
|
+
{selection.provider === 'claude' && <CodeFooterMenu label="Thinking" value={selection.thinking === false ? 'off' : 'on'}
|
|
203
|
+
className="code-footer-thinking"
|
|
204
|
+
options={[{ value: 'on', label: 'Thinking on', detail: 'Adaptive thinking with summaries' },
|
|
205
|
+
{ value: 'off', label: 'Thinking off', detail: 'Answer without extended thinking' }]}
|
|
206
|
+
disabled={controlsDisabled} onChange={value => void change({ thinking: value === 'on' })} />}
|
|
199
207
|
{locked && <button type="button" className="code-inline-action" onClick={c.newSession}>New session</button>}
|
|
200
208
|
</div>
|
|
201
209
|
{models.length === 0 && provider?.available && <div className="code-selection-notice">This runtime published no models.
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
.code-composer-stop:focus-visible,
|
|
111
111
|
.code-composer-send:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
|
|
112
112
|
|
|
113
|
-
/* Footer: [runtime icon] [permission] ····· [model] [effort] */
|
|
113
|
+
/* Footer: [runtime icon] [permission] ····· [model] [effort] [thinking] */
|
|
114
114
|
.code-composer-footer {
|
|
115
115
|
display: flex;
|
|
116
116
|
gap: 4px;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CodeCreateSessionRequest } from '../../../../src/code-mode/wire';
|
|
2
2
|
import type { CodeDraft, CodeDraftBook } from './code-controller-drafts';
|
|
3
|
+
import type { CodeResendReason } from './code-types';
|
|
3
4
|
|
|
4
5
|
const STORAGE_KEY = 'cli-jaw:code-drafts:v1';
|
|
5
6
|
const WARNING_KEY = 'cli-jaw:code-draft-warnings:v1';
|
|
@@ -13,14 +14,14 @@ const STORAGE_WARNING = 'Draft recovery is unavailable in this tab. Your current
|
|
|
13
14
|
const INCOMPLETE_WARNING = 'Draft recovery is incomplete: some recent text or request state was not saved for reload. Check your draft before sending.';
|
|
14
15
|
const LIMIT_WARNING = 'Draft recovery is incomplete: some text was not saved for reload because this tab reached its recovery limit.';
|
|
15
16
|
|
|
16
|
-
type DraftSelection = Pick<CodeCreateSessionRequest, 'provider' | 'cwd' | 'model' | 'effort' | 'permissionMode'>;
|
|
17
|
+
type DraftSelection = Pick<CodeCreateSessionRequest, 'provider' | 'cwd' | 'model' | 'effort' | 'permissionMode' | 'thinking'>;
|
|
17
18
|
export interface StoredCodeDraft {
|
|
18
19
|
input: string;
|
|
19
20
|
edit: number;
|
|
20
21
|
selection: DraftSelection;
|
|
21
22
|
selectionEdit: number;
|
|
22
23
|
creating: boolean;
|
|
23
|
-
retry: { text: string; key: string; edit: number; resend?: boolean } | null;
|
|
24
|
+
retry: { text: string; key: string; edit: number; resend?: boolean; resendReason?: CodeResendReason } | null;
|
|
24
25
|
stop: { turnId: string; epoch: number } | null;
|
|
25
26
|
}
|
|
26
27
|
export interface StoredCodeEndpoint {
|
|
@@ -41,18 +42,23 @@ function parseDraft(value: unknown): StoredCodeDraft | null {
|
|
|
41
42
|
if (!object(selection) || !['codex-app', 'claude', 'cursor', 'grok'].includes(String(selection['provider']))
|
|
42
43
|
|| !text(selection['cwd'], 4096) || !text(selection['model'], 1024)
|
|
43
44
|
|| !(selection['effort'] === null || text(selection['effort'], 80))
|
|
44
|
-
|| !['ask', 'auto', 'read-only'].includes(String(selection['permissionMode']))
|
|
45
|
+
|| !['ask', 'auto', 'read-only', 'plan', 'accept-edits', 'dont-ask', 'auto-review'].includes(String(selection['permissionMode']))
|
|
46
|
+
|| !(selection['thinking'] === undefined || typeof selection['thinking'] === 'boolean')) return null;
|
|
45
47
|
const retry = value['retry'], stop = value['stop'];
|
|
46
48
|
if (retry !== null && (!object(retry) || !text(retry['text'], MAX_TEXT_CHARS)
|
|
47
49
|
|| !text(retry['key'], 240) || !retry['key'] || !count(retry['edit']) || retry['edit'] > value['edit'])) return null;
|
|
48
50
|
if (stop !== null && (!object(stop) || !text(stop['turnId'], 240) || !stop['turnId'] || !count(stop['epoch']))) return null;
|
|
49
51
|
return {
|
|
50
52
|
input: value['input'], edit: value['edit'], selectionEdit: value['selectionEdit'], creating: value['creating'],
|
|
51
|
-
selection: { provider: selection['provider'] as DraftSelection['provider'], cwd: selection['cwd'], model: selection['model'], effort: selection['effort'], permissionMode: selection['permissionMode'] as DraftSelection['permissionMode']
|
|
53
|
+
selection: { provider: selection['provider'] as DraftSelection['provider'], cwd: selection['cwd'], model: selection['model'], effort: selection['effort'], permissionMode: selection['permissionMode'] as DraftSelection['permissionMode'],
|
|
54
|
+
// Optional: drafts stored before the Thinking switch carry none.
|
|
55
|
+
...(typeof selection['thinking'] === 'boolean' ? { thinking: selection['thinking'] } : {}) },
|
|
52
56
|
// `resend` is optional on purpose: a draft stored before this field existed
|
|
53
57
|
// must still parse, or recovery is wiped instead of downgraded.
|
|
54
58
|
retry: retry === null ? null : { text: retry['text'] as string, key: retry['key'] as string, edit: retry['edit'] as number,
|
|
55
|
-
...(retry['resend'] === true ? { resend: true } : {})
|
|
59
|
+
...(retry['resend'] === true ? { resend: true } : {}),
|
|
60
|
+
// An unknown reason downgrades to the generic spent-key copy instead of wiping recovery.
|
|
61
|
+
...(retry['resend'] === true && retry['resendReason'] === 'rolled-back' ? { resendReason: 'rolled-back' as const } : {}) },
|
|
56
62
|
stop: stop === null ? null : { turnId: stop['turnId'] as string, epoch: stop['epoch'] as number },
|
|
57
63
|
};
|
|
58
64
|
}
|
|
@@ -116,11 +122,13 @@ export function loadCodeDraftStorage(storage: Storage, endpoint: string): { data
|
|
|
116
122
|
function pack(draft: CodeDraft): StoredCodeDraft | null {
|
|
117
123
|
// Whitelist draft intent only. No permission answers, errors,
|
|
118
124
|
// credentials, transcript, native cursor, metadata revision or live cursor.
|
|
119
|
-
const { provider, cwd, model, effort, permissionMode } = draft.selection;
|
|
120
|
-
return parseDraft({ input: draft.input, edit: draft.edit, selection: { provider, cwd, model, effort, permissionMode
|
|
125
|
+
const { provider, cwd, model, effort, permissionMode, thinking } = draft.selection;
|
|
126
|
+
return parseDraft({ input: draft.input, edit: draft.edit, selection: { provider, cwd, model, effort, permissionMode,
|
|
127
|
+
...(thinking === undefined ? {} : { thinking }) }, selectionEdit: draft.selectionEdit,
|
|
121
128
|
creating: draft.createUnknown || draft.operation.kind === 'creating',
|
|
122
129
|
retry: draft.retry ? { text: draft.retry.text, key: draft.retry.key, edit: draft.retry.edit,
|
|
123
|
-
...(draft.retry.resend ? { resend: true } : {})
|
|
130
|
+
...(draft.retry.resend ? { resend: true } : {}),
|
|
131
|
+
...(draft.retry.resend && draft.retry.resendReason ? { resendReason: draft.retry.resendReason } : {}) } : null,
|
|
124
132
|
stop: draft.stopTarget ? { turnId: draft.stopTarget.turnId, epoch: draft.stopTarget.epoch } : null });
|
|
125
133
|
}
|
|
126
134
|
export function saveCodeDraftStorage(storage: Storage, endpoint: string, book: CodeDraftBook): string | null {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CodeCreateSessionRequest, CodeModelCatalog, CodeSessionInfo } from '../../../../src/code-mode/wire';
|
|
2
2
|
import type { CodeControllerModel } from './code-controller-types';
|
|
3
3
|
import { browserCodeDraftStorage, loadCodeDraftStorage, saveCodeDraftStorage, type StoredCodeDraft } from './code-controller-draft-storage';
|
|
4
|
+
import { codeResendCopy, type CodeResendReason } from './code-types';
|
|
4
5
|
|
|
5
6
|
export interface CodeDraft {
|
|
6
7
|
input: string;
|
|
@@ -11,13 +12,30 @@ export interface CodeDraft {
|
|
|
11
12
|
/**
|
|
12
13
|
* `resend` marks an attempt whose key the server already spent. Acceptance is
|
|
13
14
|
* then known — it failed — so the surfaces that describe an unconfirmed send
|
|
14
|
-
* must stop hedging.
|
|
15
|
+
* must stop hedging. `resendReason` says why, when it was not the server's own settlement.
|
|
15
16
|
*/
|
|
16
|
-
retry: { text: string; key: string; edit: number; resend?: boolean } | null;
|
|
17
|
+
retry: { text: string; key: string; edit: number; resend?: boolean; resendReason?: CodeResendReason } | null;
|
|
17
18
|
createUnknown: boolean;
|
|
18
19
|
requiredSequence: number;
|
|
19
20
|
stopTarget: { turnId: string; epoch: number; outcome: 'pending' | 'unknown' | 'retryable' | 'accepted' } | null;
|
|
20
21
|
permissionOperations: CodeControllerModel['permissionOperations'];
|
|
22
|
+
/**
|
|
23
|
+
* The clientTurnKey of a send whose turn has not visibly started or ended yet.
|
|
24
|
+
* Bridges the gap between pressing Send and the server reporting the turn, so
|
|
25
|
+
* the session reads as working the whole time. In-memory only: a reloaded page
|
|
26
|
+
* must not show a phantom spinner.
|
|
27
|
+
*/
|
|
28
|
+
awaitingTurn: string | null;
|
|
29
|
+
/**
|
|
30
|
+
* A follow-up for the running Claude turn, captured with that turn's owner. In-memory only
|
|
31
|
+
* and never the send's `operation`/`retry`: it settles on its receipt or on its own
|
|
32
|
+
* `user_message`, and `unknown` means acceptance could not be confirmed (never resent).
|
|
33
|
+
*/
|
|
34
|
+
steer: {
|
|
35
|
+
key: string; text: string; edit: number; turnId: string; epoch: number; state: 'sending' | 'unknown';
|
|
36
|
+
/** The server answered `steer_outcome_unknown`: Claude may have it, but the transcript never will. */
|
|
37
|
+
unrecorded?: boolean;
|
|
38
|
+
} | null;
|
|
21
39
|
}
|
|
22
40
|
export interface CodeDraftBook {
|
|
23
41
|
endpoint: string;
|
|
@@ -37,7 +55,7 @@ const tabBooks = new WeakMap<Storage, Map<string, CodeDraftBook>>();
|
|
|
37
55
|
export function createCodeDraft(selection: CodeCreateSessionRequest): CodeDraft {
|
|
38
56
|
return { input: '', edit: 0, selection: { ...selection }, selectionEdit: 0,
|
|
39
57
|
operation: { kind: 'idle', error: null }, retry: null, createUnknown: false, requiredSequence: 0,
|
|
40
|
-
stopTarget: null, permissionOperations: {} };
|
|
58
|
+
stopTarget: null, permissionOperations: {}, awaitingTurn: null, steer: null };
|
|
41
59
|
}
|
|
42
60
|
function restoreDraft(saved: StoredCodeDraft): CodeDraft {
|
|
43
61
|
const draft = createCodeDraft(saved.selection);
|
|
@@ -46,7 +64,7 @@ function restoreDraft(saved: StoredCodeDraft): CodeDraft {
|
|
|
46
64
|
draft.retry = saved.retry;
|
|
47
65
|
if (saved.creating) draft.operation = { kind: 'creating', error: 'Creation was interrupted by reload. The original session may exist; no request has been retried.' };
|
|
48
66
|
if (saved.retry) draft.operation = { kind: 'unknown-send', error: saved.retry.resend
|
|
49
|
-
?
|
|
67
|
+
? codeResendCopy(saved.retry.resendReason).error
|
|
50
68
|
: 'The original message has not been reconciled after reload. It will not be sent automatically.' };
|
|
51
69
|
if (saved.stop) {
|
|
52
70
|
draft.stopTarget = { ...saved.stop, outcome: 'unknown' };
|
|
@@ -75,13 +93,16 @@ export function persistCodeDraftBook(book: CodeDraftBook): void {
|
|
|
75
93
|
}
|
|
76
94
|
export function sessionSelection(session: CodeSessionInfo): CodeCreateSessionRequest {
|
|
77
95
|
return { provider: session.provider, cwd: session.cwd, model: session.model,
|
|
78
|
-
effort: session.effort, permissionMode: session.permissionMode
|
|
96
|
+
effort: session.effort, permissionMode: session.permissionMode,
|
|
97
|
+
...(session.thinking === null ? {} : { thinking: session.thinking }) };
|
|
79
98
|
}
|
|
80
99
|
export function catalogSelection(catalog: CodeModelCatalog, previous: CodeCreateSessionRequest, changedProvider = false): CodeCreateSessionRequest {
|
|
81
100
|
const provider = catalog.providers.find(row => row.id === previous.provider);
|
|
82
101
|
if (!provider) return previous;
|
|
83
102
|
const capabilities = provider.capabilities;
|
|
84
|
-
|
|
103
|
+
// Thinking is a Claude switch; it must not travel into a create for another runtime.
|
|
104
|
+
const { thinking, ...rest } = previous;
|
|
105
|
+
return { ...rest, ...(provider.id === 'claude' && thinking !== undefined ? { thinking } : {}),
|
|
85
106
|
model: !changedProvider && provider.models.includes(previous.model) ? previous.model : provider.defaultModel,
|
|
86
107
|
effort: !changedProvider && previous.effort !== null && capabilities.efforts.includes(previous.effort)
|
|
87
108
|
? previous.effort : null,
|
|
@@ -91,6 +112,12 @@ export function catalogSelection(catalog: CodeModelCatalog, previous: CodeCreate
|
|
|
91
112
|
: capabilities.permissionModes.includes('auto') ? 'auto' : 'read-only',
|
|
92
113
|
};
|
|
93
114
|
}
|
|
115
|
+
/** The follow-up is in the transcript: clear the input only if it was not edited since. */
|
|
116
|
+
export function acknowledgeCodeSteer(draft: CodeDraft, key: string): void {
|
|
117
|
+
if (draft.steer?.key !== key) return;
|
|
118
|
+
if (draft.edit === draft.steer.edit) { draft.input = ''; draft.edit++; }
|
|
119
|
+
draft.steer = null;
|
|
120
|
+
}
|
|
94
121
|
export function acknowledgeCodeSend(draft: CodeDraft, key: string): void {
|
|
95
122
|
if (draft.retry?.key !== key) return;
|
|
96
123
|
if (draft.edit === draft.retry.edit) { draft.input = ''; draft.edit++; }
|