@qduc/term2 0.4.0 → 0.5.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/dist/agent.d.ts +5 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +8 -1
- package/dist/agent.js.map +1 -1
- package/dist/agent.lite-mode.test.d.ts +2 -0
- package/dist/agent.lite-mode.test.d.ts.map +1 -0
- package/dist/agent.lite-mode.test.js +39 -0
- package/dist/agent.lite-mode.test.js.map +1 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +9 -3
- package/dist/app.js.map +1 -1
- package/dist/components/ApprovalPrompt.d.ts.map +1 -1
- package/dist/components/ApprovalPrompt.js +3 -2
- package/dist/components/ApprovalPrompt.js.map +1 -1
- package/dist/components/ApprovalPrompt.test.d.ts +2 -0
- package/dist/components/ApprovalPrompt.test.d.ts.map +1 -0
- package/dist/components/ApprovalPrompt.test.js +23 -0
- package/dist/components/ApprovalPrompt.test.js.map +1 -0
- package/dist/components/ChatMessage.d.ts.map +1 -1
- package/dist/components/ChatMessage.js +2 -1
- package/dist/components/ChatMessage.js.map +1 -1
- package/dist/components/CommandMessage.d.ts.map +1 -1
- package/dist/components/CommandMessage.js +9 -8
- package/dist/components/CommandMessage.js.map +1 -1
- package/dist/components/LiveResponse.test.d.ts +2 -0
- package/dist/components/LiveResponse.test.d.ts.map +1 -0
- package/dist/components/LiveResponse.test.js +13 -0
- package/dist/components/LiveResponse.test.js.map +1 -0
- package/dist/components/MarkdownRenderer.d.ts.map +1 -1
- package/dist/components/MarkdownRenderer.js +13 -6
- package/dist/components/MarkdownRenderer.js.map +1 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +59 -7
- package/dist/components/MessageList.js.map +1 -1
- package/dist/components/MessageList.test.js +112 -0
- package/dist/components/MessageList.test.js.map +1 -1
- package/dist/components/ModelSelectionMenu.error-tabs.test.d.ts +2 -0
- package/dist/components/ModelSelectionMenu.error-tabs.test.d.ts.map +1 -0
- package/dist/components/ModelSelectionMenu.error-tabs.test.js +18 -0
- package/dist/components/ModelSelectionMenu.error-tabs.test.js.map +1 -0
- package/dist/components/StatusBar.d.ts.map +1 -1
- package/dist/components/StatusBar.js +3 -3
- package/dist/components/StatusBar.js.map +1 -1
- package/dist/components/SubagentActivityMessage.d.ts +12 -0
- package/dist/components/SubagentActivityMessage.d.ts.map +1 -0
- package/dist/components/SubagentActivityMessage.js +15 -0
- package/dist/components/SubagentActivityMessage.js.map +1 -0
- package/dist/components/TextInput.d.ts +42 -0
- package/dist/components/TextInput.d.ts.map +1 -0
- package/dist/components/TextInput.js +398 -0
- package/dist/components/TextInput.js.map +1 -0
- package/dist/components/TextInput.test.d.ts +2 -0
- package/dist/components/TextInput.test.d.ts.map +1 -0
- package/dist/components/TextInput.test.js +75 -0
- package/dist/components/TextInput.test.js.map +1 -0
- package/dist/components/theme.d.ts +8 -0
- package/dist/components/theme.d.ts.map +1 -0
- package/dist/components/theme.js +22 -0
- package/dist/components/theme.js.map +1 -0
- package/dist/debug_ask_mentor.d.ts +2 -0
- package/dist/debug_ask_mentor.d.ts.map +1 -0
- package/dist/debug_ask_mentor.js +73 -0
- package/dist/debug_ask_mentor.js.map +1 -0
- package/dist/hooks/use-app-commands.d.ts +6 -0
- package/dist/hooks/use-app-commands.d.ts.map +1 -1
- package/dist/hooks/use-app-commands.js +23 -20
- package/dist/hooks/use-app-commands.js.map +1 -1
- package/dist/hooks/use-app-commands.test.js +34 -1
- package/dist/hooks/use-app-commands.test.js.map +1 -1
- package/dist/hooks/use-conversation.d.ts +13 -2
- package/dist/hooks/use-conversation.d.ts.map +1 -1
- package/dist/hooks/use-conversation.js +36 -13
- package/dist/hooks/use-conversation.js.map +1 -1
- package/dist/lib/editor-impl.test.js +63 -50
- package/dist/lib/editor-impl.test.js.map +1 -1
- package/dist/lib/openai-agent-client.d.ts +7 -0
- package/dist/lib/openai-agent-client.d.ts.map +1 -1
- package/dist/lib/openai-agent-client.js +35 -131
- package/dist/lib/openai-agent-client.js.map +1 -1
- package/dist/lib/openai-agent-client.test.js +7 -0
- package/dist/lib/openai-agent-client.test.js.map +1 -1
- package/dist/lib/shell.d.ts +7 -0
- package/dist/lib/shell.d.ts.map +1 -0
- package/dist/lib/shell.js +56 -0
- package/dist/lib/shell.js.map +1 -0
- package/dist/modes/companion/command-index.d.ts +26 -0
- package/dist/modes/companion/command-index.d.ts.map +1 -0
- package/dist/modes/companion/command-index.js +50 -0
- package/dist/modes/companion/command-index.js.map +1 -0
- package/dist/modes/companion/command-index.test.d.ts +2 -0
- package/dist/modes/companion/command-index.test.d.ts.map +1 -0
- package/dist/modes/companion/command-index.test.js +86 -0
- package/dist/modes/companion/command-index.test.js.map +1 -0
- package/dist/modes/companion/companion-app.d.ts +12 -0
- package/dist/modes/companion/companion-app.d.ts.map +1 -0
- package/dist/modes/companion/companion-app.js +297 -0
- package/dist/modes/companion/companion-app.js.map +1 -0
- package/dist/modes/companion/companion-session.d.ts +63 -0
- package/dist/modes/companion/companion-session.d.ts.map +1 -0
- package/dist/modes/companion/companion-session.js +146 -0
- package/dist/modes/companion/companion-session.js.map +1 -0
- package/dist/modes/companion/companion-session.test.d.ts +2 -0
- package/dist/modes/companion/companion-session.test.d.ts.map +1 -0
- package/dist/modes/companion/companion-session.test.js +28 -0
- package/dist/modes/companion/companion-session.test.js.map +1 -0
- package/dist/modes/companion/components/status-bar.d.ts +13 -0
- package/dist/modes/companion/components/status-bar.d.ts.map +1 -0
- package/dist/modes/companion/components/status-bar.js +26 -0
- package/dist/modes/companion/components/status-bar.js.map +1 -0
- package/dist/modes/companion/context-buffer.d.ts +65 -0
- package/dist/modes/companion/context-buffer.d.ts.map +1 -0
- package/dist/modes/companion/context-buffer.js +156 -0
- package/dist/modes/companion/context-buffer.js.map +1 -0
- package/dist/modes/companion/context-buffer.test.d.ts +2 -0
- package/dist/modes/companion/context-buffer.test.d.ts.map +1 -0
- package/dist/modes/companion/context-buffer.test.js +154 -0
- package/dist/modes/companion/context-buffer.test.js.map +1 -0
- package/dist/modes/companion/event-detector.d.ts +46 -0
- package/dist/modes/companion/event-detector.d.ts.map +1 -0
- package/dist/modes/companion/event-detector.js +169 -0
- package/dist/modes/companion/event-detector.js.map +1 -0
- package/dist/modes/companion/event-detector.test.d.ts +2 -0
- package/dist/modes/companion/event-detector.test.d.ts.map +1 -0
- package/dist/modes/companion/event-detector.test.js +121 -0
- package/dist/modes/companion/event-detector.test.js.map +1 -0
- package/dist/modes/companion/index.d.ts +33 -0
- package/dist/modes/companion/index.d.ts.map +1 -0
- package/dist/modes/companion/index.js +21 -0
- package/dist/modes/companion/index.js.map +1 -0
- package/dist/modes/companion/input-key-mapper.d.ts +3 -0
- package/dist/modes/companion/input-key-mapper.d.ts.map +1 -0
- package/dist/modes/companion/input-key-mapper.js +31 -0
- package/dist/modes/companion/input-key-mapper.js.map +1 -0
- package/dist/modes/companion/input-key-mapper.test.d.ts +2 -0
- package/dist/modes/companion/input-key-mapper.test.d.ts.map +1 -0
- package/dist/modes/companion/input-key-mapper.test.js +26 -0
- package/dist/modes/companion/input-key-mapper.test.js.map +1 -0
- package/dist/modes/companion/input-parser.d.ts +53 -0
- package/dist/modes/companion/input-parser.d.ts.map +1 -0
- package/dist/modes/companion/input-parser.js +114 -0
- package/dist/modes/companion/input-parser.js.map +1 -0
- package/dist/modes/companion/input-parser.test.d.ts +2 -0
- package/dist/modes/companion/input-parser.test.d.ts.map +1 -0
- package/dist/modes/companion/input-parser.test.js +123 -0
- package/dist/modes/companion/input-parser.test.js.map +1 -0
- package/dist/modes/companion/mode-manager.d.ts +41 -0
- package/dist/modes/companion/mode-manager.d.ts.map +1 -0
- package/dist/modes/companion/mode-manager.js +56 -0
- package/dist/modes/companion/mode-manager.js.map +1 -0
- package/dist/modes/companion/mode-manager.test.d.ts +2 -0
- package/dist/modes/companion/mode-manager.test.d.ts.map +1 -0
- package/dist/modes/companion/mode-manager.test.js +65 -0
- package/dist/modes/companion/mode-manager.test.js.map +1 -0
- package/dist/modes/companion/output-classifier.d.ts +15 -0
- package/dist/modes/companion/output-classifier.d.ts.map +1 -0
- package/dist/modes/companion/output-classifier.js +77 -0
- package/dist/modes/companion/output-classifier.js.map +1 -0
- package/dist/modes/companion/output-classifier.test.d.ts +2 -0
- package/dist/modes/companion/output-classifier.test.d.ts.map +1 -0
- package/dist/modes/companion/output-classifier.test.js +133 -0
- package/dist/modes/companion/output-classifier.test.js.map +1 -0
- package/dist/modes/companion/pty-wrapper.d.ts +46 -0
- package/dist/modes/companion/pty-wrapper.d.ts.map +1 -0
- package/dist/modes/companion/pty-wrapper.js +143 -0
- package/dist/modes/companion/pty-wrapper.js.map +1 -0
- package/dist/modes/companion/safety-classifier.d.ts +31 -0
- package/dist/modes/companion/safety-classifier.d.ts.map +1 -0
- package/dist/modes/companion/safety-classifier.js +140 -0
- package/dist/modes/companion/safety-classifier.js.map +1 -0
- package/dist/modes/companion/safety-classifier.test.d.ts +2 -0
- package/dist/modes/companion/safety-classifier.test.d.ts.map +1 -0
- package/dist/modes/companion/safety-classifier.test.js +151 -0
- package/dist/modes/companion/safety-classifier.test.js.map +1 -0
- package/dist/modes/companion/summarizer.d.ts +24 -0
- package/dist/modes/companion/summarizer.d.ts.map +1 -0
- package/dist/modes/companion/summarizer.js +132 -0
- package/dist/modes/companion/summarizer.js.map +1 -0
- package/dist/modes/companion/terminal-history.d.ts +27 -0
- package/dist/modes/companion/terminal-history.d.ts.map +1 -0
- package/dist/modes/companion/terminal-history.js +142 -0
- package/dist/modes/companion/terminal-history.js.map +1 -0
- package/dist/prompts/gpt-5.md +4 -7
- package/dist/prompts/simple-mentor.md +215 -0
- package/dist/prompts/subagents/explorer.md +42 -0
- package/dist/prompts/subagents/mentor.md +16 -0
- package/dist/prompts/subagents/researcher.md +40 -0
- package/dist/prompts/subagents/worker.md +41 -0
- package/dist/providers/ai-sdk-openai-compatible.provider.d.ts +2 -0
- package/dist/providers/ai-sdk-openai-compatible.provider.d.ts.map +1 -1
- package/dist/providers/ai-sdk-openai-compatible.provider.js +8 -4
- package/dist/providers/ai-sdk-openai-compatible.provider.js.map +1 -1
- package/dist/providers/ai-sdk-openai-compatible.provider.test.js +119 -1
- package/dist/providers/ai-sdk-openai-compatible.provider.test.js.map +1 -1
- package/dist/providers/ai-sdk-openai.provider.d.ts +14 -0
- package/dist/providers/ai-sdk-openai.provider.d.ts.map +1 -0
- package/dist/providers/ai-sdk-openai.provider.js +18 -0
- package/dist/providers/ai-sdk-openai.provider.js.map +1 -0
- package/dist/providers/common/openai-compatible-messages.d.ts +1 -1
- package/dist/providers/common/openai-compatible-messages.d.ts.map +1 -1
- package/dist/providers/common/openai-compatible-messages.js +41 -16
- package/dist/providers/common/openai-compatible-messages.js.map +1 -1
- package/dist/providers/common/openai-compatible-messages.test.js +55 -14
- package/dist/providers/common/openai-compatible-messages.test.js.map +1 -1
- package/dist/providers/github-copilot/converters.d.ts +45 -0
- package/dist/providers/github-copilot/converters.d.ts.map +1 -0
- package/dist/providers/github-copilot/converters.js +118 -0
- package/dist/providers/github-copilot/converters.js.map +1 -0
- package/dist/providers/github-copilot/converters.test.d.ts +2 -0
- package/dist/providers/github-copilot/converters.test.d.ts.map +1 -0
- package/dist/providers/github-copilot/converters.test.js +162 -0
- package/dist/providers/github-copilot/converters.test.js.map +1 -0
- package/dist/providers/github-copilot/github-copilot.provider.d.ts +2 -0
- package/dist/providers/github-copilot/github-copilot.provider.d.ts.map +1 -0
- package/dist/providers/github-copilot/github-copilot.provider.js +75 -0
- package/dist/providers/github-copilot/github-copilot.provider.js.map +1 -0
- package/dist/providers/github-copilot/github-copilot.provider.test.d.ts +2 -0
- package/dist/providers/github-copilot/github-copilot.provider.test.d.ts.map +1 -0
- package/dist/providers/github-copilot/github-copilot.provider.test.js +26 -0
- package/dist/providers/github-copilot/github-copilot.provider.test.js.map +1 -0
- package/dist/providers/github-copilot/index.d.ts +4 -0
- package/dist/providers/github-copilot/index.d.ts.map +1 -0
- package/dist/providers/github-copilot/index.js +4 -0
- package/dist/providers/github-copilot/index.js.map +1 -0
- package/dist/providers/github-copilot/model.d.ts +24 -0
- package/dist/providers/github-copilot/model.d.ts.map +1 -0
- package/dist/providers/github-copilot/model.delta.test.d.ts +2 -0
- package/dist/providers/github-copilot/model.delta.test.d.ts.map +1 -0
- package/dist/providers/github-copilot/model.delta.test.js +15 -0
- package/dist/providers/github-copilot/model.delta.test.js.map +1 -0
- package/dist/providers/github-copilot/model.js +498 -0
- package/dist/providers/github-copilot/model.js.map +1 -0
- package/dist/providers/github-copilot/model.test.d.ts +2 -0
- package/dist/providers/github-copilot/model.test.d.ts.map +1 -0
- package/dist/providers/github-copilot/model.test.js +63 -0
- package/dist/providers/github-copilot/model.test.js.map +1 -0
- package/dist/providers/github-copilot/provider.d.ts +15 -0
- package/dist/providers/github-copilot/provider.d.ts.map +1 -0
- package/dist/providers/github-copilot/provider.js +25 -0
- package/dist/providers/github-copilot/provider.js.map +1 -0
- package/dist/providers/github-copilot/provider.test.d.ts +2 -0
- package/dist/providers/github-copilot/provider.test.d.ts.map +1 -0
- package/dist/providers/github-copilot/provider.test.js +52 -0
- package/dist/providers/github-copilot/provider.test.js.map +1 -0
- package/dist/providers/github-copilot/utils.d.ts +20 -0
- package/dist/providers/github-copilot/utils.d.ts.map +1 -0
- package/dist/providers/github-copilot/utils.js +142 -0
- package/dist/providers/github-copilot/utils.js.map +1 -0
- package/dist/providers/github-copilot/utils.test.d.ts +2 -0
- package/dist/providers/github-copilot/utils.test.d.ts.map +1 -0
- package/dist/providers/github-copilot/utils.test.js +21 -0
- package/dist/providers/github-copilot/utils.test.js.map +1 -0
- package/dist/providers/openai-compatible/api.d.ts +17 -0
- package/dist/providers/openai-compatible/api.d.ts.map +1 -0
- package/dist/providers/openai-compatible/api.js +58 -0
- package/dist/providers/openai-compatible/api.js.map +1 -0
- package/dist/providers/openai-compatible/model.d.ts +17 -0
- package/dist/providers/openai-compatible/model.d.ts.map +1 -0
- package/dist/providers/openai-compatible/model.js +473 -0
- package/dist/providers/openai-compatible/model.js.map +1 -0
- package/dist/providers/openai-compatible/provider.d.ts +22 -0
- package/dist/providers/openai-compatible/provider.d.ts.map +1 -0
- package/dist/providers/openai-compatible/provider.js +43 -0
- package/dist/providers/openai-compatible/provider.js.map +1 -0
- package/dist/providers/openai-compatible/reasoning-content.test.d.ts +2 -0
- package/dist/providers/openai-compatible/reasoning-content.test.d.ts.map +1 -0
- package/dist/providers/openai-compatible/reasoning-content.test.js +258 -0
- package/dist/providers/openai-compatible/reasoning-content.test.js.map +1 -0
- package/dist/providers/openai-compatible/utils.d.ts +3 -0
- package/dist/providers/openai-compatible/utils.d.ts.map +1 -0
- package/dist/providers/openai-compatible/utils.js +11 -0
- package/dist/providers/openai-compatible/utils.js.map +1 -0
- package/dist/providers/openai-compatible-behavior-gate.test.js +42 -1
- package/dist/providers/openai-compatible-behavior-gate.test.js.map +1 -1
- package/dist/providers/openai-compatible.provider.js +2 -2
- package/dist/providers/openai-compatible.provider.js.map +1 -1
- package/dist/providers/openai-compatible.provider.test.js +1 -1
- package/dist/providers/openai-compatible.provider.test.js.map +1 -1
- package/dist/providers/openrouter/api.d.ts +39 -0
- package/dist/providers/openrouter/api.d.ts.map +1 -0
- package/dist/providers/openrouter/api.js +164 -0
- package/dist/providers/openrouter/api.js.map +1 -0
- package/dist/providers/openrouter/converters.d.ts +8 -0
- package/dist/providers/openrouter/converters.d.ts.map +1 -0
- package/dist/providers/openrouter/converters.js +391 -0
- package/dist/providers/openrouter/converters.js.map +1 -0
- package/dist/providers/openrouter/converters.test.d.ts +2 -0
- package/dist/providers/openrouter/converters.test.d.ts.map +1 -0
- package/dist/providers/openrouter/converters.test.js +179 -0
- package/dist/providers/openrouter/converters.test.js.map +1 -0
- package/dist/providers/openrouter/index.d.ts +4 -0
- package/dist/providers/openrouter/index.d.ts.map +1 -0
- package/dist/providers/openrouter/index.js +4 -0
- package/dist/providers/openrouter/index.js.map +1 -0
- package/dist/providers/openrouter/model.d.ts +14 -0
- package/dist/providers/openrouter/model.d.ts.map +1 -0
- package/dist/providers/openrouter/model.js +457 -0
- package/dist/providers/openrouter/model.js.map +1 -0
- package/dist/providers/openrouter/provider.d.ts +15 -0
- package/dist/providers/openrouter/provider.d.ts.map +1 -0
- package/dist/providers/openrouter/provider.js +21 -0
- package/dist/providers/openrouter/provider.js.map +1 -0
- package/dist/providers/openrouter/reasoning-content.test.d.ts +2 -0
- package/dist/providers/openrouter/reasoning-content.test.d.ts.map +1 -0
- package/dist/providers/openrouter/reasoning-content.test.js +122 -0
- package/dist/providers/openrouter/reasoning-content.test.js.map +1 -0
- package/dist/providers/openrouter/utils.d.ts +11 -0
- package/dist/providers/openrouter/utils.d.ts.map +1 -0
- package/dist/providers/openrouter/utils.js +29 -0
- package/dist/providers/openrouter/utils.js.map +1 -0
- package/dist/providers/openrouter.api.retry.test.d.ts +2 -0
- package/dist/providers/openrouter.api.retry.test.d.ts.map +1 -0
- package/dist/providers/openrouter.api.retry.test.js +148 -0
- package/dist/providers/openrouter.api.retry.test.js.map +1 -0
- package/dist/providers/openrouter.provider.js +1 -1
- package/dist/providers/openrouter.provider.js.map +1 -1
- package/dist/providers/openrouter.test.d.ts +2 -0
- package/dist/providers/openrouter.test.d.ts.map +1 -0
- package/dist/providers/openrouter.test.js +1449 -0
- package/dist/providers/openrouter.test.js.map +1 -0
- package/dist/safety-checker.js +57 -0
- package/dist/services/approval-rejection-interceptor.test.js +5 -0
- package/dist/services/approval-rejection-interceptor.test.js.map +1 -1
- package/dist/services/check_mock.d.ts +2 -0
- package/dist/services/check_mock.d.ts.map +1 -0
- package/dist/services/check_mock.js +22 -0
- package/dist/services/check_mock.js.map +1 -0
- package/dist/services/conversation-events.d.ts +25 -1
- package/dist/services/conversation-events.d.ts.map +1 -1
- package/dist/services/conversation-service.test.d.ts +2 -0
- package/dist/services/conversation-service.test.d.ts.map +1 -0
- package/dist/services/conversation-service.test.js +156 -0
- package/dist/services/conversation-service.test.js.map +1 -0
- package/dist/services/conversation-session.auto-approval.test.js +52 -0
- package/dist/services/conversation-session.auto-approval.test.js.map +1 -1
- package/dist/services/conversation-session.d.ts.map +1 -1
- package/dist/services/conversation-session.js +64 -30
- package/dist/services/conversation-session.js.map +1 -1
- package/dist/services/interruption-info.js +1 -1
- package/dist/services/interruption-info.js.map +1 -1
- package/dist/services/interruption-info.test.js +12 -0
- package/dist/services/interruption-info.test.js.map +1 -1
- package/dist/services/rtk-service.d.ts.map +1 -1
- package/dist/services/rtk-service.js +104 -9
- package/dist/services/rtk-service.js.map +1 -1
- package/dist/services/rtk-service.test.js +55 -20
- package/dist/services/rtk-service.test.js.map +1 -1
- package/dist/services/stream-event-processor.d.ts.map +1 -1
- package/dist/services/stream-event-processor.js +19 -8
- package/dist/services/stream-event-processor.js.map +1 -1
- package/dist/services/subagents/subagent-manager.d.ts +16 -0
- package/dist/services/subagents/subagent-manager.d.ts.map +1 -0
- package/dist/services/subagents/subagent-manager.js +530 -0
- package/dist/services/subagents/subagent-manager.js.map +1 -0
- package/dist/services/subagents/subagent-manager.test.d.ts +2 -0
- package/dist/services/subagents/subagent-manager.test.d.ts.map +1 -0
- package/dist/services/subagents/subagent-manager.test.js +690 -0
- package/dist/services/subagents/subagent-manager.test.js.map +1 -0
- package/dist/services/subagents/subagent-session.d.ts +26 -0
- package/dist/services/subagents/subagent-session.d.ts.map +1 -0
- package/dist/services/subagents/subagent-session.js +86 -0
- package/dist/services/subagents/subagent-session.js.map +1 -0
- package/dist/services/subagents/subagent-session.test.d.ts +2 -0
- package/dist/services/subagents/subagent-session.test.d.ts.map +1 -0
- package/dist/services/subagents/subagent-session.test.js +118 -0
- package/dist/services/subagents/subagent-session.test.js.map +1 -0
- package/dist/services/subagents/types.d.ts +35 -0
- package/dist/services/subagents/types.d.ts.map +1 -0
- package/dist/services/subagents/types.js +2 -0
- package/dist/services/subagents/types.js.map +1 -0
- package/dist/services/terminal-result-collector.d.ts.map +1 -1
- package/dist/services/terminal-result-collector.js +30 -3
- package/dist/services/terminal-result-collector.js.map +1 -1
- package/dist/services/terminal-result-collector.test.js +81 -0
- package/dist/services/terminal-result-collector.test.js.map +1 -1
- package/dist/tools/apply-patch.d.ts +2 -2
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js.map +1 -1
- package/dist/tools/ask-mentor.d.ts +2 -2
- package/dist/tools/ask-mentor.d.ts.map +1 -1
- package/dist/tools/ask-mentor.js.map +1 -1
- package/dist/tools/bash.d.ts +10 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +55 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/code-context.d.ts +3 -3
- package/dist/tools/code-context.d.ts.map +1 -1
- package/dist/tools/code-context.js.map +1 -1
- package/dist/tools/code-context.test.js +1 -2
- package/dist/tools/code-context.test.js.map +1 -1
- package/dist/tools/command-message-formatters.d.ts +5 -0
- package/dist/tools/command-message-formatters.d.ts.map +1 -0
- package/dist/tools/command-message-formatters.js +13 -0
- package/dist/tools/command-message-formatters.js.map +1 -0
- package/dist/tools/command-message-formatters.test.d.ts +2 -0
- package/dist/tools/command-message-formatters.test.d.ts.map +1 -0
- package/dist/tools/command-message-formatters.test.js +23 -0
- package/dist/tools/command-message-formatters.test.js.map +1 -0
- package/dist/tools/create-file.d.ts +2 -2
- package/dist/tools/create-file.d.ts.map +1 -1
- package/dist/tools/create-file.js +15 -9
- package/dist/tools/create-file.js.map +1 -1
- package/dist/tools/create-file.test.js +11 -0
- package/dist/tools/create-file.test.js.map +1 -1
- package/dist/tools/file-locks.d.ts +1 -0
- package/dist/tools/file-locks.d.ts.map +1 -1
- package/dist/tools/file-locks.js +16 -0
- package/dist/tools/file-locks.js.map +1 -1
- package/dist/tools/find-files.d.ts +2 -2
- package/dist/tools/find-files.d.ts.map +1 -1
- package/dist/tools/find-files.js.map +1 -1
- package/dist/tools/find-files.test.js +1 -2
- package/dist/tools/find-files.test.js.map +1 -1
- package/dist/tools/grep.d.ts +2 -2
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/grep.test.d.ts +2 -0
- package/dist/tools/grep.test.d.ts.map +1 -0
- package/dist/tools/read-file.d.ts +2 -2
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/read-file.test.js +2 -3
- package/dist/tools/read-file.test.js.map +1 -1
- package/dist/tools/run-subagent.d.ts +14 -0
- package/dist/tools/run-subagent.d.ts.map +1 -0
- package/dist/tools/run-subagent.js +196 -0
- package/dist/tools/run-subagent.js.map +1 -0
- package/dist/tools/run-subagent.test.d.ts +2 -0
- package/dist/tools/run-subagent.test.d.ts.map +1 -0
- package/dist/tools/run-subagent.test.js +159 -0
- package/dist/tools/run-subagent.test.js.map +1 -0
- package/dist/tools/search-replace.d.ts +2 -2
- package/dist/tools/search-replace.d.ts.map +1 -1
- package/dist/tools/search-replace.js +49 -6
- package/dist/tools/search-replace.js.map +1 -1
- package/dist/tools/search-replace.test.js +78 -0
- package/dist/tools/search-replace.test.js.map +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/shell.d.ts +2 -2
- package/dist/tools/shell.d.ts.map +1 -1
- package/dist/tools/shell.js.map +1 -1
- package/dist/tools/shell.test.js +137 -11
- package/dist/tools/shell.test.js.map +1 -1
- package/dist/tools/types.d.ts +2 -1
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/utils.d.ts.map +1 -1
- package/dist/tools/utils.js +11 -1
- package/dist/tools/utils.js.map +1 -1
- package/dist/tools/utils.test.js +13 -0
- package/dist/tools/utils.test.js.map +1 -1
- package/dist/tools/web-fetch.d.ts +2 -2
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-search.d.ts +2 -2
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js.map +1 -1
- package/dist/utils/clipboard.d.ts +2 -2
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +58 -22
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/clipboard.test.js +25 -6
- package/dist/utils/clipboard.test.js.map +1 -1
- package/dist/utils/command-safety/handlers/sed-handler.d.ts.map +1 -1
- package/dist/utils/command-safety/handlers/sed-handler.js +10 -7
- package/dist/utils/command-safety/handlers/sed-handler.js.map +1 -1
- package/dist/utils/command-safety/index.d.ts.map +1 -1
- package/dist/utils/command-safety/index.js +153 -117
- package/dist/utils/command-safety/index.js.map +1 -1
- package/dist/utils/command-safety/path-analysis.d.ts.map +1 -1
- package/dist/utils/command-safety/path-analysis.js +6 -2
- package/dist/utils/command-safety/path-analysis.js.map +1 -1
- package/dist/utils/command-safety/utils.js +2 -2
- package/dist/utils/command-safety/utils.js.map +1 -1
- package/dist/utils/command-safety.d.ts +21 -0
- package/dist/utils/command-safety.d.ts.map +1 -0
- package/dist/utils/command-safety.devnull.test.d.ts +2 -0
- package/dist/utils/command-safety.devnull.test.d.ts.map +1 -0
- package/dist/utils/command-safety.devnull.test.js +13 -0
- package/dist/utils/command-safety.devnull.test.js.map +1 -0
- package/dist/utils/command-safety.js +348 -0
- package/dist/utils/command-safety.js.map +1 -0
- package/dist/utils/command-safety.path.test.js +15 -1
- package/dist/utils/command-safety.path.test.js.map +1 -1
- package/dist/utils/conversation-event-handler.d.ts +15 -0
- package/dist/utils/conversation-event-handler.d.ts.map +1 -1
- package/dist/utils/conversation-event-handler.js +104 -8
- package/dist/utils/conversation-event-handler.js.map +1 -1
- package/dist/utils/conversation-event-handler.test.js +185 -2
- package/dist/utils/conversation-event-handler.test.js.map +1 -1
- package/dist/utils/conversation-utils.d.ts +1 -0
- package/dist/utils/conversation-utils.d.ts.map +1 -1
- package/dist/utils/conversation-utils.js +1 -0
- package/dist/utils/conversation-utils.js.map +1 -1
- package/dist/utils/extract-command-messages.d.ts.map +1 -1
- package/dist/utils/extract-command-messages.js +2 -21
- package/dist/utils/extract-command-messages.js.map +1 -1
- package/dist/utils/extract-command-messages.test.d.ts +2 -0
- package/dist/utils/extract-command-messages.test.d.ts.map +1 -0
- package/dist/utils/provider-traffic-extractor.repro.test.d.ts +2 -0
- package/dist/utils/provider-traffic-extractor.repro.test.d.ts.map +1 -0
- package/dist/utils/provider-traffic-extractor.repro.test.js.map +1 -0
- package/dist/utils/streaming-session-factory.d.ts.map +1 -1
- package/dist/utils/streaming-session-factory.js +16 -1
- package/dist/utils/streaming-session-factory.js.map +1 -1
- package/dist/utils/streaming-session-factory.test.js +42 -0
- package/dist/utils/streaming-session-factory.test.js.map +1 -1
- package/dist/utils/token-usage.d.ts +13 -0
- package/dist/utils/token-usage.d.ts.map +1 -1
- package/dist/utils/token-usage.js +92 -1
- package/dist/utils/token-usage.js.map +1 -1
- package/dist/utils/token-usage.test.js +62 -1
- package/dist/utils/token-usage.test.js.map +1 -1
- package/package.json +2 -2
- package/dist/providers/openai.provider.test.d.ts +0 -2
- package/dist/providers/openai.provider.test.d.ts.map +0 -1
- package/dist/providers/openai.provider.test.js +0 -22
- package/dist/providers/openai.provider.test.js.map +0 -1
- package/dist/utils/project-tree.d.ts +0 -14
- package/dist/utils/project-tree.d.ts.map +0 -1
- package/dist/utils/project-tree.js +0 -111
- package/dist/utils/project-tree.js.map +0 -1
- package/dist/utils/project-tree.test.d.ts +0 -2
- package/dist/utils/project-tree.test.d.ts.map +0 -1
- package/dist/utils/project-tree.test.js +0 -149
- package/dist/utils/project-tree.test.js.map +0 -1
- package/dist/utils/terminal-clear.d.ts +0 -7
- package/dist/utils/terminal-clear.d.ts.map +0 -1
- package/dist/utils/terminal-clear.js +0 -5
- package/dist/utils/terminal-clear.js.map +0 -1
- package/dist/utils/terminal-clear.test.d.ts +0 -2
- package/dist/utils/terminal-clear.test.d.ts.map +0 -1
- package/dist/utils/terminal-clear.test.js +0 -13
- package/dist/utils/terminal-clear.test.js.map +0 -1
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { SubagentManager } from './subagent-manager.js';
|
|
5
|
+
import { registerProvider } from '../../providers/registry.js';
|
|
6
|
+
// ========== Mock Utilities ==========
|
|
7
|
+
function createMockLogger() {
|
|
8
|
+
return {
|
|
9
|
+
debug: () => { },
|
|
10
|
+
info: () => { },
|
|
11
|
+
warn: () => { },
|
|
12
|
+
error: () => { },
|
|
13
|
+
security: () => { },
|
|
14
|
+
setCorrelationId: () => { },
|
|
15
|
+
clearCorrelationId: () => { },
|
|
16
|
+
getCorrelationId: () => undefined,
|
|
17
|
+
log: () => { },
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function createMockSettings(values = {}) {
|
|
21
|
+
const store = { ...values };
|
|
22
|
+
return {
|
|
23
|
+
get: (key) => store[key],
|
|
24
|
+
set: (key, value) => {
|
|
25
|
+
store[key] = value;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// ========== Provider Mocks ==========
|
|
30
|
+
let mentorManagerRunnerCalls = [];
|
|
31
|
+
let mentorManagerResponseCounter = 0;
|
|
32
|
+
let mentorManagerProviderRegistered = false;
|
|
33
|
+
function ensureMentorManagerProviderRegistered() {
|
|
34
|
+
if (!mentorManagerProviderRegistered) {
|
|
35
|
+
registerProvider({
|
|
36
|
+
id: 'mock-mentor-manager',
|
|
37
|
+
label: 'Mock Mentor Manager',
|
|
38
|
+
createRunner: () => ({
|
|
39
|
+
run: async (_agent, _input, _options) => {
|
|
40
|
+
mentorManagerRunnerCalls.push({ input: _input, options: _options, agent: _agent });
|
|
41
|
+
mentorManagerResponseCounter++;
|
|
42
|
+
return {
|
|
43
|
+
status: 'completed',
|
|
44
|
+
finalOutput: `mentor-response-${mentorManagerResponseCounter}`,
|
|
45
|
+
responseId: `resp-${mentorManagerResponseCounter}`,
|
|
46
|
+
history: [],
|
|
47
|
+
messages: [],
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
fetchModels: async () => [{ id: 'mock-model' }],
|
|
52
|
+
});
|
|
53
|
+
mentorManagerProviderRegistered = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
let explorerManagerProviderRegistered = false;
|
|
57
|
+
let explorerRunnerCalls = [];
|
|
58
|
+
function ensureExplorerProviderRegistered() {
|
|
59
|
+
if (!explorerManagerProviderRegistered) {
|
|
60
|
+
registerProvider({
|
|
61
|
+
id: 'mock-explorer-provider',
|
|
62
|
+
label: 'Mock Explorer Provider',
|
|
63
|
+
createRunner: () => ({
|
|
64
|
+
run: async (_agent, input, _options) => {
|
|
65
|
+
explorerRunnerCalls.push({ input, agent: _agent });
|
|
66
|
+
return {
|
|
67
|
+
status: 'completed',
|
|
68
|
+
finalOutput: 'Found the relevant files.',
|
|
69
|
+
history: [],
|
|
70
|
+
messages: [],
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
fetchModels: async () => [{ id: 'mock-model' }],
|
|
75
|
+
});
|
|
76
|
+
explorerManagerProviderRegistered = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
test.beforeEach(() => {
|
|
80
|
+
mentorManagerRunnerCalls = [];
|
|
81
|
+
mentorManagerResponseCounter = 0;
|
|
82
|
+
explorerRunnerCalls = [];
|
|
83
|
+
ensureMentorManagerProviderRegistered();
|
|
84
|
+
ensureExplorerProviderRegistered();
|
|
85
|
+
});
|
|
86
|
+
// ========== run() with unknown role ==========
|
|
87
|
+
test.serial('run() returns failed result for unknown role', async (t) => {
|
|
88
|
+
const settings = createMockSettings({
|
|
89
|
+
'agent.model': 'mock-model',
|
|
90
|
+
'agent.provider': 'mock-mentor-manager',
|
|
91
|
+
});
|
|
92
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
93
|
+
const result = await manager.run({ role: 'nonexistent-role-xyz', task: 'do something' });
|
|
94
|
+
t.is(result.status, 'failed');
|
|
95
|
+
t.truthy(result.error);
|
|
96
|
+
t.true(result.error.includes('nonexistent-role-xyz'));
|
|
97
|
+
});
|
|
98
|
+
// ========== Mentor role ==========
|
|
99
|
+
test.serial('run() with mentor role uses mentorModel setting', async (t) => {
|
|
100
|
+
const settings = createMockSettings({
|
|
101
|
+
'agent.model': 'main-model',
|
|
102
|
+
'agent.provider': 'mock-mentor-manager',
|
|
103
|
+
'agent.mentorModel': 'mentor-model',
|
|
104
|
+
'agent.mentorProvider': 'mock-mentor-manager',
|
|
105
|
+
});
|
|
106
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
107
|
+
const result = await manager.run({ role: 'mentor', task: 'advise me' });
|
|
108
|
+
t.is(result.status, 'completed');
|
|
109
|
+
t.is(result.role, 'mentor');
|
|
110
|
+
t.true(result.finalText.includes('mentor-response'));
|
|
111
|
+
t.is(mentorManagerRunnerCalls.length, 1);
|
|
112
|
+
t.is(mentorManagerRunnerCalls[0].agent.model, 'mentor-model');
|
|
113
|
+
});
|
|
114
|
+
test.serial('run() with mentor role fails when mentorModel is not set', async (t) => {
|
|
115
|
+
const settings = createMockSettings({
|
|
116
|
+
'agent.model': 'main-model',
|
|
117
|
+
'agent.provider': 'mock-mentor-manager',
|
|
118
|
+
});
|
|
119
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
120
|
+
const result = await manager.run({ role: 'mentor', task: 'advise me' });
|
|
121
|
+
t.is(result.status, 'failed');
|
|
122
|
+
t.true(result.error.includes('Mentor model is not configured'));
|
|
123
|
+
});
|
|
124
|
+
test.serial('run() mentor maintains conversation history across calls', async (t) => {
|
|
125
|
+
const settings = createMockSettings({
|
|
126
|
+
'agent.model': 'main-model',
|
|
127
|
+
'agent.provider': 'mock-mentor-manager',
|
|
128
|
+
'agent.mentorModel': 'mentor-model',
|
|
129
|
+
'agent.mentorProvider': 'mock-mentor-manager',
|
|
130
|
+
});
|
|
131
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
132
|
+
await manager.run({ role: 'mentor', task: 'first question' });
|
|
133
|
+
await manager.run({ role: 'mentor', task: 'second question' });
|
|
134
|
+
t.is(mentorManagerRunnerCalls.length, 2);
|
|
135
|
+
// Second call should have history (array with previous messages)
|
|
136
|
+
t.true(Array.isArray(mentorManagerRunnerCalls[1].input));
|
|
137
|
+
t.true(mentorManagerRunnerCalls[1].input.length > 1);
|
|
138
|
+
});
|
|
139
|
+
test.serial('resetMentorSession() clears conversation history', async (t) => {
|
|
140
|
+
const settings = createMockSettings({
|
|
141
|
+
'agent.model': 'main-model',
|
|
142
|
+
'agent.provider': 'mock-mentor-manager',
|
|
143
|
+
'agent.mentorModel': 'mentor-model',
|
|
144
|
+
'agent.mentorProvider': 'mock-mentor-manager',
|
|
145
|
+
});
|
|
146
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
147
|
+
await manager.run({ role: 'mentor', task: 'first question' });
|
|
148
|
+
t.true(mentorManagerRunnerCalls[0].input.length >= 1);
|
|
149
|
+
manager.resetMentorSession();
|
|
150
|
+
await manager.run({ role: 'mentor', task: 'fresh question' });
|
|
151
|
+
// After reset, second call should have only 1 message (fresh start)
|
|
152
|
+
t.is(mentorManagerRunnerCalls[1].input.length, 1);
|
|
153
|
+
});
|
|
154
|
+
// ========== Explorer role ==========
|
|
155
|
+
test.serial('run() with explorer role returns SubagentResult', async (t) => {
|
|
156
|
+
const settings = createMockSettings({
|
|
157
|
+
'agent.model': 'mock-model',
|
|
158
|
+
'agent.provider': 'mock-explorer-provider',
|
|
159
|
+
});
|
|
160
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
161
|
+
const result = await manager.run({ role: 'explorer', task: 'find all TypeScript files' });
|
|
162
|
+
t.is(result.status, 'completed');
|
|
163
|
+
t.is(result.role, 'explorer');
|
|
164
|
+
t.truthy(result.agentId);
|
|
165
|
+
t.is(result.filesChanged.length, 0);
|
|
166
|
+
t.truthy(result.finalText);
|
|
167
|
+
});
|
|
168
|
+
test.serial('run() with explorer role uses read-only tools only', async (t) => {
|
|
169
|
+
const settings = createMockSettings({
|
|
170
|
+
'agent.model': 'mock-model',
|
|
171
|
+
'agent.provider': 'mock-explorer-provider',
|
|
172
|
+
});
|
|
173
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
174
|
+
await manager.run({ role: 'explorer', task: 'find files' });
|
|
175
|
+
t.is(explorerRunnerCalls.length, 1);
|
|
176
|
+
const agent = explorerRunnerCalls[0].agent;
|
|
177
|
+
const toolNames = agent.tools.map((t) => t.name);
|
|
178
|
+
// Explorer should have read tools
|
|
179
|
+
t.true(toolNames.includes('read_file'));
|
|
180
|
+
t.true(toolNames.includes('grep'));
|
|
181
|
+
t.true(toolNames.includes('find_files'));
|
|
182
|
+
// Explorer should NOT have write tools
|
|
183
|
+
t.false(toolNames.includes('apply_patch'));
|
|
184
|
+
t.false(toolNames.includes('search_replace'));
|
|
185
|
+
t.false(toolNames.includes('create_file'));
|
|
186
|
+
// Explorer should NOT have web tools by default
|
|
187
|
+
t.false(toolNames.includes('web_search'));
|
|
188
|
+
t.false(toolNames.includes('web_fetch'));
|
|
189
|
+
});
|
|
190
|
+
// ========== Researcher role ==========
|
|
191
|
+
let researcherRunnerCalls = [];
|
|
192
|
+
let researcherProviderRegistered = false;
|
|
193
|
+
function ensureResearcherProviderRegistered() {
|
|
194
|
+
if (!researcherProviderRegistered) {
|
|
195
|
+
registerProvider({
|
|
196
|
+
id: 'mock-researcher-provider',
|
|
197
|
+
label: 'Mock Researcher Provider',
|
|
198
|
+
createRunner: () => ({
|
|
199
|
+
run: async (_agent, _input, _options) => {
|
|
200
|
+
researcherRunnerCalls.push({ agent: _agent });
|
|
201
|
+
return {
|
|
202
|
+
status: 'completed',
|
|
203
|
+
finalOutput: 'Research findings.',
|
|
204
|
+
history: [],
|
|
205
|
+
messages: [],
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
fetchModels: async () => [{ id: 'mock-model' }],
|
|
210
|
+
});
|
|
211
|
+
researcherProviderRegistered = true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
test.beforeEach(() => {
|
|
215
|
+
researcherRunnerCalls = [];
|
|
216
|
+
ensureResearcherProviderRegistered();
|
|
217
|
+
});
|
|
218
|
+
test.serial('run() with researcher role includes web tools', async (t) => {
|
|
219
|
+
const settings = createMockSettings({
|
|
220
|
+
'agent.model': 'mock-model',
|
|
221
|
+
'agent.provider': 'mock-researcher-provider',
|
|
222
|
+
});
|
|
223
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
224
|
+
await manager.run({ role: 'researcher', task: 'look up the latest TypeScript release' });
|
|
225
|
+
t.is(researcherRunnerCalls.length, 1);
|
|
226
|
+
const agent = researcherRunnerCalls[0].agent;
|
|
227
|
+
const toolNames = agent.tools.map((t) => t.name);
|
|
228
|
+
// Researcher should have read and web tools
|
|
229
|
+
t.true(toolNames.includes('read_file'));
|
|
230
|
+
t.true(toolNames.includes('web_search'));
|
|
231
|
+
t.true(toolNames.includes('web_fetch'));
|
|
232
|
+
// But not write tools
|
|
233
|
+
t.false(toolNames.includes('apply_patch'));
|
|
234
|
+
});
|
|
235
|
+
// ========== Worker role ==========
|
|
236
|
+
let workerRunnerCalls = [];
|
|
237
|
+
let workerProviderRegistered = false;
|
|
238
|
+
function ensureWorkerProviderRegistered() {
|
|
239
|
+
if (!workerProviderRegistered) {
|
|
240
|
+
registerProvider({
|
|
241
|
+
id: 'mock-worker-provider',
|
|
242
|
+
label: 'Mock Worker Provider',
|
|
243
|
+
createRunner: () => ({
|
|
244
|
+
run: async (_agent, _input, _options) => {
|
|
245
|
+
workerRunnerCalls.push({ agent: _agent });
|
|
246
|
+
return {
|
|
247
|
+
status: 'completed',
|
|
248
|
+
finalOutput: 'Changes applied.',
|
|
249
|
+
history: [],
|
|
250
|
+
messages: [],
|
|
251
|
+
};
|
|
252
|
+
},
|
|
253
|
+
}),
|
|
254
|
+
fetchModels: async () => [{ id: 'mock-model' }],
|
|
255
|
+
});
|
|
256
|
+
workerProviderRegistered = true;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
test.beforeEach(() => {
|
|
260
|
+
workerRunnerCalls = [];
|
|
261
|
+
ensureWorkerProviderRegistered();
|
|
262
|
+
});
|
|
263
|
+
test.serial('run() with worker role includes write tools', async (t) => {
|
|
264
|
+
const settings = createMockSettings({
|
|
265
|
+
'agent.model': 'mock-model',
|
|
266
|
+
'agent.provider': 'mock-worker-provider',
|
|
267
|
+
});
|
|
268
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
269
|
+
await manager.run({ role: 'worker', task: 'update the README.md file' });
|
|
270
|
+
t.is(workerRunnerCalls.length, 1);
|
|
271
|
+
const agent = workerRunnerCalls[0].agent;
|
|
272
|
+
const toolNames = agent.tools.map((t) => t.name);
|
|
273
|
+
// Worker should have read AND write tools
|
|
274
|
+
t.true(toolNames.includes('read_file'));
|
|
275
|
+
t.true(toolNames.includes('apply_patch'));
|
|
276
|
+
t.true(toolNames.includes('search_replace'));
|
|
277
|
+
t.true(toolNames.includes('create_file'));
|
|
278
|
+
// But not web or shell
|
|
279
|
+
t.false(toolNames.includes('web_search'));
|
|
280
|
+
t.false(toolNames.includes('shell'));
|
|
281
|
+
});
|
|
282
|
+
test.serial('run() result contains agentId and correct role', async (t) => {
|
|
283
|
+
const settings = createMockSettings({
|
|
284
|
+
'agent.model': 'mock-model',
|
|
285
|
+
'agent.provider': 'mock-worker-provider',
|
|
286
|
+
});
|
|
287
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
288
|
+
const result = await manager.run({ role: 'worker', task: 'do some work' });
|
|
289
|
+
t.is(result.role, 'worker');
|
|
290
|
+
t.truthy(result.agentId);
|
|
291
|
+
t.true(result.agentId.length > 0);
|
|
292
|
+
t.is(result.filesChanged.length, 0);
|
|
293
|
+
t.is(result.status, 'completed');
|
|
294
|
+
});
|
|
295
|
+
// ========== Write boundary enforcement ==========
|
|
296
|
+
let boundaryWorkerCalls = [];
|
|
297
|
+
let boundaryWorkerRegistered = false;
|
|
298
|
+
function ensureBoundaryWorkerProviderRegistered() {
|
|
299
|
+
if (!boundaryWorkerRegistered) {
|
|
300
|
+
registerProvider({
|
|
301
|
+
id: 'mock-boundary-worker',
|
|
302
|
+
label: 'Mock Boundary Worker',
|
|
303
|
+
createRunner: () => ({
|
|
304
|
+
run: async (_agent, _input, _options) => {
|
|
305
|
+
boundaryWorkerCalls.push({ agent: _agent });
|
|
306
|
+
return {
|
|
307
|
+
status: 'completed',
|
|
308
|
+
finalOutput: 'Done.',
|
|
309
|
+
history: [],
|
|
310
|
+
messages: [],
|
|
311
|
+
};
|
|
312
|
+
},
|
|
313
|
+
}),
|
|
314
|
+
fetchModels: async () => [{ id: 'mock-model' }],
|
|
315
|
+
});
|
|
316
|
+
boundaryWorkerRegistered = true;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
test.beforeEach(() => {
|
|
320
|
+
boundaryWorkerCalls = [];
|
|
321
|
+
ensureBoundaryWorkerProviderRegistered();
|
|
322
|
+
});
|
|
323
|
+
test.serial('worker write tool rejects paths outside writeBoundary', async (t) => {
|
|
324
|
+
const tmpDir = fs.mkdtempSync(path.join('/tmp', 'term2-test-boundary-'));
|
|
325
|
+
t.teardown(() => fs.rmSync(tmpDir, { recursive: true, force: true }));
|
|
326
|
+
const mockExecutionContext = {
|
|
327
|
+
getCwd: () => tmpDir,
|
|
328
|
+
isRemote: () => false,
|
|
329
|
+
getSSHService: () => undefined,
|
|
330
|
+
};
|
|
331
|
+
const settings = createMockSettings({
|
|
332
|
+
'agent.model': 'mock-model',
|
|
333
|
+
'agent.provider': 'mock-boundary-worker',
|
|
334
|
+
});
|
|
335
|
+
const manager = new SubagentManager({
|
|
336
|
+
logger: createMockLogger(),
|
|
337
|
+
settings,
|
|
338
|
+
executionContext: mockExecutionContext,
|
|
339
|
+
});
|
|
340
|
+
await manager.run({
|
|
341
|
+
role: 'worker',
|
|
342
|
+
task: 'update a file',
|
|
343
|
+
writeBoundary: ['.'],
|
|
344
|
+
});
|
|
345
|
+
t.is(boundaryWorkerCalls.length, 1);
|
|
346
|
+
const agent = boundaryWorkerCalls[0].agent;
|
|
347
|
+
const applyPatch = agent.tools.find((t) => t.name === 'apply_patch');
|
|
348
|
+
const searchReplace = agent.tools.find((t) => t.name === 'search_replace');
|
|
349
|
+
t.truthy(applyPatch);
|
|
350
|
+
t.truthy(searchReplace);
|
|
351
|
+
// Call with a path inside the boundary — should NOT be rejected for boundary violation
|
|
352
|
+
const insideResult = await applyPatch.invoke({}, JSON.stringify({
|
|
353
|
+
type: 'create_file',
|
|
354
|
+
path: 'newfile.ts',
|
|
355
|
+
diff: '+hello\n',
|
|
356
|
+
}), {});
|
|
357
|
+
const insideParsed = JSON.parse(insideResult);
|
|
358
|
+
const insideError = insideParsed?.output?.[0]?.error;
|
|
359
|
+
t.true(!insideError || !insideError.includes('outside the allowed write boundary'));
|
|
360
|
+
// Call with a path outside the boundary — should be rejected
|
|
361
|
+
const outsideResult = await applyPatch.invoke({}, JSON.stringify({
|
|
362
|
+
type: 'create_file',
|
|
363
|
+
path: '../outside/file.ts',
|
|
364
|
+
diff: '+hello\n',
|
|
365
|
+
}), {});
|
|
366
|
+
const outsideParsed = JSON.parse(outsideResult);
|
|
367
|
+
t.true(outsideParsed?.output?.[0]?.error?.includes('outside the allowed write boundary'));
|
|
368
|
+
t.false(outsideParsed?.output?.[0]?.success ?? true);
|
|
369
|
+
const outsideNeedsApproval = await applyPatch.needsApproval({}, {
|
|
370
|
+
type: 'create_file',
|
|
371
|
+
path: '../outside/file.ts',
|
|
372
|
+
diff: '+hello\n',
|
|
373
|
+
});
|
|
374
|
+
t.is(outsideNeedsApproval, false);
|
|
375
|
+
// Batch search_replace: all replacement paths must be boundary-checked
|
|
376
|
+
const batchOutsideResult = await searchReplace.invoke({}, JSON.stringify({
|
|
377
|
+
replacements: [
|
|
378
|
+
{
|
|
379
|
+
path: 'inside.ts',
|
|
380
|
+
search_content: '',
|
|
381
|
+
replace_content: 'ok',
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
path: '../outside.ts',
|
|
385
|
+
search_content: '',
|
|
386
|
+
replace_content: 'no',
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
}), {});
|
|
390
|
+
const batchOutsideParsed = JSON.parse(batchOutsideResult);
|
|
391
|
+
t.true(batchOutsideParsed?.output?.[0]?.error?.includes('outside the allowed write boundary'));
|
|
392
|
+
t.false(batchOutsideParsed?.output?.[0]?.success ?? true);
|
|
393
|
+
});
|
|
394
|
+
test.serial('worker in-boundary write is auto-approved (boundary is the grant)', async (t) => {
|
|
395
|
+
const tmpDir = fs.mkdtempSync(path.join('/tmp', 'term2-test-worker-approval-'));
|
|
396
|
+
t.teardown(() => fs.rmSync(tmpDir, { recursive: true, force: true }));
|
|
397
|
+
const mockExecutionContext = {
|
|
398
|
+
getCwd: () => tmpDir,
|
|
399
|
+
isRemote: () => false,
|
|
400
|
+
getSSHService: () => undefined,
|
|
401
|
+
};
|
|
402
|
+
const settings = createMockSettings({
|
|
403
|
+
'agent.model': 'mock-model',
|
|
404
|
+
'agent.provider': 'mock-boundary-worker',
|
|
405
|
+
'app.editMode': false,
|
|
406
|
+
});
|
|
407
|
+
const manager = new SubagentManager({
|
|
408
|
+
logger: createMockLogger(),
|
|
409
|
+
settings,
|
|
410
|
+
executionContext: mockExecutionContext,
|
|
411
|
+
});
|
|
412
|
+
await manager.run({
|
|
413
|
+
role: 'worker',
|
|
414
|
+
task: 'update files',
|
|
415
|
+
writeBoundary: ['.'],
|
|
416
|
+
});
|
|
417
|
+
t.is(boundaryWorkerCalls.length, 1);
|
|
418
|
+
const agent = boundaryWorkerCalls[0].agent;
|
|
419
|
+
const createFile = agent.tools.find((tool) => tool.name === 'create_file');
|
|
420
|
+
t.truthy(createFile);
|
|
421
|
+
// The writeBoundary is the worker's permission grant: an in-boundary write
|
|
422
|
+
// requires no interactive approval (there is no foreground approval channel
|
|
423
|
+
// for a subagent running inside a blocked parent tool call). Edit mode is off
|
|
424
|
+
// but the write still does not need approval.
|
|
425
|
+
const inBoundaryNeedsApproval = await createFile.needsApproval({}, { path: 'a.ts', content: 'x' });
|
|
426
|
+
t.is(inBoundaryNeedsApproval, false);
|
|
427
|
+
// Out-of-boundary writes are also not surfaced for approval; they are
|
|
428
|
+
// rejected deterministically by execute() instead.
|
|
429
|
+
const outBoundaryNeedsApproval = await createFile.needsApproval({}, { path: '../escape.ts', content: 'x' });
|
|
430
|
+
t.is(outBoundaryNeedsApproval, false);
|
|
431
|
+
});
|
|
432
|
+
test.serial('worker without explicit writeBoundary cannot write outside the workspace', async (t) => {
|
|
433
|
+
const tmpDir = fs.mkdtempSync(path.join('/tmp', 'term2-test-worker-default-boundary-'));
|
|
434
|
+
t.teardown(() => fs.rmSync(tmpDir, { recursive: true, force: true }));
|
|
435
|
+
const mockExecutionContext = {
|
|
436
|
+
getCwd: () => tmpDir,
|
|
437
|
+
isRemote: () => false,
|
|
438
|
+
getSSHService: () => undefined,
|
|
439
|
+
};
|
|
440
|
+
const settings = createMockSettings({
|
|
441
|
+
'agent.model': 'mock-model',
|
|
442
|
+
'agent.provider': 'mock-boundary-worker',
|
|
443
|
+
});
|
|
444
|
+
const manager = new SubagentManager({
|
|
445
|
+
logger: createMockLogger(),
|
|
446
|
+
settings,
|
|
447
|
+
executionContext: mockExecutionContext,
|
|
448
|
+
});
|
|
449
|
+
// No writeBoundary passed -> defaults to the workspace root.
|
|
450
|
+
await manager.run({ role: 'worker', task: 'update a file' });
|
|
451
|
+
const agent = boundaryWorkerCalls[boundaryWorkerCalls.length - 1].agent;
|
|
452
|
+
const createFile = agent.tools.find((tool) => tool.name === 'create_file');
|
|
453
|
+
t.truthy(createFile);
|
|
454
|
+
const outsideResult = await createFile.invoke({}, JSON.stringify({ path: '../outside.ts', content: 'x' }), {});
|
|
455
|
+
const outsideParsed = JSON.parse(outsideResult);
|
|
456
|
+
t.true(outsideParsed?.output?.[0]?.error?.includes('outside the allowed write boundary'));
|
|
457
|
+
t.false(outsideParsed?.output?.[0]?.success ?? true);
|
|
458
|
+
});
|
|
459
|
+
test.serial('worker filesChanged tracks only successful writes for batch operations', async (t) => {
|
|
460
|
+
const tmpDir = fs.mkdtempSync(path.join('/tmp', 'term2-test-worker-files-changed-'));
|
|
461
|
+
t.teardown(() => fs.rmSync(tmpDir, { recursive: true, force: true }));
|
|
462
|
+
registerProvider({
|
|
463
|
+
id: 'mock-worker-files-changed-provider',
|
|
464
|
+
label: 'Mock Worker Files Changed Provider',
|
|
465
|
+
createRunner: () => ({
|
|
466
|
+
run: async (agent) => {
|
|
467
|
+
const applyPatch = agent.tools.find((tool) => tool.name === 'apply_patch');
|
|
468
|
+
const searchReplace = agent.tools.find((tool) => tool.name === 'search_replace');
|
|
469
|
+
await applyPatch.invoke({}, JSON.stringify({
|
|
470
|
+
operations: [
|
|
471
|
+
{ type: 'create_file', path: 'a.txt', diff: '+hello\n' },
|
|
472
|
+
{ type: 'update_file', path: 'missing.txt', diff: '-x\n+y\n' },
|
|
473
|
+
],
|
|
474
|
+
}), {});
|
|
475
|
+
await searchReplace.invoke({}, JSON.stringify({
|
|
476
|
+
replacements: [
|
|
477
|
+
{ path: 'b.txt', search_content: '', replace_content: 'created' },
|
|
478
|
+
{ path: 'missing2.txt', search_content: 'x', replace_content: 'y' },
|
|
479
|
+
],
|
|
480
|
+
}), {});
|
|
481
|
+
return {
|
|
482
|
+
status: 'completed',
|
|
483
|
+
finalOutput: 'Done.',
|
|
484
|
+
history: [],
|
|
485
|
+
messages: [],
|
|
486
|
+
};
|
|
487
|
+
},
|
|
488
|
+
}),
|
|
489
|
+
fetchModels: async () => [{ id: 'mock-model' }],
|
|
490
|
+
});
|
|
491
|
+
const manager = new SubagentManager({
|
|
492
|
+
logger: createMockLogger(),
|
|
493
|
+
settings: createMockSettings({
|
|
494
|
+
'agent.model': 'mock-model',
|
|
495
|
+
'agent.provider': 'mock-worker-files-changed-provider',
|
|
496
|
+
}),
|
|
497
|
+
executionContext: {
|
|
498
|
+
getCwd: () => tmpDir,
|
|
499
|
+
isRemote: () => false,
|
|
500
|
+
getSSHService: () => undefined,
|
|
501
|
+
},
|
|
502
|
+
});
|
|
503
|
+
const result = await manager.run({ role: 'worker', task: 'do mixed writes' });
|
|
504
|
+
t.deepEqual(new Set(result.filesChanged), new Set(['a.txt', 'b.txt']));
|
|
505
|
+
});
|
|
506
|
+
test.serial('worker write lock rejects concurrent create_file for same path without waiting', async (t) => {
|
|
507
|
+
const tmpDir = fs.mkdtempSync(path.join('/tmp', 'term2-test-worker-lock-'));
|
|
508
|
+
t.teardown(() => fs.rmSync(tmpDir, { recursive: true, force: true }));
|
|
509
|
+
registerProvider({
|
|
510
|
+
id: 'mock-worker-lock-provider',
|
|
511
|
+
label: 'Mock Worker Lock Provider',
|
|
512
|
+
createRunner: () => ({
|
|
513
|
+
run: async (agent) => {
|
|
514
|
+
const createFile = agent.tools.find((tool) => tool.name === 'create_file');
|
|
515
|
+
const [first, second] = await Promise.all([
|
|
516
|
+
createFile.invoke({}, JSON.stringify({
|
|
517
|
+
path: 'locked.txt',
|
|
518
|
+
content: 'one',
|
|
519
|
+
}), {}),
|
|
520
|
+
createFile.invoke({}, JSON.stringify({
|
|
521
|
+
path: 'locked.txt',
|
|
522
|
+
content: 'two',
|
|
523
|
+
}), {}),
|
|
524
|
+
]);
|
|
525
|
+
return {
|
|
526
|
+
status: 'completed',
|
|
527
|
+
finalOutput: JSON.stringify({ first, second }),
|
|
528
|
+
history: [],
|
|
529
|
+
messages: [],
|
|
530
|
+
};
|
|
531
|
+
},
|
|
532
|
+
}),
|
|
533
|
+
fetchModels: async () => [{ id: 'mock-model' }],
|
|
534
|
+
});
|
|
535
|
+
const manager = new SubagentManager({
|
|
536
|
+
logger: createMockLogger(),
|
|
537
|
+
settings: createMockSettings({
|
|
538
|
+
'agent.model': 'mock-model',
|
|
539
|
+
'agent.provider': 'mock-worker-lock-provider',
|
|
540
|
+
}),
|
|
541
|
+
executionContext: {
|
|
542
|
+
getCwd: () => tmpDir,
|
|
543
|
+
isRemote: () => false,
|
|
544
|
+
getSSHService: () => undefined,
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
const result = await manager.run({ role: 'worker', task: 'concurrent writes' });
|
|
548
|
+
const parsed = JSON.parse(result.finalText);
|
|
549
|
+
const first = JSON.parse(parsed.first);
|
|
550
|
+
const second = JSON.parse(parsed.second);
|
|
551
|
+
const allErrors = [first?.error, second?.error, first?.output?.[0]?.error, second?.output?.[0]?.error]
|
|
552
|
+
.filter(Boolean)
|
|
553
|
+
.join(' ');
|
|
554
|
+
t.true(allErrors.includes('already being modified'));
|
|
555
|
+
});
|
|
556
|
+
// ========== Session isolation ==========
|
|
557
|
+
test.serial('run() creates isolated sessions for each subagent call', async (t) => {
|
|
558
|
+
const settings = createMockSettings({
|
|
559
|
+
'agent.model': 'mock-model',
|
|
560
|
+
'agent.provider': 'mock-explorer-provider',
|
|
561
|
+
});
|
|
562
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
563
|
+
const [result1, result2] = await Promise.all([
|
|
564
|
+
manager.run({ role: 'explorer', task: 'task 1' }),
|
|
565
|
+
manager.run({ role: 'explorer', task: 'task 2' }),
|
|
566
|
+
]);
|
|
567
|
+
t.is(result1.status, 'completed');
|
|
568
|
+
t.is(result2.status, 'completed');
|
|
569
|
+
t.not(result1.agentId, result2.agentId);
|
|
570
|
+
});
|
|
571
|
+
// ========== Real-time events ==========
|
|
572
|
+
let eventToolProviderRegistered = false;
|
|
573
|
+
function ensureEventToolProviderRegistered() {
|
|
574
|
+
if (!eventToolProviderRegistered) {
|
|
575
|
+
registerProvider({
|
|
576
|
+
id: 'mock-event-tool-provider',
|
|
577
|
+
label: 'Mock Event Tool Provider',
|
|
578
|
+
createRunner: () => ({
|
|
579
|
+
run: async (agent) => {
|
|
580
|
+
const readFile = agent.tools.find((tool) => tool.name === 'read_file');
|
|
581
|
+
try {
|
|
582
|
+
await readFile.invoke({}, JSON.stringify({ path: '/nonexistent-subagent-event-test' }), {});
|
|
583
|
+
}
|
|
584
|
+
catch {
|
|
585
|
+
// Tool execution may fail (missing file); we only care that the
|
|
586
|
+
// tool-started event fired before execution.
|
|
587
|
+
}
|
|
588
|
+
return { status: 'completed', finalOutput: 'done', history: [], messages: [] };
|
|
589
|
+
},
|
|
590
|
+
}),
|
|
591
|
+
fetchModels: async () => [{ id: 'mock-model' }],
|
|
592
|
+
});
|
|
593
|
+
eventToolProviderRegistered = true;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
test.serial('run() emits started, tool_started and completed events', async (t) => {
|
|
597
|
+
ensureEventToolProviderRegistered();
|
|
598
|
+
const settings = createMockSettings({
|
|
599
|
+
'agent.model': 'mock-model',
|
|
600
|
+
'agent.provider': 'mock-event-tool-provider',
|
|
601
|
+
});
|
|
602
|
+
const events = [];
|
|
603
|
+
const manager = new SubagentManager({
|
|
604
|
+
logger: createMockLogger(),
|
|
605
|
+
settings,
|
|
606
|
+
onEvent: (event) => events.push(event),
|
|
607
|
+
});
|
|
608
|
+
const result = await manager.run({ role: 'explorer', task: 'search the code' });
|
|
609
|
+
const started = events.find((e) => e.type === 'subagent_started');
|
|
610
|
+
t.truthy(started);
|
|
611
|
+
t.is(started.role, 'explorer');
|
|
612
|
+
t.is(started.task, 'search the code');
|
|
613
|
+
t.is(started.agentId, result.agentId);
|
|
614
|
+
const toolStarted = events.find((e) => e.type === 'subagent_tool_started');
|
|
615
|
+
t.truthy(toolStarted);
|
|
616
|
+
t.is(toolStarted.toolName, 'read_file');
|
|
617
|
+
t.is(toolStarted.agentId, result.agentId);
|
|
618
|
+
t.is(toolStarted.commandMessages?.[0]?.command, 'read_file "/nonexistent-subagent-event-test"');
|
|
619
|
+
const completed = events.find((e) => e.type === 'subagent_completed');
|
|
620
|
+
t.truthy(completed);
|
|
621
|
+
t.is(completed.result.agentId, result.agentId);
|
|
622
|
+
});
|
|
623
|
+
test.serial('run() emits a completed event even when the role is unknown', async (t) => {
|
|
624
|
+
const settings = createMockSettings({ 'agent.model': 'mock-model' });
|
|
625
|
+
const events = [];
|
|
626
|
+
const manager = new SubagentManager({
|
|
627
|
+
logger: createMockLogger(),
|
|
628
|
+
settings,
|
|
629
|
+
onEvent: (event) => events.push(event),
|
|
630
|
+
});
|
|
631
|
+
await manager.run({ role: 'definitely-not-a-role', task: 'x' });
|
|
632
|
+
t.truthy(events.find((e) => e.type === 'subagent_started'));
|
|
633
|
+
const completed = events.find((e) => e.type === 'subagent_completed');
|
|
634
|
+
t.truthy(completed);
|
|
635
|
+
t.is(completed.result.status, 'failed');
|
|
636
|
+
});
|
|
637
|
+
// ========== Mentor role definition sourced from markdown ==========
|
|
638
|
+
test.serial('mentor base instructions come from the mentor role markdown', async (t) => {
|
|
639
|
+
const settings = createMockSettings({
|
|
640
|
+
'agent.model': 'main-model',
|
|
641
|
+
'agent.provider': 'mock-mentor-manager',
|
|
642
|
+
'agent.mentorModel': 'mentor-model',
|
|
643
|
+
'agent.mentorProvider': 'mock-mentor-manager',
|
|
644
|
+
'app.mentorMode': false,
|
|
645
|
+
});
|
|
646
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
647
|
+
await manager.run({ role: 'mentor', task: 'advise me' });
|
|
648
|
+
t.is(mentorManagerRunnerCalls.length, 1);
|
|
649
|
+
const instructions = mentorManagerRunnerCalls[0].agent.instructions;
|
|
650
|
+
// Body text defined in source/prompts/subagents/mentor.md
|
|
651
|
+
t.true(instructions.includes('helpful mentor assistant'));
|
|
652
|
+
t.true(instructions.includes('no direct workspace access'));
|
|
653
|
+
});
|
|
654
|
+
// ========== finalText excludes pre-tool narration ==========
|
|
655
|
+
let postToolProviderRegistered = false;
|
|
656
|
+
function ensurePostToolProviderRegistered() {
|
|
657
|
+
if (!postToolProviderRegistered) {
|
|
658
|
+
registerProvider({
|
|
659
|
+
id: 'mock-post-tool-provider',
|
|
660
|
+
label: 'Mock Post Tool Provider',
|
|
661
|
+
createRunner: () => ({
|
|
662
|
+
run: async () => ({
|
|
663
|
+
status: 'completed',
|
|
664
|
+
// finalOutput intentionally absent to exercise the history fallback.
|
|
665
|
+
history: [
|
|
666
|
+
{ rawItem: { role: 'assistant', content: 'Let me look into this first.' } },
|
|
667
|
+
{ rawItem: { type: 'function_call', name: 'grep' } },
|
|
668
|
+
{ rawItem: { type: 'function_call_result', name: 'grep' } },
|
|
669
|
+
{ rawItem: { role: 'assistant', content: 'Final answer: it is in foo.ts.' } },
|
|
670
|
+
],
|
|
671
|
+
messages: [],
|
|
672
|
+
}),
|
|
673
|
+
}),
|
|
674
|
+
fetchModels: async () => [{ id: 'mock-model' }],
|
|
675
|
+
});
|
|
676
|
+
postToolProviderRegistered = true;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
test.serial('finalText is the assistant message after the last tool item', async (t) => {
|
|
680
|
+
ensurePostToolProviderRegistered();
|
|
681
|
+
const settings = createMockSettings({
|
|
682
|
+
'agent.model': 'mock-model',
|
|
683
|
+
'agent.provider': 'mock-post-tool-provider',
|
|
684
|
+
});
|
|
685
|
+
const manager = new SubagentManager({ logger: createMockLogger(), settings });
|
|
686
|
+
const result = await manager.run({ role: 'explorer', task: 'where is it' });
|
|
687
|
+
t.is(result.finalText, 'Final answer: it is in foo.ts.');
|
|
688
|
+
t.false(result.finalText.includes('Let me look into this first.'));
|
|
689
|
+
});
|
|
690
|
+
//# sourceMappingURL=subagent-manager.test.js.map
|