@butlerw/vellum 0.1.0
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/LICENSE +21 -0
- package/README.md +411 -0
- package/__fixtures__/responses/code-generation.json +42 -0
- package/__fixtures__/responses/error-response.json +20 -0
- package/__fixtures__/responses/hello-world.json +32 -0
- package/dist/auth-6MCXESOH.js +26 -0
- package/dist/chunk-SECXJGWA.js +597 -0
- package/dist/index.js +34023 -0
- package/package.json +67 -0
- package/src/__tests__/commands.e2e.test.ts +728 -0
- package/src/__tests__/credentials.test.ts +713 -0
- package/src/__tests__/mode-e2e.test.ts +391 -0
- package/src/__tests__/tui-integration.test.tsx +1271 -0
- package/src/agents/__tests__/task-persistence.test.ts +235 -0
- package/src/agents/commands/delegate.ts +240 -0
- package/src/agents/commands/index.ts +10 -0
- package/src/agents/commands/resume.ts +335 -0
- package/src/agents/index.ts +29 -0
- package/src/agents/task-persistence.ts +272 -0
- package/src/agents/task-resumption.ts +242 -0
- package/src/app.tsx +4737 -0
- package/src/commands/__tests__/.gitkeep +1 -0
- package/src/commands/__tests__/agents.test.ts +606 -0
- package/src/commands/__tests__/auth.test.ts +626 -0
- package/src/commands/__tests__/autocomplete.test.ts +683 -0
- package/src/commands/__tests__/batch.test.ts +287 -0
- package/src/commands/__tests__/chain-pipe-parser.test.ts +654 -0
- package/src/commands/__tests__/completion.test.ts +238 -0
- package/src/commands/__tests__/core.test.ts +363 -0
- package/src/commands/__tests__/executor.test.ts +496 -0
- package/src/commands/__tests__/exit-codes.test.ts +220 -0
- package/src/commands/__tests__/init.test.ts +243 -0
- package/src/commands/__tests__/language.test.ts +353 -0
- package/src/commands/__tests__/mode-cli.test.ts +667 -0
- package/src/commands/__tests__/model.test.ts +277 -0
- package/src/commands/__tests__/parser.test.ts +493 -0
- package/src/commands/__tests__/performance.bench.ts +380 -0
- package/src/commands/__tests__/registry.test.ts +534 -0
- package/src/commands/__tests__/resume.test.ts +449 -0
- package/src/commands/__tests__/security.test.ts +845 -0
- package/src/commands/__tests__/stream-json.test.ts +372 -0
- package/src/commands/__tests__/user-commands.test.ts +597 -0
- package/src/commands/adapters.ts +267 -0
- package/src/commands/agent.ts +395 -0
- package/src/commands/agents/generate.ts +506 -0
- package/src/commands/agents/index.ts +272 -0
- package/src/commands/agents/show.ts +271 -0
- package/src/commands/agents/validate.ts +387 -0
- package/src/commands/auth.ts +883 -0
- package/src/commands/autocomplete.ts +480 -0
- package/src/commands/batch/command.ts +388 -0
- package/src/commands/batch/executor.ts +361 -0
- package/src/commands/batch/index.ts +12 -0
- package/src/commands/commit.ts +235 -0
- package/src/commands/completion/index.ts +371 -0
- package/src/commands/condense.ts +191 -0
- package/src/commands/config.ts +344 -0
- package/src/commands/context-provider.ts +173 -0
- package/src/commands/copy.ts +329 -0
- package/src/commands/core/clear.ts +38 -0
- package/src/commands/core/exit.ts +43 -0
- package/src/commands/core/help.ts +354 -0
- package/src/commands/core/index.ts +15 -0
- package/src/commands/cost.ts +179 -0
- package/src/commands/credentials.tsx +618 -0
- package/src/commands/custom-agents/__tests__/custom-agents.test.ts +709 -0
- package/src/commands/custom-agents/create.ts +377 -0
- package/src/commands/custom-agents/export.ts +135 -0
- package/src/commands/custom-agents/import.ts +199 -0
- package/src/commands/custom-agents/index.ts +372 -0
- package/src/commands/custom-agents/info.ts +318 -0
- package/src/commands/custom-agents/list.ts +267 -0
- package/src/commands/custom-agents/validate.ts +388 -0
- package/src/commands/diff-mode.ts +241 -0
- package/src/commands/env.ts +53 -0
- package/src/commands/executor.ts +579 -0
- package/src/commands/exit-codes.ts +202 -0
- package/src/commands/index.ts +701 -0
- package/src/commands/init/index.ts +15 -0
- package/src/commands/init/prompts.ts +366 -0
- package/src/commands/init/templates/commands-readme.md +80 -0
- package/src/commands/init/templates/example-command.md +79 -0
- package/src/commands/init/templates/example-skill.md +168 -0
- package/src/commands/init/templates/example-workflow.md +101 -0
- package/src/commands/init/templates/prompts-readme.md +52 -0
- package/src/commands/init/templates/rules-readme.md +63 -0
- package/src/commands/init/templates/skills-readme.md +83 -0
- package/src/commands/init/templates/workflows-readme.md +94 -0
- package/src/commands/init.ts +391 -0
- package/src/commands/install.ts +90 -0
- package/src/commands/language.ts +191 -0
- package/src/commands/loaders/.gitkeep +1 -0
- package/src/commands/lsp.ts +199 -0
- package/src/commands/markdown-commands.ts +253 -0
- package/src/commands/mcp.ts +588 -0
- package/src/commands/memory/export.ts +341 -0
- package/src/commands/memory/index.ts +148 -0
- package/src/commands/memory/list.ts +261 -0
- package/src/commands/memory/search.ts +346 -0
- package/src/commands/memory/utils.ts +15 -0
- package/src/commands/metrics.ts +75 -0
- package/src/commands/migrate/index.ts +16 -0
- package/src/commands/migrate/prompts.ts +477 -0
- package/src/commands/mode.ts +331 -0
- package/src/commands/model.ts +298 -0
- package/src/commands/onboard.ts +205 -0
- package/src/commands/open.ts +169 -0
- package/src/commands/output/stream-json.ts +373 -0
- package/src/commands/parser/chain-parser.ts +370 -0
- package/src/commands/parser/index.ts +29 -0
- package/src/commands/parser/pipe-parser.ts +480 -0
- package/src/commands/parser.ts +588 -0
- package/src/commands/persistence.ts +355 -0
- package/src/commands/progress.ts +18 -0
- package/src/commands/prompt/index.ts +17 -0
- package/src/commands/prompt/validate.ts +621 -0
- package/src/commands/prompt-priority.ts +401 -0
- package/src/commands/registry.ts +374 -0
- package/src/commands/sandbox/index.ts +131 -0
- package/src/commands/security/index.ts +21 -0
- package/src/commands/security/input-sanitizer.ts +168 -0
- package/src/commands/security/permission-checker.ts +456 -0
- package/src/commands/security/sensitive-data.ts +350 -0
- package/src/commands/session/delete.ts +38 -0
- package/src/commands/session/export.ts +39 -0
- package/src/commands/session/index.ts +26 -0
- package/src/commands/session/list.ts +26 -0
- package/src/commands/session/resume.ts +562 -0
- package/src/commands/session/search.ts +434 -0
- package/src/commands/session/show.ts +26 -0
- package/src/commands/settings.ts +368 -0
- package/src/commands/setup.ts +23 -0
- package/src/commands/shell/index.ts +16 -0
- package/src/commands/shell/setup.ts +422 -0
- package/src/commands/shell-init.ts +50 -0
- package/src/commands/shell-integration/index.ts +194 -0
- package/src/commands/skill.ts +1220 -0
- package/src/commands/spec.ts +558 -0
- package/src/commands/status.ts +246 -0
- package/src/commands/theme.ts +211 -0
- package/src/commands/think.ts +551 -0
- package/src/commands/trust.ts +211 -0
- package/src/commands/tutorial.ts +522 -0
- package/src/commands/types.ts +512 -0
- package/src/commands/update.ts +274 -0
- package/src/commands/usage.ts +213 -0
- package/src/commands/user-commands.ts +630 -0
- package/src/commands/utils.ts +142 -0
- package/src/commands/vim.ts +152 -0
- package/src/commands/workflow.ts +257 -0
- package/src/components/header.tsx +25 -0
- package/src/components/input.tsx +25 -0
- package/src/components/message-list.tsx +32 -0
- package/src/components/status-bar.tsx +23 -0
- package/src/index.tsx +614 -0
- package/src/onboarding/__tests__/tutorial.test.ts +740 -0
- package/src/onboarding/index.ts +69 -0
- package/src/onboarding/tips/index.ts +9 -0
- package/src/onboarding/tips/tip-engine.ts +459 -0
- package/src/onboarding/tutorial/index.ts +88 -0
- package/src/onboarding/tutorial/lessons/basics.ts +151 -0
- package/src/onboarding/tutorial/lessons/index.ts +151 -0
- package/src/onboarding/tutorial/lessons/modes.ts +230 -0
- package/src/onboarding/tutorial/lessons/tools.ts +172 -0
- package/src/onboarding/tutorial/progress-tracker.ts +350 -0
- package/src/onboarding/tutorial/storage.ts +249 -0
- package/src/onboarding/tutorial/tutorial-system.ts +462 -0
- package/src/onboarding/tutorial/types.ts +310 -0
- package/src/orchestrator-singleton.ts +129 -0
- package/src/shutdown.ts +33 -0
- package/src/test/e2e/assertions.ts +267 -0
- package/src/test/e2e/fixtures.ts +204 -0
- package/src/test/e2e/harness.ts +575 -0
- package/src/test/e2e/index.ts +57 -0
- package/src/test/e2e/types.ts +228 -0
- package/src/test/fixtures/__tests__/fake-response-loader.test.ts +314 -0
- package/src/test/fixtures/fake-response-loader.ts +314 -0
- package/src/test/fixtures/index.ts +20 -0
- package/src/tui/__tests__/mcp-panel.test.tsx +82 -0
- package/src/tui/__tests__/mcp-wiring.test.tsx +78 -0
- package/src/tui/__tests__/mode-components.test.tsx +395 -0
- package/src/tui/__tests__/permission-ask-flow.test.tsx +138 -0
- package/src/tui/__tests__/sidebar-panel-data.test.tsx +148 -0
- package/src/tui/__tests__/tools-panel-hotkeys.test.tsx +41 -0
- package/src/tui/adapters/agent-adapter.ts +1008 -0
- package/src/tui/adapters/index.ts +48 -0
- package/src/tui/adapters/message-adapter.ts +315 -0
- package/src/tui/adapters/persistence-bridge.ts +331 -0
- package/src/tui/adapters/session-adapter.ts +419 -0
- package/src/tui/buffered-stdout.ts +223 -0
- package/src/tui/components/AgentProgress.tsx +424 -0
- package/src/tui/components/Banner/AsciiArt.ts +160 -0
- package/src/tui/components/Banner/Banner.tsx +355 -0
- package/src/tui/components/Banner/ShimmerContext.tsx +131 -0
- package/src/tui/components/Banner/ShimmerText.tsx +193 -0
- package/src/tui/components/Banner/TypeWriterGradient.tsx +321 -0
- package/src/tui/components/Banner/index.ts +61 -0
- package/src/tui/components/Banner/useShimmer.ts +241 -0
- package/src/tui/components/ChatView.tsx +11 -0
- package/src/tui/components/Checkpoint/CheckpointDiffView.tsx +371 -0
- package/src/tui/components/Checkpoint/SnapshotCheckpointPanel.tsx +440 -0
- package/src/tui/components/Checkpoint/index.ts +19 -0
- package/src/tui/components/CostDisplay.tsx +226 -0
- package/src/tui/components/InitErrorBanner.tsx +122 -0
- package/src/tui/components/Input/Autocomplete.tsx +603 -0
- package/src/tui/components/Input/EnhancedCommandInput.tsx +471 -0
- package/src/tui/components/Input/HighlightedText.tsx +236 -0
- package/src/tui/components/Input/MentionAutocomplete.tsx +375 -0
- package/src/tui/components/Input/TextInput.tsx +1002 -0
- package/src/tui/components/Input/__tests__/Autocomplete.test.tsx +374 -0
- package/src/tui/components/Input/__tests__/TextInput.test.tsx +241 -0
- package/src/tui/components/Input/__tests__/highlight.test.ts +219 -0
- package/src/tui/components/Input/__tests__/slash-command-utils.test.ts +104 -0
- package/src/tui/components/Input/highlight.ts +362 -0
- package/src/tui/components/Input/index.ts +36 -0
- package/src/tui/components/Input/slash-command-utils.ts +135 -0
- package/src/tui/components/Layout.tsx +432 -0
- package/src/tui/components/McpPanel.tsx +137 -0
- package/src/tui/components/MemoryPanel.tsx +448 -0
- package/src/tui/components/Messages/CodeBlock.tsx +527 -0
- package/src/tui/components/Messages/DiffView.tsx +679 -0
- package/src/tui/components/Messages/ImageReference.tsx +89 -0
- package/src/tui/components/Messages/MarkdownBlock.tsx +228 -0
- package/src/tui/components/Messages/MarkdownRenderer.tsx +498 -0
- package/src/tui/components/Messages/MessageBubble.tsx +270 -0
- package/src/tui/components/Messages/MessageList.tsx +1719 -0
- package/src/tui/components/Messages/StreamingText.tsx +216 -0
- package/src/tui/components/Messages/ThinkingBlock.tsx +408 -0
- package/src/tui/components/Messages/ToolResultPreview.tsx +243 -0
- package/src/tui/components/Messages/__tests__/CodeBlock.test.tsx +296 -0
- package/src/tui/components/Messages/__tests__/DiffView.test.tsx +239 -0
- package/src/tui/components/Messages/__tests__/MarkdownRenderer.test.tsx +303 -0
- package/src/tui/components/Messages/__tests__/MessageBubble.test.tsx +268 -0
- package/src/tui/components/Messages/__tests__/MessageList.test.tsx +324 -0
- package/src/tui/components/Messages/__tests__/StreamingText.test.tsx +215 -0
- package/src/tui/components/Messages/index.ts +25 -0
- package/src/tui/components/ModeIndicator.tsx +177 -0
- package/src/tui/components/ModeSelector.tsx +216 -0
- package/src/tui/components/ModelSelector.tsx +339 -0
- package/src/tui/components/OnboardingWizard.tsx +670 -0
- package/src/tui/components/PhaseProgressIndicator.tsx +270 -0
- package/src/tui/components/RateLimitIndicator.tsx +82 -0
- package/src/tui/components/ScreenReaderLayout.tsx +295 -0
- package/src/tui/components/SettingsPanel.tsx +643 -0
- package/src/tui/components/Sidebar/SystemStatusPanel.tsx +284 -0
- package/src/tui/components/Sidebar/index.ts +9 -0
- package/src/tui/components/Status/ModelStatusBar.tsx +270 -0
- package/src/tui/components/Status/index.ts +12 -0
- package/src/tui/components/StatusBar/AgentModeIndicator.tsx +257 -0
- package/src/tui/components/StatusBar/ContextProgress.tsx +167 -0
- package/src/tui/components/StatusBar/FileChangesIndicator.tsx +62 -0
- package/src/tui/components/StatusBar/GitIndicator.tsx +89 -0
- package/src/tui/components/StatusBar/HeaderBar.tsx +126 -0
- package/src/tui/components/StatusBar/ModelIndicator.tsx +157 -0
- package/src/tui/components/StatusBar/PersistenceStatusIndicator.tsx +210 -0
- package/src/tui/components/StatusBar/ResilienceIndicator.tsx +106 -0
- package/src/tui/components/StatusBar/SandboxIndicator.tsx +167 -0
- package/src/tui/components/StatusBar/StatusBar.tsx +368 -0
- package/src/tui/components/StatusBar/ThinkingModeIndicator.tsx +170 -0
- package/src/tui/components/StatusBar/TokenBreakdown.tsx +246 -0
- package/src/tui/components/StatusBar/TokenCounter.tsx +135 -0
- package/src/tui/components/StatusBar/TrustModeIndicator.tsx +130 -0
- package/src/tui/components/StatusBar/WorkspaceIndicator.tsx +86 -0
- package/src/tui/components/StatusBar/__tests__/AgentModeIndicator.test.tsx +193 -0
- package/src/tui/components/StatusBar/__tests__/StatusBar.test.tsx +729 -0
- package/src/tui/components/StatusBar/index.ts +60 -0
- package/src/tui/components/TipBanner.tsx +115 -0
- package/src/tui/components/TodoItem.tsx +208 -0
- package/src/tui/components/TodoPanel.tsx +455 -0
- package/src/tui/components/Tools/ApprovalQueue.tsx +407 -0
- package/src/tui/components/Tools/OptionSelector.tsx +160 -0
- package/src/tui/components/Tools/PermissionDialog.tsx +286 -0
- package/src/tui/components/Tools/ToolParams.tsx +483 -0
- package/src/tui/components/Tools/ToolsPanel.tsx +178 -0
- package/src/tui/components/Tools/__tests__/PermissionDialog.test.tsx +510 -0
- package/src/tui/components/Tools/__tests__/ToolParams.test.tsx +432 -0
- package/src/tui/components/Tools/index.ts +21 -0
- package/src/tui/components/TrustPrompt.tsx +279 -0
- package/src/tui/components/UpdateBanner.tsx +166 -0
- package/src/tui/components/VimModeIndicator.tsx +112 -0
- package/src/tui/components/backtrack/BacktrackControls.tsx +402 -0
- package/src/tui/components/backtrack/index.ts +13 -0
- package/src/tui/components/common/AutoApprovalStatus.tsx +251 -0
- package/src/tui/components/common/CostWarning.tsx +294 -0
- package/src/tui/components/common/DynamicShortcutHints.tsx +209 -0
- package/src/tui/components/common/EnhancedLoadingIndicator.tsx +305 -0
- package/src/tui/components/common/ErrorBoundary.tsx +140 -0
- package/src/tui/components/common/GradientText.tsx +224 -0
- package/src/tui/components/common/HotkeyHelpModal.tsx +193 -0
- package/src/tui/components/common/HotkeyHints.tsx +70 -0
- package/src/tui/components/common/MaxSizedBox.tsx +354 -0
- package/src/tui/components/common/NewMessagesBadge.tsx +65 -0
- package/src/tui/components/common/ProtectedFileLegend.tsx +89 -0
- package/src/tui/components/common/ScrollIndicator.tsx +160 -0
- package/src/tui/components/common/Spinner.tsx +342 -0
- package/src/tui/components/common/StreamingIndicator.tsx +316 -0
- package/src/tui/components/common/VirtualizedList/VirtualizedList.tsx +428 -0
- package/src/tui/components/common/VirtualizedList/hooks/index.ts +19 -0
- package/src/tui/components/common/VirtualizedList/hooks/useBatchedScroll.ts +64 -0
- package/src/tui/components/common/VirtualizedList/hooks/useScrollAnchor.ts +290 -0
- package/src/tui/components/common/VirtualizedList/hooks/useVirtualization.ts +340 -0
- package/src/tui/components/common/VirtualizedList/index.ts +30 -0
- package/src/tui/components/common/VirtualizedList/types.ts +107 -0
- package/src/tui/components/common/__tests__/NewMessagesBadge.test.tsx +74 -0
- package/src/tui/components/common/__tests__/ScrollIndicator.test.tsx +193 -0
- package/src/tui/components/common/index.ts +110 -0
- package/src/tui/components/index.ts +79 -0
- package/src/tui/components/session/CheckpointPanel.tsx +323 -0
- package/src/tui/components/session/RollbackDialog.tsx +169 -0
- package/src/tui/components/session/SessionItem.tsx +136 -0
- package/src/tui/components/session/SessionListPanel.tsx +252 -0
- package/src/tui/components/session/SessionPicker.tsx +449 -0
- package/src/tui/components/session/SessionPreview.tsx +240 -0
- package/src/tui/components/session/__tests__/session.test.tsx +408 -0
- package/src/tui/components/session/index.ts +28 -0
- package/src/tui/components/session/types.ts +116 -0
- package/src/tui/components/theme/__tests__/tokens.test.ts +471 -0
- package/src/tui/components/theme/index.ts +227 -0
- package/src/tui/components/theme/tokens.ts +484 -0
- package/src/tui/config/defaults.ts +134 -0
- package/src/tui/config/index.ts +17 -0
- package/src/tui/context/AnimationContext.tsx +284 -0
- package/src/tui/context/AppContext.tsx +349 -0
- package/src/tui/context/BracketedPasteContext.tsx +372 -0
- package/src/tui/context/LspContext.tsx +192 -0
- package/src/tui/context/McpContext.tsx +325 -0
- package/src/tui/context/MessagesContext.tsx +870 -0
- package/src/tui/context/OverflowContext.tsx +213 -0
- package/src/tui/context/RateLimitContext.tsx +108 -0
- package/src/tui/context/ResilienceContext.tsx +275 -0
- package/src/tui/context/RootProvider.tsx +136 -0
- package/src/tui/context/ScrollContext.tsx +331 -0
- package/src/tui/context/ToolsContext.tsx +702 -0
- package/src/tui/context/__tests__/BracketedPasteContext.test.tsx +416 -0
- package/src/tui/context/index.ts +140 -0
- package/src/tui/enterprise-integration.ts +282 -0
- package/src/tui/hooks/__tests__/useBacktrack.test.tsx +138 -0
- package/src/tui/hooks/__tests__/useBracketedPaste.test.tsx +222 -0
- package/src/tui/hooks/__tests__/useCopyMode.test.tsx +336 -0
- package/src/tui/hooks/__tests__/useHotkeys.ctrl-input.test.tsx +96 -0
- package/src/tui/hooks/__tests__/useHotkeys.test.tsx +454 -0
- package/src/tui/hooks/__tests__/useInputHistory.test.tsx +660 -0
- package/src/tui/hooks/__tests__/useLineBuffer.test.ts +295 -0
- package/src/tui/hooks/__tests__/useModeController.test.ts +137 -0
- package/src/tui/hooks/__tests__/useModeShortcuts.test.tsx +142 -0
- package/src/tui/hooks/__tests__/useScrollController.test.ts +464 -0
- package/src/tui/hooks/__tests__/useVim.test.tsx +531 -0
- package/src/tui/hooks/index.ts +252 -0
- package/src/tui/hooks/useAgentLoop.ts +712 -0
- package/src/tui/hooks/useAlternateBuffer.ts +398 -0
- package/src/tui/hooks/useAnimatedScrollbar.ts +241 -0
- package/src/tui/hooks/useBacktrack.ts +443 -0
- package/src/tui/hooks/useBracketedPaste.ts +104 -0
- package/src/tui/hooks/useCollapsible.ts +240 -0
- package/src/tui/hooks/useCopyMode.ts +382 -0
- package/src/tui/hooks/useCostSummary.ts +75 -0
- package/src/tui/hooks/useDesktopNotification.ts +414 -0
- package/src/tui/hooks/useDiffMode.ts +44 -0
- package/src/tui/hooks/useFileChangeStats.ts +110 -0
- package/src/tui/hooks/useFileSuggestions.ts +284 -0
- package/src/tui/hooks/useFlickerDetector.ts +250 -0
- package/src/tui/hooks/useGitStatus.ts +200 -0
- package/src/tui/hooks/useHotkeys.ts +579 -0
- package/src/tui/hooks/useImagePaste.ts +114 -0
- package/src/tui/hooks/useInputHighlight.ts +145 -0
- package/src/tui/hooks/useInputHistory.ts +246 -0
- package/src/tui/hooks/useKeyboardScroll.ts +209 -0
- package/src/tui/hooks/useLineBuffer.ts +356 -0
- package/src/tui/hooks/useMentionAutocomplete.ts +235 -0
- package/src/tui/hooks/useModeController.ts +167 -0
- package/src/tui/hooks/useModeShortcuts.ts +196 -0
- package/src/tui/hooks/usePermissionHandler.ts +146 -0
- package/src/tui/hooks/usePersistence.ts +480 -0
- package/src/tui/hooks/usePersistenceShortcuts.ts +225 -0
- package/src/tui/hooks/usePlaceholderRotation.ts +143 -0
- package/src/tui/hooks/useProviderStatus.ts +270 -0
- package/src/tui/hooks/useRateLimitStatus.ts +90 -0
- package/src/tui/hooks/useScreenReader.ts +315 -0
- package/src/tui/hooks/useScrollController.ts +450 -0
- package/src/tui/hooks/useScrollEventBatcher.ts +185 -0
- package/src/tui/hooks/useSidebarPanelData.ts +115 -0
- package/src/tui/hooks/useSmoothScroll.ts +202 -0
- package/src/tui/hooks/useSnapshots.ts +300 -0
- package/src/tui/hooks/useStateAndRef.ts +50 -0
- package/src/tui/hooks/useTerminalSize.ts +206 -0
- package/src/tui/hooks/useToolApprovalController.ts +91 -0
- package/src/tui/hooks/useVim.ts +334 -0
- package/src/tui/hooks/useWorkspace.ts +56 -0
- package/src/tui/i18n/__tests__/init.test.ts +278 -0
- package/src/tui/i18n/__tests__/language-config.test.ts +199 -0
- package/src/tui/i18n/__tests__/locale-detection.test.ts +250 -0
- package/src/tui/i18n/__tests__/settings-integration.test.ts +262 -0
- package/src/tui/i18n/index.ts +72 -0
- package/src/tui/i18n/init.ts +131 -0
- package/src/tui/i18n/language-config.ts +106 -0
- package/src/tui/i18n/locale-detection.ts +173 -0
- package/src/tui/i18n/settings-integration.ts +557 -0
- package/src/tui/i18n/tui-namespace.ts +538 -0
- package/src/tui/i18n/types.ts +312 -0
- package/src/tui/index.ts +43 -0
- package/src/tui/lsp-integration.ts +409 -0
- package/src/tui/metrics-integration.ts +366 -0
- package/src/tui/plugins.ts +383 -0
- package/src/tui/resilience.ts +342 -0
- package/src/tui/sandbox-integration.ts +317 -0
- package/src/tui/services/clipboard.ts +348 -0
- package/src/tui/services/fuzzy-search.ts +441 -0
- package/src/tui/services/index.ts +72 -0
- package/src/tui/services/markdown-renderer.ts +565 -0
- package/src/tui/services/open-external.ts +247 -0
- package/src/tui/services/syntax-highlighter.ts +483 -0
- package/src/tui/slash-commands.ts +12 -0
- package/src/tui/theme/index.ts +15 -0
- package/src/tui/theme/provider.tsx +206 -0
- package/src/tui/tip-integration.ts +300 -0
- package/src/tui/types/__tests__/ink-extended.test.ts +121 -0
- package/src/tui/types/ink-extended.ts +87 -0
- package/src/tui/utils/__tests__/bracketedPaste.test.ts +231 -0
- package/src/tui/utils/__tests__/heightEstimator.test.ts +157 -0
- package/src/tui/utils/__tests__/text-width.test.ts +158 -0
- package/src/tui/utils/__tests__/textSanitizer.test.ts +266 -0
- package/src/tui/utils/__tests__/ui-sizing.test.ts +169 -0
- package/src/tui/utils/bracketedPaste.ts +107 -0
- package/src/tui/utils/cursor-manager.ts +131 -0
- package/src/tui/utils/detectTerminal.ts +596 -0
- package/src/tui/utils/findLastSafeSplitPoint.ts +92 -0
- package/src/tui/utils/heightEstimator.ts +198 -0
- package/src/tui/utils/index.ts +91 -0
- package/src/tui/utils/isNarrowWidth.ts +52 -0
- package/src/tui/utils/stdoutGuard.ts +90 -0
- package/src/tui/utils/synchronized-update.ts +70 -0
- package/src/tui/utils/text-width.ts +225 -0
- package/src/tui/utils/textSanitizer.ts +225 -0
- package/src/tui/utils/textUtils.ts +114 -0
- package/src/tui/utils/ui-sizing.ts +192 -0
- package/src/tui-blessed/app.ts +160 -0
- package/src/tui-blessed/index.ts +2 -0
- package/src/tui-blessed/neo-blessed.d.ts +6 -0
- package/src/tui-blessed/test.ts +21 -0
- package/src/tui-blessed/types.ts +14 -0
- package/src/utils/icons.ts +130 -0
- package/src/utils/index.ts +33 -0
- package/src/utils/resume-hint.ts +86 -0
- package/src/version.ts +1 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +35 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAgentLoop Hook (T038)
|
|
3
|
+
*
|
|
4
|
+
* React hook that wraps AgentLoop for use in React Ink applications.
|
|
5
|
+
* Provides reactive state, run/cancel methods, and automatic cleanup.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
type AgentLoop,
|
|
10
|
+
type AgentState,
|
|
11
|
+
createUserMessage,
|
|
12
|
+
type ExecutionResult,
|
|
13
|
+
SessionParts,
|
|
14
|
+
type StateContext,
|
|
15
|
+
} from "@vellum/core";
|
|
16
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
17
|
+
|
|
18
|
+
import { ICONS } from "../../utils/icons.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Message type for the hook's message list.
|
|
22
|
+
*/
|
|
23
|
+
export interface AgentMessage {
|
|
24
|
+
id: string;
|
|
25
|
+
role: "user" | "assistant" | "system";
|
|
26
|
+
content: string;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
/** If this message contains thinking content */
|
|
29
|
+
thinking?: string;
|
|
30
|
+
thinkingDuration?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Delegation state for subagent tracking (T059).
|
|
35
|
+
*/
|
|
36
|
+
export interface DelegationState {
|
|
37
|
+
/** Whether a delegation is currently active */
|
|
38
|
+
isActive: boolean;
|
|
39
|
+
/** Delegation ID for tracking */
|
|
40
|
+
delegationId: string | null;
|
|
41
|
+
/** Agent being delegated to */
|
|
42
|
+
delegatingTo: string | null;
|
|
43
|
+
/** Task being delegated */
|
|
44
|
+
task: string | null;
|
|
45
|
+
/** Latest subagent text chunk */
|
|
46
|
+
subagentText: string | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* User prompt state for ask_followup_question tool.
|
|
51
|
+
*/
|
|
52
|
+
export interface UserPromptState {
|
|
53
|
+
/** Whether the prompt is visible */
|
|
54
|
+
visible: boolean;
|
|
55
|
+
/** The question to display to the user */
|
|
56
|
+
question: string;
|
|
57
|
+
/** Optional suggestions for the user */
|
|
58
|
+
suggestions?: string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Completion state for attempt_completion tool.
|
|
63
|
+
*/
|
|
64
|
+
export interface CompletionState {
|
|
65
|
+
/** Whether a completion was attempted */
|
|
66
|
+
attempted: boolean;
|
|
67
|
+
/** The result/summary of the completion */
|
|
68
|
+
result: string;
|
|
69
|
+
/** Whether verification was performed */
|
|
70
|
+
verified: boolean;
|
|
71
|
+
/** Whether verification passed (if verified) */
|
|
72
|
+
verificationPassed?: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Hook status derived from AgentLoop state.
|
|
77
|
+
*/
|
|
78
|
+
export type HookStatus =
|
|
79
|
+
| "idle"
|
|
80
|
+
| "running"
|
|
81
|
+
| "waiting_permission"
|
|
82
|
+
| "waiting_input"
|
|
83
|
+
| "error"
|
|
84
|
+
| "cancelled";
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Auto-approval status state for tracking consecutive approvals.
|
|
88
|
+
*/
|
|
89
|
+
export interface AutoApprovalStatusState {
|
|
90
|
+
/** Number of consecutive auto-approved requests */
|
|
91
|
+
consecutiveRequests: number;
|
|
92
|
+
/** Request limit for auto-approvals */
|
|
93
|
+
requestLimit: number;
|
|
94
|
+
/** Cumulative cost of consecutive auto-approved operations in USD */
|
|
95
|
+
consecutiveCost: number;
|
|
96
|
+
/** Cost limit for auto-approvals in USD */
|
|
97
|
+
costLimit: number;
|
|
98
|
+
/** Percentage of request limit used (0-100) */
|
|
99
|
+
requestPercentUsed: number;
|
|
100
|
+
/** Percentage of cost limit used (0-100) */
|
|
101
|
+
costPercentUsed: number;
|
|
102
|
+
/** Whether any limit has been reached */
|
|
103
|
+
limitReached: boolean;
|
|
104
|
+
/** Which limit type was reached */
|
|
105
|
+
limitType?: "requests" | "cost";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Return value of useAgentLoop hook.
|
|
110
|
+
*/
|
|
111
|
+
export interface UseAgentLoopReturn {
|
|
112
|
+
/** Current status of the agent loop */
|
|
113
|
+
status: HookStatus;
|
|
114
|
+
/** Raw AgentLoop state */
|
|
115
|
+
agentState: AgentState;
|
|
116
|
+
/** List of messages in the conversation */
|
|
117
|
+
messages: AgentMessage[];
|
|
118
|
+
/** Current thinking content being streamed */
|
|
119
|
+
thinking: string;
|
|
120
|
+
/** Whether an operation is in progress */
|
|
121
|
+
isLoading: boolean;
|
|
122
|
+
/** Last error encountered */
|
|
123
|
+
error: Error | null;
|
|
124
|
+
/** Current delegation state (T059) */
|
|
125
|
+
delegation: DelegationState;
|
|
126
|
+
/** Current user prompt state for ask_followup_question tool */
|
|
127
|
+
userPromptState: UserPromptState | null;
|
|
128
|
+
/** Submit user response to pending prompt */
|
|
129
|
+
submitUserResponse: (response: string) => void;
|
|
130
|
+
/** Current completion state for attempt_completion tool */
|
|
131
|
+
completionState: CompletionState | null;
|
|
132
|
+
/** Current auto-approval status (Phase 35+) */
|
|
133
|
+
autoApprovalStatus: AutoApprovalStatusState | null;
|
|
134
|
+
/** Run the agent with user input */
|
|
135
|
+
run: (input: string) => Promise<void>;
|
|
136
|
+
/** Cancel the current operation */
|
|
137
|
+
cancel: (reason?: string) => void;
|
|
138
|
+
/** Clear the conversation */
|
|
139
|
+
clear: () => void;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Maps AgentState to HookStatus.
|
|
144
|
+
*/
|
|
145
|
+
function mapStateToStatus(state: AgentState): HookStatus {
|
|
146
|
+
switch (state) {
|
|
147
|
+
case "idle":
|
|
148
|
+
return "idle";
|
|
149
|
+
case "streaming":
|
|
150
|
+
case "tool_executing":
|
|
151
|
+
case "recovering":
|
|
152
|
+
case "retry":
|
|
153
|
+
return "running";
|
|
154
|
+
case "wait_permission":
|
|
155
|
+
return "waiting_permission";
|
|
156
|
+
case "wait_input":
|
|
157
|
+
return "waiting_input";
|
|
158
|
+
case "terminated":
|
|
159
|
+
case "shutdown":
|
|
160
|
+
return "cancelled";
|
|
161
|
+
case "paused":
|
|
162
|
+
return "idle";
|
|
163
|
+
default:
|
|
164
|
+
return "idle";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Generates a unique message ID.
|
|
170
|
+
*/
|
|
171
|
+
function generateMessageId(): string {
|
|
172
|
+
return `msg-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* React hook for managing AgentLoop in React Ink applications.
|
|
177
|
+
*
|
|
178
|
+
* Provides:
|
|
179
|
+
* - Reactive state (status, messages, thinking)
|
|
180
|
+
* - Methods (run, cancel, clear)
|
|
181
|
+
* - Automatic event subscription and cleanup
|
|
182
|
+
* - Ctrl+C and ESC cancellation integration
|
|
183
|
+
*
|
|
184
|
+
* Note: Tool execution state is managed via ToolsContext.
|
|
185
|
+
* Use useTools().executions for tool status display.
|
|
186
|
+
*
|
|
187
|
+
* @param loop - The AgentLoop instance to wrap
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```tsx
|
|
191
|
+
* function App() {
|
|
192
|
+
* const loop = useMemo(() => new AgentLoop(config), [config]);
|
|
193
|
+
* const {
|
|
194
|
+
* status,
|
|
195
|
+
* messages,
|
|
196
|
+
* thinking,
|
|
197
|
+
* isLoading,
|
|
198
|
+
* run,
|
|
199
|
+
* cancel,
|
|
200
|
+
* } = useAgentLoop(loop);
|
|
201
|
+
*
|
|
202
|
+
* const handleSubmit = async (input: string) => {
|
|
203
|
+
* await run(input);
|
|
204
|
+
* };
|
|
205
|
+
*
|
|
206
|
+
* // Use useInput for Ctrl+C/ESC handling
|
|
207
|
+
* useInput((char, key) => {
|
|
208
|
+
* if (key.escape || (key.ctrl && char === 'c')) {
|
|
209
|
+
* cancel('user_interrupt');
|
|
210
|
+
* }
|
|
211
|
+
* });
|
|
212
|
+
*
|
|
213
|
+
* return (
|
|
214
|
+
* <Box flexDirection="column">
|
|
215
|
+
* <MessageList messages={messages} />
|
|
216
|
+
* {thinking && <ThinkingBlock thinking={thinking} duration={0} />}
|
|
217
|
+
* <Input onSubmit={handleSubmit} disabled={isLoading} />
|
|
218
|
+
* </Box>
|
|
219
|
+
* );
|
|
220
|
+
* }
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
export function useAgentLoop(loop: AgentLoop): UseAgentLoopReturn {
|
|
224
|
+
// Core state
|
|
225
|
+
const [agentState, setAgentState] = useState<AgentState>(loop.getState());
|
|
226
|
+
const [messages, setMessages] = useState<AgentMessage[]>([]);
|
|
227
|
+
const [thinking, setThinking] = useState("");
|
|
228
|
+
const [error, setError] = useState<Error | null>(null);
|
|
229
|
+
|
|
230
|
+
// Delegation state (T059)
|
|
231
|
+
const [delegation, setDelegation] = useState<DelegationState>({
|
|
232
|
+
isActive: false,
|
|
233
|
+
delegationId: null,
|
|
234
|
+
delegatingTo: null,
|
|
235
|
+
task: null,
|
|
236
|
+
subagentText: null,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// User prompt state for ask_followup_question tool
|
|
240
|
+
const [userPromptState, setUserPromptState] = useState<UserPromptState | null>(null);
|
|
241
|
+
|
|
242
|
+
// Completion state for attempt_completion tool
|
|
243
|
+
const [completionState, setCompletionState] = useState<CompletionState | null>(null);
|
|
244
|
+
|
|
245
|
+
// Auto-approval status state (Phase 35+)
|
|
246
|
+
const [autoApprovalStatus, setAutoApprovalStatus] = useState<AutoApprovalStatusState | null>(
|
|
247
|
+
() => {
|
|
248
|
+
// Initialize from current state if available
|
|
249
|
+
// Use type guard since method was added in Phase 35+
|
|
250
|
+
const loopWithStatus = loop as AgentLoop & {
|
|
251
|
+
getAutoApprovalStatus?: () => {
|
|
252
|
+
consecutiveRequests: number;
|
|
253
|
+
requestLimit: number;
|
|
254
|
+
consecutiveCost: number;
|
|
255
|
+
costLimit: number;
|
|
256
|
+
requestPercentUsed: number;
|
|
257
|
+
costPercentUsed: number;
|
|
258
|
+
requestLimitReached: boolean;
|
|
259
|
+
costLimitReached: boolean;
|
|
260
|
+
} | null;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
if (typeof loopWithStatus.getAutoApprovalStatus !== "function") {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
const status = loopWithStatus.getAutoApprovalStatus();
|
|
267
|
+
if (status) {
|
|
268
|
+
return {
|
|
269
|
+
consecutiveRequests: status.consecutiveRequests,
|
|
270
|
+
requestLimit: status.requestLimit,
|
|
271
|
+
consecutiveCost: status.consecutiveCost,
|
|
272
|
+
costLimit: status.costLimit,
|
|
273
|
+
requestPercentUsed: status.requestPercentUsed,
|
|
274
|
+
costPercentUsed: status.costPercentUsed,
|
|
275
|
+
limitReached: status.requestLimitReached || status.costLimitReached,
|
|
276
|
+
limitType: status.requestLimitReached
|
|
277
|
+
? "requests"
|
|
278
|
+
: status.costLimitReached
|
|
279
|
+
? "cost"
|
|
280
|
+
: undefined,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Track thinking start time for duration calculation
|
|
288
|
+
const thinkingStartRef = useRef<number | null>(null);
|
|
289
|
+
|
|
290
|
+
// Track current streaming message for accumulation
|
|
291
|
+
const currentMessageRef = useRef<{
|
|
292
|
+
id: string;
|
|
293
|
+
content: string;
|
|
294
|
+
thinking: string;
|
|
295
|
+
thinkingDuration: number;
|
|
296
|
+
} | null>(null);
|
|
297
|
+
|
|
298
|
+
// Derived state
|
|
299
|
+
const status = mapStateToStatus(agentState);
|
|
300
|
+
const isLoading = status === "running" || status === "waiting_permission";
|
|
301
|
+
|
|
302
|
+
// Subscribe to AgentLoop events
|
|
303
|
+
useEffect(() => {
|
|
304
|
+
// State change handler
|
|
305
|
+
const handleStateChange = (from: AgentState, to: AgentState, _context: StateContext) => {
|
|
306
|
+
setAgentState(to);
|
|
307
|
+
|
|
308
|
+
// Clear error on successful state transition from recovering
|
|
309
|
+
if (from === "recovering" && to !== "terminated") {
|
|
310
|
+
setError(null);
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// Text streaming handler
|
|
315
|
+
const handleText = (text: string) => {
|
|
316
|
+
if (!currentMessageRef.current) {
|
|
317
|
+
currentMessageRef.current = {
|
|
318
|
+
id: generateMessageId(),
|
|
319
|
+
content: "",
|
|
320
|
+
thinking: "",
|
|
321
|
+
thinkingDuration: 0,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
currentMessageRef.current.content += text;
|
|
325
|
+
|
|
326
|
+
// Update messages with accumulated content
|
|
327
|
+
const current = currentMessageRef.current;
|
|
328
|
+
setMessages((prev) => {
|
|
329
|
+
const existingIndex = prev.findIndex((m) => m.id === current.id);
|
|
330
|
+
const message: AgentMessage = {
|
|
331
|
+
id: current.id,
|
|
332
|
+
role: "assistant",
|
|
333
|
+
content: current.content,
|
|
334
|
+
timestamp: Date.now(),
|
|
335
|
+
thinking: current.thinking || undefined,
|
|
336
|
+
thinkingDuration: current.thinkingDuration || undefined,
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
if (existingIndex >= 0) {
|
|
340
|
+
const updated = [...prev];
|
|
341
|
+
updated[existingIndex] = message;
|
|
342
|
+
return updated;
|
|
343
|
+
}
|
|
344
|
+
return [...prev, message];
|
|
345
|
+
});
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// Thinking streaming handler
|
|
349
|
+
const handleThinking = (text: string) => {
|
|
350
|
+
if (thinkingStartRef.current === null) {
|
|
351
|
+
thinkingStartRef.current = Date.now();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Create streaming message if not exists (same pattern as handleText)
|
|
355
|
+
// This ensures UI shows response even when thinking arrives before text
|
|
356
|
+
if (!currentMessageRef.current) {
|
|
357
|
+
currentMessageRef.current = {
|
|
358
|
+
id: generateMessageId(),
|
|
359
|
+
content: "",
|
|
360
|
+
thinking: "",
|
|
361
|
+
thinkingDuration: 0,
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// Add initial message to UI so it knows there's a response
|
|
365
|
+
const current = currentMessageRef.current;
|
|
366
|
+
setMessages((prev) => [
|
|
367
|
+
...prev,
|
|
368
|
+
{
|
|
369
|
+
id: current.id,
|
|
370
|
+
role: "assistant",
|
|
371
|
+
content: "",
|
|
372
|
+
timestamp: Date.now(),
|
|
373
|
+
thinking: text,
|
|
374
|
+
thinkingDuration: 0,
|
|
375
|
+
},
|
|
376
|
+
]);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
setThinking((prev) => prev + text);
|
|
380
|
+
|
|
381
|
+
// Accumulate in current message ref
|
|
382
|
+
currentMessageRef.current.thinking += text;
|
|
383
|
+
currentMessageRef.current.thinkingDuration =
|
|
384
|
+
Date.now() - (thinkingStartRef.current ?? Date.now());
|
|
385
|
+
|
|
386
|
+
// Update message with accumulated thinking
|
|
387
|
+
const current = currentMessageRef.current;
|
|
388
|
+
setMessages((prev) => {
|
|
389
|
+
const existingIndex = prev.findIndex((m) => m.id === current.id);
|
|
390
|
+
const existing = prev[existingIndex];
|
|
391
|
+
if (existingIndex >= 0 && existing) {
|
|
392
|
+
const updated = [...prev];
|
|
393
|
+
updated[existingIndex] = {
|
|
394
|
+
id: existing.id,
|
|
395
|
+
role: existing.role,
|
|
396
|
+
content: existing.content,
|
|
397
|
+
timestamp: existing.timestamp,
|
|
398
|
+
thinking: current.thinking,
|
|
399
|
+
thinkingDuration: current.thinkingDuration,
|
|
400
|
+
};
|
|
401
|
+
return updated;
|
|
402
|
+
}
|
|
403
|
+
return prev;
|
|
404
|
+
});
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
// Tool event handlers (events still fired but state managed by ToolsContext)
|
|
408
|
+
const handleToolStart = (_callId: string, _name: string, _input: Record<string, unknown>) => {
|
|
409
|
+
// Tool state is managed by ToolsContext, not here
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const handleToolEnd = (_callId: string, _name: string, _result: ExecutionResult) => {
|
|
413
|
+
// Tool state is managed by ToolsContext, not here
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
// Error handler
|
|
417
|
+
const handleError = (err: Error) => {
|
|
418
|
+
setError(err);
|
|
419
|
+
// Surface error as assistant message for visibility
|
|
420
|
+
const errorMessage: AgentMessage = {
|
|
421
|
+
id: generateMessageId(),
|
|
422
|
+
role: "assistant",
|
|
423
|
+
content: `${ICONS.warning} ${err.message}`,
|
|
424
|
+
timestamp: Date.now(),
|
|
425
|
+
};
|
|
426
|
+
setMessages((prev) => [...prev, errorMessage]);
|
|
427
|
+
currentMessageRef.current = null;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// Complete handler
|
|
431
|
+
const handleComplete = () => {
|
|
432
|
+
// Finalize current message
|
|
433
|
+
if (currentMessageRef.current) {
|
|
434
|
+
currentMessageRef.current = null;
|
|
435
|
+
}
|
|
436
|
+
// Clear thinking
|
|
437
|
+
setThinking("");
|
|
438
|
+
thinkingStartRef.current = null;
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
// Message handler - log or store full message content
|
|
442
|
+
const handleMessage = (_content: string) => {
|
|
443
|
+
// Message events are handled via text accumulation
|
|
444
|
+
// This handler can be extended for message-level operations
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// Tool call handler - events still fired but state managed by ToolsContext
|
|
448
|
+
const handleToolCall = (_id: string, _name: string, _input: Record<string, unknown>) => {
|
|
449
|
+
// Tool state is managed by ToolsContext, not here
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
// Usage handler - track token usage
|
|
453
|
+
const handleUsage = (_usage: { inputTokens: number; outputTokens: number }) => {
|
|
454
|
+
// Token usage can be tracked here for display
|
|
455
|
+
// This data is also available in ContextProgress via context
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
// Terminated handler - handle cancellation/termination
|
|
459
|
+
const handleTerminated = () => {
|
|
460
|
+
setThinking("");
|
|
461
|
+
thinkingStartRef.current = null;
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// Loop detected handler - show warning to user
|
|
465
|
+
const handleLoopDetected = (_result: {
|
|
466
|
+
detected: boolean;
|
|
467
|
+
confidence: number;
|
|
468
|
+
reason?: string;
|
|
469
|
+
}) => {
|
|
470
|
+
// Loop detection can trigger UI feedback
|
|
471
|
+
// Consider emitting a warning message or status
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
// Retry handler - show retry feedback
|
|
475
|
+
const handleRetry = (_attempt: number, _error: Error, _delay: number) => {
|
|
476
|
+
// Retry events can be used for UI feedback
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// Retry exhausted handler - show retry failure
|
|
480
|
+
const handleRetryExhausted = (err: Error, _attempts: number) => {
|
|
481
|
+
setError(err);
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// Delegation start handler (T059)
|
|
485
|
+
const handleDelegationStart = (delegationId: string, agent: string, task: string) => {
|
|
486
|
+
setDelegation({
|
|
487
|
+
isActive: true,
|
|
488
|
+
delegationId,
|
|
489
|
+
delegatingTo: agent,
|
|
490
|
+
task,
|
|
491
|
+
subagentText: null,
|
|
492
|
+
});
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
// Delegation complete handler (T059)
|
|
496
|
+
const handleDelegationComplete = (_delegationId: string, _agent: string, _result: string) => {
|
|
497
|
+
setDelegation({
|
|
498
|
+
isActive: false,
|
|
499
|
+
delegationId: null,
|
|
500
|
+
delegatingTo: null,
|
|
501
|
+
task: null,
|
|
502
|
+
subagentText: null,
|
|
503
|
+
});
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// Subagent text handler (T059)
|
|
507
|
+
const handleSubagentText = (_delegationId: string, agent: string, chunk: string) => {
|
|
508
|
+
setDelegation((prev) => ({
|
|
509
|
+
...prev,
|
|
510
|
+
subagentText: chunk,
|
|
511
|
+
delegatingTo: agent,
|
|
512
|
+
}));
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// Subagent tool handler (T059)
|
|
516
|
+
const handleSubagentTool = (_delegationId: string, agent: string, toolName: string) => {
|
|
517
|
+
setDelegation((prev) => ({
|
|
518
|
+
...prev,
|
|
519
|
+
subagentText: `${agent}: ${toolName}`,
|
|
520
|
+
}));
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
// User prompt handler for ask_followup_question tool
|
|
524
|
+
const handleUserPromptRequired = (prompt: { question: string; suggestions?: string[] }) => {
|
|
525
|
+
setUserPromptState({
|
|
526
|
+
visible: true,
|
|
527
|
+
question: prompt.question,
|
|
528
|
+
suggestions: prompt.suggestions,
|
|
529
|
+
});
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
// Completion handler for attempt_completion tool
|
|
533
|
+
const handleCompletionAttempted = (completion: {
|
|
534
|
+
result: string;
|
|
535
|
+
verified: boolean;
|
|
536
|
+
verificationPassed?: boolean;
|
|
537
|
+
}) => {
|
|
538
|
+
setCompletionState({
|
|
539
|
+
attempted: true,
|
|
540
|
+
result: completion.result,
|
|
541
|
+
verified: completion.verified,
|
|
542
|
+
verificationPassed: completion.verificationPassed,
|
|
543
|
+
});
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
// Auto-approval status handler (Phase 35+)
|
|
547
|
+
const handleAutoApprovalStatus = (status: {
|
|
548
|
+
consecutiveRequests: number;
|
|
549
|
+
requestLimit: number;
|
|
550
|
+
consecutiveCost: number;
|
|
551
|
+
costLimit: number;
|
|
552
|
+
requestPercentUsed: number;
|
|
553
|
+
costPercentUsed: number;
|
|
554
|
+
limitReached: boolean;
|
|
555
|
+
limitType?: "requests" | "cost";
|
|
556
|
+
}) => {
|
|
557
|
+
setAutoApprovalStatus(status);
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
// Subscribe to events
|
|
561
|
+
loop.on("stateChange", handleStateChange);
|
|
562
|
+
loop.on("text", handleText);
|
|
563
|
+
loop.on("thinking", handleThinking);
|
|
564
|
+
loop.on("toolStart", handleToolStart);
|
|
565
|
+
loop.on("toolEnd", handleToolEnd);
|
|
566
|
+
loop.on("error", handleError);
|
|
567
|
+
loop.on("complete", handleComplete);
|
|
568
|
+
loop.on("message", handleMessage);
|
|
569
|
+
loop.on("toolCall", handleToolCall);
|
|
570
|
+
loop.on("usage", handleUsage);
|
|
571
|
+
loop.on("terminated", handleTerminated);
|
|
572
|
+
loop.on("loopDetected", handleLoopDetected);
|
|
573
|
+
loop.on("retry", handleRetry);
|
|
574
|
+
loop.on("retryExhausted", handleRetryExhausted);
|
|
575
|
+
// Delegation events (T059)
|
|
576
|
+
loop.on("delegationStart", handleDelegationStart);
|
|
577
|
+
loop.on("delegationComplete", handleDelegationComplete);
|
|
578
|
+
loop.on("subagentText", handleSubagentText);
|
|
579
|
+
loop.on("subagentTool", handleSubagentTool);
|
|
580
|
+
// User prompt and completion events
|
|
581
|
+
loop.on("userPrompt:required", handleUserPromptRequired);
|
|
582
|
+
loop.on("completion:attempted", handleCompletionAttempted);
|
|
583
|
+
// Auto-approval status event (Phase 35+)
|
|
584
|
+
// Use type cast since event was added in Phase 35+
|
|
585
|
+
const loopWithAutoApproval = loop as unknown as {
|
|
586
|
+
on(event: "autoApproval:statusChange", handler: typeof handleAutoApprovalStatus): void;
|
|
587
|
+
off(event: "autoApproval:statusChange", handler: typeof handleAutoApprovalStatus): void;
|
|
588
|
+
};
|
|
589
|
+
try {
|
|
590
|
+
loopWithAutoApproval.on("autoApproval:statusChange", handleAutoApprovalStatus);
|
|
591
|
+
} catch {
|
|
592
|
+
// Event may not exist in older versions
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Cleanup subscriptions
|
|
596
|
+
return () => {
|
|
597
|
+
// Unsubscribe from all events
|
|
598
|
+
loop.off("stateChange", handleStateChange);
|
|
599
|
+
loop.off("text", handleText);
|
|
600
|
+
loop.off("thinking", handleThinking);
|
|
601
|
+
loop.off("toolStart", handleToolStart);
|
|
602
|
+
loop.off("toolEnd", handleToolEnd);
|
|
603
|
+
loop.off("error", handleError);
|
|
604
|
+
loop.off("complete", handleComplete);
|
|
605
|
+
loop.off("message", handleMessage);
|
|
606
|
+
loop.off("toolCall", handleToolCall);
|
|
607
|
+
loop.off("usage", handleUsage);
|
|
608
|
+
loop.off("terminated", handleTerminated);
|
|
609
|
+
loop.off("loopDetected", handleLoopDetected);
|
|
610
|
+
loop.off("retry", handleRetry);
|
|
611
|
+
loop.off("retryExhausted", handleRetryExhausted);
|
|
612
|
+
// Delegation events cleanup (T059)
|
|
613
|
+
loop.off("delegationStart", handleDelegationStart);
|
|
614
|
+
loop.off("delegationComplete", handleDelegationComplete);
|
|
615
|
+
loop.off("subagentText", handleSubagentText);
|
|
616
|
+
loop.off("subagentTool", handleSubagentTool);
|
|
617
|
+
// User prompt and completion events cleanup
|
|
618
|
+
loop.off("userPrompt:required", handleUserPromptRequired);
|
|
619
|
+
loop.off("completion:attempted", handleCompletionAttempted);
|
|
620
|
+
// Auto-approval status event cleanup (Phase 35+)
|
|
621
|
+
try {
|
|
622
|
+
loopWithAutoApproval.off("autoApproval:statusChange", handleAutoApprovalStatus);
|
|
623
|
+
} catch {
|
|
624
|
+
// Event may not exist in older versions
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}, [loop]);
|
|
628
|
+
|
|
629
|
+
// Run method - adds user message and runs the loop
|
|
630
|
+
const run = useCallback(
|
|
631
|
+
async (input: string) => {
|
|
632
|
+
if (!input.trim()) return;
|
|
633
|
+
|
|
634
|
+
// Reset state for new run
|
|
635
|
+
setError(null);
|
|
636
|
+
setThinking("");
|
|
637
|
+
thinkingStartRef.current = null;
|
|
638
|
+
currentMessageRef.current = null;
|
|
639
|
+
|
|
640
|
+
// Add user message
|
|
641
|
+
const userMessage: AgentMessage = {
|
|
642
|
+
id: generateMessageId(),
|
|
643
|
+
role: "user",
|
|
644
|
+
content: input,
|
|
645
|
+
timestamp: Date.now(),
|
|
646
|
+
};
|
|
647
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
648
|
+
|
|
649
|
+
// Add to AgentLoop messages
|
|
650
|
+
loop.addMessage(createUserMessage([SessionParts.text(input)]));
|
|
651
|
+
|
|
652
|
+
// Run the loop
|
|
653
|
+
try {
|
|
654
|
+
await loop.run();
|
|
655
|
+
} catch (err) {
|
|
656
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
657
|
+
setError(error);
|
|
658
|
+
throw error; // Re-throw for caller to handle
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
[loop]
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
// Cancel method
|
|
665
|
+
const cancel = useCallback(
|
|
666
|
+
(reason?: string) => {
|
|
667
|
+
loop.cancel(reason);
|
|
668
|
+
setThinking("");
|
|
669
|
+
thinkingStartRef.current = null;
|
|
670
|
+
},
|
|
671
|
+
[loop]
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
// Clear conversation
|
|
675
|
+
const clear = useCallback(() => {
|
|
676
|
+
setMessages([]);
|
|
677
|
+
setThinking("");
|
|
678
|
+
setError(null);
|
|
679
|
+
thinkingStartRef.current = null;
|
|
680
|
+
currentMessageRef.current = null;
|
|
681
|
+
setUserPromptState(null);
|
|
682
|
+
setCompletionState(null);
|
|
683
|
+
}, []);
|
|
684
|
+
|
|
685
|
+
// Submit user response to pending prompt
|
|
686
|
+
const submitUserResponse = useCallback(
|
|
687
|
+
(response: string) => {
|
|
688
|
+
loop.submitUserResponse(response);
|
|
689
|
+
setUserPromptState(null);
|
|
690
|
+
},
|
|
691
|
+
[loop]
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
return {
|
|
695
|
+
status,
|
|
696
|
+
agentState,
|
|
697
|
+
messages,
|
|
698
|
+
thinking,
|
|
699
|
+
isLoading,
|
|
700
|
+
error,
|
|
701
|
+
delegation,
|
|
702
|
+
userPromptState,
|
|
703
|
+
submitUserResponse,
|
|
704
|
+
completionState,
|
|
705
|
+
autoApprovalStatus,
|
|
706
|
+
run,
|
|
707
|
+
cancel,
|
|
708
|
+
clear,
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
export default useAgentLoop;
|