@adecore/agents-react 0.0.1 → 0.17.0-beta.1
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 +110 -0
- package/README.md +53 -0
- package/dist/agents/AccountDot.d.ts +4 -0
- package/dist/agents/AccountDot.js +8 -0
- package/dist/agents/AgentIcon.d.ts +6 -0
- package/dist/agents/AgentIcon.js +20 -0
- package/dist/agents/ModelOptionControl.d.ts +15 -0
- package/dist/agents/ModelOptionControl.js +33 -0
- package/dist/agents/ProviderLogo.d.ts +8 -0
- package/dist/agents/ProviderLogo.js +8 -0
- package/dist/agents/account-limits.d.ts +7 -0
- package/dist/agents/account-limits.js +40 -0
- package/dist/agents/accounts.d.ts +22 -0
- package/dist/agents/accounts.js +113 -0
- package/dist/agents/model-name.d.ts +12 -0
- package/dist/agents/model-name.js +56 -0
- package/dist/agents/model-options.d.ts +5 -0
- package/dist/agents/model-options.js +15 -0
- package/dist/agents/provider-paths.d.ts +2 -0
- package/dist/agents/provider-paths.js +8 -0
- package/dist/agents/status-look.d.ts +13 -0
- package/dist/agents/status-look.js +24 -0
- package/dist/chat/account-choice.d.ts +11 -0
- package/dist/chat/account-choice.js +30 -0
- package/dist/chat/actions.d.ts +6 -0
- package/dist/chat/actions.js +68 -0
- package/dist/chat/activity.d.ts +5 -0
- package/dist/chat/activity.js +6 -0
- package/dist/chat/attachments.d.ts +23 -0
- package/dist/chat/attachments.js +86 -0
- package/dist/chat/bookmarks.d.ts +14 -0
- package/dist/chat/bookmarks.js +73 -0
- package/dist/chat/chat-client.d.ts +55 -0
- package/dist/chat/chat-client.js +279 -0
- package/dist/chat/chat-references.d.ts +5 -0
- package/dist/chat/chat-references.js +5 -0
- package/dist/chat/drafts.d.ts +29 -0
- package/dist/chat/drafts.js +116 -0
- package/dist/chat/forks.d.ts +2 -0
- package/dist/chat/forks.js +28 -0
- package/dist/chat/guards.d.ts +12 -0
- package/dist/chat/guards.js +69 -0
- package/dist/chat/logic/ansi.d.ts +13 -0
- package/dist/chat/logic/ansi.js +185 -0
- package/dist/chat/logic/answers.d.ts +1 -0
- package/dist/chat/logic/answers.js +8 -0
- package/dist/chat/logic/bookmarks.d.ts +5 -0
- package/dist/chat/logic/bookmarks.js +33 -0
- package/dist/chat/logic/context-usage.d.ts +12 -0
- package/dist/chat/logic/context-usage.js +21 -0
- package/dist/chat/logic/fork.d.ts +45 -0
- package/dist/chat/logic/fork.js +148 -0
- package/dist/chat/logic/handback.d.ts +2 -0
- package/dist/chat/logic/handback.js +8 -0
- package/dist/chat/logic/json.d.ts +1 -0
- package/dist/chat/logic/json.js +4 -0
- package/dist/chat/logic/limit.d.ts +7 -0
- package/dist/chat/logic/limit.js +34 -0
- package/dist/chat/logic/resume-compaction.d.ts +14 -0
- package/dist/chat/logic/resume-compaction.js +47 -0
- package/dist/chat/logic/scrubber.d.ts +50 -0
- package/dist/chat/logic/scrubber.js +220 -0
- package/dist/chat/logic/thread-cards.d.ts +3 -0
- package/dist/chat/logic/thread-cards.js +59 -0
- package/dist/chat/logic/timeline-copy.d.ts +4 -0
- package/dist/chat/logic/timeline-copy.js +59 -0
- package/dist/chat/logic/timeline-target.d.ts +12 -0
- package/dist/chat/logic/timeline-target.js +34 -0
- package/dist/chat/logic/timeline.d.ts +115 -0
- package/dist/chat/logic/timeline.js +343 -0
- package/dist/chat/logic/tool-catalog.d.ts +10 -0
- package/dist/chat/logic/tool-catalog.js +44 -0
- package/dist/chat/logic/tools.d.ts +15 -0
- package/dist/chat/logic/tools.js +82 -0
- package/dist/chat/logic/welcome.d.ts +2 -0
- package/dist/chat/logic/welcome.js +10 -0
- package/dist/chat/logic/workflow.d.ts +14 -0
- package/dist/chat/logic/workflow.js +47 -0
- package/dist/chat/mentions.d.ts +65 -0
- package/dist/chat/mentions.js +184 -0
- package/dist/chat/persisted-json.d.ts +5 -0
- package/dist/chat/persisted-json.js +58 -0
- package/dist/chat/preferences.d.ts +27 -0
- package/dist/chat/preferences.js +139 -0
- package/dist/chat/quote.d.ts +2 -0
- package/dist/chat/quote.js +25 -0
- package/dist/chat/recent-messages.d.ts +17 -0
- package/dist/chat/recent-messages.js +36 -0
- package/dist/chat/resume-compaction-dismissals.d.ts +3 -0
- package/dist/chat/resume-compaction-dismissals.js +14 -0
- package/dist/chat/runtime-modes.d.ts +4 -0
- package/dist/chat/runtime-modes.js +15 -0
- package/dist/chat/stash.d.ts +24 -0
- package/dist/chat/stash.js +91 -0
- package/dist/chat/subagent-conversation.d.ts +41 -0
- package/dist/chat/subagent-conversation.js +167 -0
- package/dist/chat/subagent-list.d.ts +19 -0
- package/dist/chat/subagent-list.js +106 -0
- package/dist/chat/subagent-support.d.ts +6 -0
- package/dist/chat/subagent-support.js +14 -0
- package/dist/chat/subagent-view.d.ts +35 -0
- package/dist/chat/subagent-view.js +96 -0
- package/dist/chat/thumbnails.d.ts +10 -0
- package/dist/chat/thumbnails.js +89 -0
- package/dist/chat/timeline-flash.d.ts +12 -0
- package/dist/chat/timeline-flash.js +16 -0
- package/dist/chat/timeline-scroll.d.ts +27 -0
- package/dist/chat/timeline-scroll.js +152 -0
- package/dist/chat/ui/AccountPill.d.ts +3 -0
- package/dist/chat/ui/AccountPill.js +23 -0
- package/dist/chat/ui/AnsiOutput.d.ts +5 -0
- package/dist/chat/ui/AnsiOutput.js +9 -0
- package/dist/chat/ui/BookmarkMarker.d.ts +8 -0
- package/dist/chat/ui/BookmarkMarker.js +57 -0
- package/dist/chat/ui/BookmarkSubmenu.d.ts +3 -0
- package/dist/chat/ui/BookmarkSubmenu.js +33 -0
- package/dist/chat/ui/ChatActivity.d.ts +7 -0
- package/dist/chat/ui/ChatActivity.js +84 -0
- package/dist/chat/ui/ChatReferenceChip.d.ts +4 -0
- package/dist/chat/ui/ChatReferenceChip.js +16 -0
- package/dist/chat/ui/CodeBlock.d.ts +4 -0
- package/dist/chat/ui/CodeBlock.js +132 -0
- package/dist/chat/ui/Composer.d.ts +15 -0
- package/dist/chat/ui/Composer.js +856 -0
- package/dist/chat/ui/ComposerInput.d.ts +29 -0
- package/dist/chat/ui/ComposerInput.js +127 -0
- package/dist/chat/ui/DiffPool.d.ts +5 -0
- package/dist/chat/ui/DiffPool.js +26 -0
- package/dist/chat/ui/EditDiff.d.ts +4 -0
- package/dist/chat/ui/EditDiff.js +21 -0
- package/dist/chat/ui/FadingWords.d.ts +3 -0
- package/dist/chat/ui/FadingWords.js +5 -0
- package/dist/chat/ui/ImageView.d.ts +6 -0
- package/dist/chat/ui/ImageView.js +81 -0
- package/dist/chat/ui/LimitState.d.ts +8 -0
- package/dist/chat/ui/LimitState.js +112 -0
- package/dist/chat/ui/Markdown.d.ts +18 -0
- package/dist/chat/ui/Markdown.js +159 -0
- package/dist/chat/ui/MessageActions.d.ts +9 -0
- package/dist/chat/ui/MessageActions.js +53 -0
- package/dist/chat/ui/Pickers.d.ts +21 -0
- package/dist/chat/ui/Pickers.js +111 -0
- package/dist/chat/ui/PromptComposer.d.ts +13 -0
- package/dist/chat/ui/PromptComposer.js +56 -0
- package/dist/chat/ui/QuoteButton.d.ts +7 -0
- package/dist/chat/ui/QuoteButton.js +74 -0
- package/dist/chat/ui/ResumeCompactionDock.d.ts +5 -0
- package/dist/chat/ui/ResumeCompactionDock.js +14 -0
- package/dist/chat/ui/RunSettings.d.ts +28 -0
- package/dist/chat/ui/RunSettings.js +153 -0
- package/dist/chat/ui/Scrubber.d.ts +17 -0
- package/dist/chat/ui/Scrubber.js +118 -0
- package/dist/chat/ui/SubagentControls.d.ts +11 -0
- package/dist/chat/ui/SubagentControls.js +36 -0
- package/dist/chat/ui/SubagentInfo.d.ts +4 -0
- package/dist/chat/ui/SubagentInfo.js +34 -0
- package/dist/chat/ui/SubagentStopButton.d.ts +7 -0
- package/dist/chat/ui/SubagentStopButton.js +34 -0
- package/dist/chat/ui/SubagentTimeline.d.ts +4 -0
- package/dist/chat/ui/SubagentTimeline.js +107 -0
- package/dist/chat/ui/Timeline.d.ts +6 -0
- package/dist/chat/ui/Timeline.js +424 -0
- package/dist/chat/ui/TimelineMenu.d.ts +12 -0
- package/dist/chat/ui/TimelineMenu.js +54 -0
- package/dist/chat/ui/UnifiedDiff.d.ts +11 -0
- package/dist/chat/ui/UnifiedDiff.js +66 -0
- package/dist/chat/ui/UploadThumb.d.ts +4 -0
- package/dist/chat/ui/UploadThumb.js +11 -0
- package/dist/chat/ui/Welcome.d.ts +4 -0
- package/dist/chat/ui/Welcome.js +16 -0
- package/dist/chat/ui/chips.d.ts +4 -0
- package/dist/chat/ui/chips.js +11 -0
- package/dist/chat/ui/code-lines.d.ts +18 -0
- package/dist/chat/ui/code-lines.js +46 -0
- package/dist/chat/ui/code-streaming.d.ts +1 -0
- package/dist/chat/ui/code-streaming.js +3 -0
- package/dist/chat/ui/code-theme.d.ts +3 -0
- package/dist/chat/ui/code-theme.js +12 -0
- package/dist/chat/ui/composer/chips.d.ts +6 -0
- package/dist/chat/ui/composer/chips.js +31 -0
- package/dist/chat/ui/composer/editor.d.ts +11 -0
- package/dist/chat/ui/composer/editor.js +98 -0
- package/dist/chat/ui/composer/insert.d.ts +2 -0
- package/dist/chat/ui/composer/insert.js +19 -0
- package/dist/chat/ui/composer/keys.d.ts +30 -0
- package/dist/chat/ui/composer/keys.js +100 -0
- package/dist/chat/ui/diff-theme.d.ts +2 -0
- package/dist/chat/ui/diff-theme.js +20 -0
- package/dist/chat/ui/file-links.d.ts +5 -0
- package/dist/chat/ui/file-links.js +20 -0
- package/dist/chat/ui/find-reveal.d.ts +7 -0
- package/dist/chat/ui/find-reveal.js +13 -0
- package/dist/chat/ui/full-diff.d.ts +6 -0
- package/dist/chat/ui/full-diff.js +19 -0
- package/dist/chat/ui/icons.d.ts +4 -0
- package/dist/chat/ui/icons.js +20 -0
- package/dist/chat/ui/markdown-blocks.d.ts +6 -0
- package/dist/chat/ui/markdown-blocks.js +103 -0
- package/dist/chat/ui/quote-selection.d.ts +3 -0
- package/dist/chat/ui/quote-selection.js +46 -0
- package/dist/chat/ui/rehype-chips.d.ts +23 -0
- package/dist/chat/ui/rehype-chips.js +32 -0
- package/dist/chat/ui/rehype-fade.d.ts +23 -0
- package/dist/chat/ui/rehype-fade.js +36 -0
- package/dist/chat/ui/remark-html-as-text.d.ts +8 -0
- package/dist/chat/ui/remark-html-as-text.js +26 -0
- package/dist/chat/ui/reply-context.d.ts +5 -0
- package/dist/chat/ui/reply-context.js +2 -0
- package/dist/chat/ui/reveal.d.ts +13 -0
- package/dist/chat/ui/reveal.js +114 -0
- package/dist/chat/ui/rows/ForksRow.d.ts +4 -0
- package/dist/chat/ui/rows/ForksRow.js +38 -0
- package/dist/chat/ui/rows/MessageRows.d.ts +40 -0
- package/dist/chat/ui/rows/MessageRows.js +204 -0
- package/dist/chat/ui/rows/Rows.d.ts +12 -0
- package/dist/chat/ui/rows/Rows.js +61 -0
- package/dist/chat/ui/rows/SubagentRow.d.ts +17 -0
- package/dist/chat/ui/rows/SubagentRow.js +101 -0
- package/dist/chat/ui/rows/TaskRow.d.ts +8 -0
- package/dist/chat/ui/rows/TaskRow.js +63 -0
- package/dist/chat/ui/rows/WorkRows.d.ts +47 -0
- package/dist/chat/ui/rows/WorkRows.js +168 -0
- package/dist/chat/ui/rows/WorkflowRow.d.ts +8 -0
- package/dist/chat/ui/rows/WorkflowRow.js +48 -0
- package/dist/chat/ui/rows/row-rhythm.d.ts +6 -0
- package/dist/chat/ui/rows/row-rhythm.js +34 -0
- package/dist/chat/ui/useToggleSet.d.ts +6 -0
- package/dist/chat/ui/useToggleSet.js +25 -0
- package/dist/host.d.ts +195 -0
- package/dist/host.js +55 -0
- package/dist/lazy.d.ts +6 -0
- package/dist/lazy.js +57 -0
- package/dist/locales/en/agent-chat.json +446 -0
- package/dist/locales/en/agent-prompts.json +42 -0
- package/dist/locales/en/agent-providers.json +131 -0
- package/dist/locales/en/agent-usage.json +131 -0
- package/dist/locales/nl/agent-chat.json +446 -0
- package/dist/locales/nl/agent-prompts.json +42 -0
- package/dist/locales/nl/agent-providers.json +131 -0
- package/dist/locales/nl/agent-usage.json +131 -0
- package/dist/locales.d.ts +5 -0
- package/dist/locales.js +20 -0
- package/dist/mounted-registry.d.ts +7 -0
- package/dist/mounted-registry.js +39 -0
- package/dist/port-transport.d.ts +6 -0
- package/dist/port-transport.js +129 -0
- package/dist/prompts/logic/focus.d.ts +2 -0
- package/dist/prompts/logic/focus.js +23 -0
- package/dist/prompts/logic/keys.d.ts +42 -0
- package/dist/prompts/logic/keys.js +94 -0
- package/dist/prompts/logic/prompts.d.ts +34 -0
- package/dist/prompts/logic/prompts.fixtures.d.ts +5 -0
- package/dist/prompts/logic/prompts.fixtures.js +79 -0
- package/dist/prompts/logic/prompts.js +32 -0
- package/dist/prompts/logic/subjects.d.ts +50 -0
- package/dist/prompts/logic/subjects.js +63 -0
- package/dist/prompts/logic/useFocusAfterAnswer.d.ts +6 -0
- package/dist/prompts/logic/useFocusAfterAnswer.js +28 -0
- package/dist/prompts/logic/usePromptSession.d.ts +24 -0
- package/dist/prompts/logic/usePromptSession.js +87 -0
- package/dist/prompts/ui/ApprovalBody.d.ts +13 -0
- package/dist/prompts/ui/ApprovalBody.js +34 -0
- package/dist/prompts/ui/PromptActions.d.ts +27 -0
- package/dist/prompts/ui/PromptActions.js +18 -0
- package/dist/prompts/ui/PromptCard.d.ts +16 -0
- package/dist/prompts/ui/PromptCard.js +50 -0
- package/dist/prompts/ui/PromptView.d.ts +2 -0
- package/dist/prompts/ui/PromptView.js +47 -0
- package/dist/prompts/ui/QuestionBody.d.ts +9 -0
- package/dist/prompts/ui/QuestionBody.js +87 -0
- package/dist/providers/AccountColors.d.ts +5 -0
- package/dist/providers/AccountColors.js +8 -0
- package/dist/providers/AccountDetail.d.ts +12 -0
- package/dist/providers/AccountDetail.js +69 -0
- package/dist/providers/AccountVariables.d.ts +8 -0
- package/dist/providers/AccountVariables.js +43 -0
- package/dist/providers/AddAccountForm.d.ts +10 -0
- package/dist/providers/AddAccountForm.js +54 -0
- package/dist/providers/CliDetail.d.ts +12 -0
- package/dist/providers/CliDetail.js +54 -0
- package/dist/providers/ProvidersPane.d.ts +9 -0
- package/dist/providers/ProvidersPane.js +86 -0
- package/dist/providers/account-actions.d.ts +7 -0
- package/dist/providers/account-actions.js +57 -0
- package/dist/providers/parts.d.ts +9 -0
- package/dist/providers/parts.js +18 -0
- package/dist/providers/provider-abilities.d.ts +2 -0
- package/dist/providers/provider-abilities.js +7 -0
- package/dist/providers/variables.d.ts +13 -0
- package/dist/providers/variables.js +63 -0
- package/dist/scope.d.ts +11 -0
- package/dist/scope.js +10 -0
- package/dist/settings/sections.d.ts +13 -0
- package/dist/settings/sections.js +32 -0
- package/dist/state/chats.d.ts +44 -0
- package/dist/state/chats.js +199 -0
- package/dist/state/provider-accounts.d.ts +17 -0
- package/dist/state/provider-accounts.js +67 -0
- package/dist/state/providers.d.ts +17 -0
- package/dist/state/providers.js +33 -0
- package/dist/state/usage.d.ts +44 -0
- package/dist/state/usage.js +121 -0
- package/dist/storage.d.ts +20 -0
- package/dist/storage.js +89 -0
- package/dist/theme.css +196 -0
- package/dist/transport.d.ts +24 -0
- package/dist/transport.js +18 -0
- package/dist/ui/PlainTextarea.d.ts +2 -0
- package/dist/ui/PlainTextarea.js +5 -0
- package/dist/usage/LimitsList.d.ts +18 -0
- package/dist/usage/LimitsList.js +73 -0
- package/dist/usage/UsageBreakdown.d.ts +9 -0
- package/dist/usage/UsageBreakdown.js +67 -0
- package/dist/usage/UsageChart.d.ts +10 -0
- package/dist/usage/UsageChart.js +47 -0
- package/dist/usage/UsageDialog.d.ts +6 -0
- package/dist/usage/UsageDialog.js +12 -0
- package/dist/usage/UsageLimits.d.ts +1 -0
- package/dist/usage/UsageLimits.js +57 -0
- package/dist/usage/UsageLimitsCard.d.ts +4 -0
- package/dist/usage/UsageLimitsCard.js +26 -0
- package/dist/usage/UsagePage.d.ts +7 -0
- package/dist/usage/UsagePage.js +154 -0
- package/dist/usage/UsagePane.d.ts +3 -0
- package/dist/usage/UsagePane.js +21 -0
- package/dist/usage/UsageSummary.d.ts +12 -0
- package/dist/usage/UsageSummary.js +15 -0
- package/dist/usage/UsageTiles.d.ts +7 -0
- package/dist/usage/UsageTiles.js +19 -0
- package/dist/usage/format.d.ts +13 -0
- package/dist/usage/format.js +55 -0
- package/dist/usage/limit-groups.d.ts +22 -0
- package/dist/usage/limit-groups.js +86 -0
- package/dist/usage/limits.d.ts +4 -0
- package/dist/usage/limits.js +58 -0
- package/dist/usage/money.d.ts +1 -0
- package/dist/usage/money.js +13 -0
- package/dist/usage/summary.d.ts +34 -0
- package/dist/usage/summary.js +94 -0
- package/package.json +895 -3
- package/src/agents/AccountDot.tsx +14 -0
- package/src/agents/AgentIcon.tsx +26 -0
- package/src/agents/ModelOptionControl.tsx +68 -0
- package/src/agents/ProviderLogo.tsx +20 -0
- package/src/agents/account-limits.ts +61 -0
- package/src/agents/accounts.ts +137 -0
- package/src/agents/model-name.ts +70 -0
- package/src/agents/model-options.ts +21 -0
- package/src/agents/provider-paths.ts +10 -0
- package/src/agents/status-look.ts +43 -0
- package/src/chat/account-choice.ts +44 -0
- package/src/chat/actions.ts +72 -0
- package/src/chat/activity.ts +8 -0
- package/src/chat/attachments.ts +118 -0
- package/src/chat/bookmarks.ts +87 -0
- package/src/chat/chat-client.ts +345 -0
- package/src/chat/chat-references.ts +11 -0
- package/src/chat/drafts.ts +165 -0
- package/src/chat/forks.ts +33 -0
- package/src/chat/guards.ts +84 -0
- package/src/chat/logic/ansi.ts +206 -0
- package/src/chat/logic/answers.ts +8 -0
- package/src/chat/logic/bookmarks.ts +42 -0
- package/src/chat/logic/context-usage.ts +36 -0
- package/src/chat/logic/fork.ts +200 -0
- package/src/chat/logic/handback.ts +10 -0
- package/src/chat/logic/json.ts +4 -0
- package/src/chat/logic/limit.ts +46 -0
- package/src/chat/logic/resume-compaction.ts +71 -0
- package/src/chat/logic/scrubber.ts +289 -0
- package/src/chat/logic/thread-cards.ts +64 -0
- package/src/chat/logic/timeline-copy.ts +65 -0
- package/src/chat/logic/timeline-target.ts +52 -0
- package/src/chat/logic/timeline.ts +419 -0
- package/src/chat/logic/tool-catalog.ts +62 -0
- package/src/chat/logic/tools.ts +102 -0
- package/src/chat/logic/welcome.ts +13 -0
- package/src/chat/logic/workflow.ts +66 -0
- package/src/chat/mentions.ts +232 -0
- package/src/chat/persisted-json.ts +67 -0
- package/src/chat/preferences.ts +179 -0
- package/src/chat/quote.ts +26 -0
- package/src/chat/recent-messages.ts +59 -0
- package/src/chat/resume-compaction-dismissals.ts +17 -0
- package/src/chat/runtime-modes.ts +18 -0
- package/src/chat/stash.ts +122 -0
- package/src/chat/subagent-conversation.ts +203 -0
- package/src/chat/subagent-list.ts +141 -0
- package/src/chat/subagent-support.ts +21 -0
- package/src/chat/subagent-view.ts +137 -0
- package/src/chat/thumbnails.ts +108 -0
- package/src/chat/timeline-flash.ts +33 -0
- package/src/chat/timeline-scroll.ts +199 -0
- package/src/chat/ui/AccountPill.tsx +31 -0
- package/src/chat/ui/AnsiOutput.tsx +18 -0
- package/src/chat/ui/BookmarkMarker.tsx +108 -0
- package/src/chat/ui/BookmarkSubmenu.tsx +53 -0
- package/src/chat/ui/ChatActivity.tsx +198 -0
- package/src/chat/ui/ChatReferenceChip.tsx +28 -0
- package/src/chat/ui/CodeBlock.tsx +175 -0
- package/src/chat/ui/Composer.tsx +1326 -0
- package/src/chat/ui/ComposerInput.tsx +187 -0
- package/src/chat/ui/DiffPool.tsx +36 -0
- package/src/chat/ui/EditDiff.tsx +30 -0
- package/src/chat/ui/FadingWords.tsx +18 -0
- package/src/chat/ui/ImageView.tsx +159 -0
- package/src/chat/ui/LimitState.tsx +165 -0
- package/src/chat/ui/Markdown.tsx +258 -0
- package/src/chat/ui/MessageActions.tsx +88 -0
- package/src/chat/ui/Pickers.tsx +272 -0
- package/src/chat/ui/PromptComposer.tsx +119 -0
- package/src/chat/ui/QuoteButton.tsx +103 -0
- package/src/chat/ui/ResumeCompactionDock.tsx +28 -0
- package/src/chat/ui/RunSettings.tsx +546 -0
- package/src/chat/ui/Scrubber.tsx +265 -0
- package/src/chat/ui/SubagentControls.tsx +66 -0
- package/src/chat/ui/SubagentInfo.tsx +67 -0
- package/src/chat/ui/SubagentStopButton.tsx +45 -0
- package/src/chat/ui/SubagentTimeline.tsx +170 -0
- package/src/chat/ui/Timeline.tsx +628 -0
- package/src/chat/ui/TimelineMenu.tsx +124 -0
- package/src/chat/ui/UnifiedDiff.tsx +99 -0
- package/src/chat/ui/UploadThumb.tsx +13 -0
- package/src/chat/ui/Welcome.tsx +22 -0
- package/src/chat/ui/chips.ts +15 -0
- package/src/chat/ui/code-lines.ts +60 -0
- package/src/chat/ui/code-streaming.ts +4 -0
- package/src/chat/ui/code-theme.ts +15 -0
- package/src/chat/ui/composer/chips.ts +43 -0
- package/src/chat/ui/composer/editor.ts +118 -0
- package/src/chat/ui/composer/insert.ts +22 -0
- package/src/chat/ui/composer/keys.ts +144 -0
- package/src/chat/ui/diff-theme.ts +23 -0
- package/src/chat/ui/file-links.ts +24 -0
- package/src/chat/ui/find-reveal.ts +23 -0
- package/src/chat/ui/full-diff.ts +25 -0
- package/src/chat/ui/icons.tsx +24 -0
- package/src/chat/ui/markdown-blocks.ts +123 -0
- package/src/chat/ui/quote-selection.ts +53 -0
- package/src/chat/ui/rehype-chips.ts +62 -0
- package/src/chat/ui/rehype-fade.ts +64 -0
- package/src/chat/ui/remark-html-as-text.ts +36 -0
- package/src/chat/ui/reply-context.ts +4 -0
- package/src/chat/ui/reveal.ts +135 -0
- package/src/chat/ui/rows/ForksRow.tsx +79 -0
- package/src/chat/ui/rows/MessageRows.tsx +415 -0
- package/src/chat/ui/rows/Rows.tsx +81 -0
- package/src/chat/ui/rows/SubagentRow.tsx +209 -0
- package/src/chat/ui/rows/TaskRow.tsx +91 -0
- package/src/chat/ui/rows/WorkRows.tsx +445 -0
- package/src/chat/ui/rows/WorkflowRow.tsx +120 -0
- package/src/chat/ui/rows/row-rhythm.ts +37 -0
- package/src/chat/ui/useToggleSet.ts +39 -0
- package/src/host.ts +324 -0
- package/src/lazy.tsx +64 -0
- package/src/locales/en/agent-chat.json +446 -0
- package/src/locales/en/agent-prompts.json +42 -0
- package/src/locales/en/agent-providers.json +131 -0
- package/src/locales/en/agent-usage.json +131 -0
- package/src/locales/nl/agent-chat.json +446 -0
- package/src/locales/nl/agent-prompts.json +42 -0
- package/src/locales/nl/agent-providers.json +131 -0
- package/src/locales/nl/agent-usage.json +131 -0
- package/src/locales.ts +33 -0
- package/src/mounted-registry.ts +45 -0
- package/src/port-transport.ts +146 -0
- package/src/prompts/logic/focus.ts +25 -0
- package/src/prompts/logic/keys.ts +116 -0
- package/src/prompts/logic/prompts.fixtures.ts +84 -0
- package/src/prompts/logic/prompts.ts +58 -0
- package/src/prompts/logic/subjects.ts +122 -0
- package/src/prompts/logic/useFocusAfterAnswer.ts +38 -0
- package/src/prompts/logic/usePromptSession.ts +114 -0
- package/src/prompts/ui/ApprovalBody.tsx +109 -0
- package/src/prompts/ui/PromptActions.tsx +109 -0
- package/src/prompts/ui/PromptCard.tsx +101 -0
- package/src/prompts/ui/PromptView.tsx +111 -0
- package/src/prompts/ui/QuestionBody.tsx +174 -0
- package/src/providers/AccountColors.tsx +8 -0
- package/src/providers/AccountDetail.tsx +209 -0
- package/src/providers/AccountVariables.tsx +126 -0
- package/src/providers/AddAccountForm.tsx +121 -0
- package/src/providers/CliDetail.tsx +157 -0
- package/src/providers/ProvidersPane.tsx +236 -0
- package/src/providers/account-actions.ts +67 -0
- package/src/providers/parts.tsx +25 -0
- package/src/providers/provider-abilities.ts +9 -0
- package/src/providers/variables.ts +87 -0
- package/src/scope.ts +30 -0
- package/src/settings/sections.ts +47 -0
- package/src/state/chats.ts +279 -0
- package/src/state/provider-accounts.ts +87 -0
- package/src/state/providers.ts +50 -0
- package/src/state/usage.ts +196 -0
- package/src/storage.ts +113 -0
- package/src/theme.css +196 -0
- package/src/transport.ts +50 -0
- package/src/ui/PlainTextarea.tsx +6 -0
- package/src/usage/LimitsList.tsx +232 -0
- package/src/usage/UsageBreakdown.tsx +210 -0
- package/src/usage/UsageChart.tsx +113 -0
- package/src/usage/UsageDialog.tsx +20 -0
- package/src/usage/UsageLimits.tsx +145 -0
- package/src/usage/UsageLimitsCard.tsx +36 -0
- package/src/usage/UsagePage.tsx +276 -0
- package/src/usage/UsagePane.tsx +52 -0
- package/src/usage/UsageSummary.tsx +50 -0
- package/src/usage/UsageTiles.tsx +34 -0
- package/src/usage/format.ts +71 -0
- package/src/usage/limit-groups.ts +119 -0
- package/src/usage/limits.ts +71 -0
- package/src/usage/money.ts +14 -0
- package/src/usage/summary.ts +137 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The text a selection in an answer quotes: its lines without trailing spaces or the blank lines
|
|
3
|
+
* around them, and at most one blank line in a row. A selection of only whitespace quotes nothing.
|
|
4
|
+
*/
|
|
5
|
+
export function quoteOf(selected: string): string | null {
|
|
6
|
+
const text = selected
|
|
7
|
+
.replace(/\r\n?/g, '\n')
|
|
8
|
+
.split('\n')
|
|
9
|
+
.map((line) => line.replace(/\s+$/, ''))
|
|
10
|
+
.join('\n')
|
|
11
|
+
.replace(/^\n+|\n+$/g, '')
|
|
12
|
+
.replace(/\n{3,}/g, '\n\n');
|
|
13
|
+
return text.trim() === '' ? null : text;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* The message as it goes out: the quote as a markdown blockquote, a blank line, then what was typed. */
|
|
17
|
+
export function withQuote(quote: string, message: string): string {
|
|
18
|
+
if (quote === '') {
|
|
19
|
+
return message;
|
|
20
|
+
}
|
|
21
|
+
const block = quote
|
|
22
|
+
.split('\n')
|
|
23
|
+
.map((line) => (line === '' ? '>' : `> ${line}`))
|
|
24
|
+
.join('\n');
|
|
25
|
+
return message === '' ? block : `${block}\n\n${message}`;
|
|
26
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ChatItem } from '@adecore/agent-contracts';
|
|
2
|
+
|
|
3
|
+
export interface RecentChatMessage {
|
|
4
|
+
role: 'user' | 'assistant';
|
|
5
|
+
text: string;
|
|
6
|
+
createdAt: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface RecentChatMessages {
|
|
10
|
+
messages: RecentChatMessage[];
|
|
11
|
+
truncated: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const MAX_TOTAL_CHARACTERS = 12_000;
|
|
15
|
+
const MAX_MESSAGE_CHARACTERS = 3_000;
|
|
16
|
+
|
|
17
|
+
function clipped(text: string): { text: string; truncated: boolean } {
|
|
18
|
+
return text.length <= MAX_MESSAGE_CHARACTERS ? { text, truncated: false } : { text: `${text.slice(0, MAX_MESSAGE_CHARACTERS - 1)}…`, truncated: true };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type MessageItem = Extract<ChatItem, { kind: 'user' | 'assistant' }>;
|
|
22
|
+
|
|
23
|
+
function isMessage(item: ChatItem | undefined): item is MessageItem {
|
|
24
|
+
return item !== undefined && (item.kind === 'user' || item.kind === 'assistant') && item.text.trim() !== '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* The last messages of a conversation already cut down to the ones worth reading, clipped to what a reader can take in. */
|
|
28
|
+
export function recentMessages(eligible: readonly MessageItem[], limit: number): RecentChatMessages {
|
|
29
|
+
const candidates = eligible.slice(-limit);
|
|
30
|
+
const messages: RecentChatMessage[] = [];
|
|
31
|
+
let characters = 0;
|
|
32
|
+
let truncated = candidates.length < eligible.length;
|
|
33
|
+
for (const item of candidates.toReversed()) {
|
|
34
|
+
const message = clipped(item.text.trim());
|
|
35
|
+
const remaining = MAX_TOTAL_CHARACTERS - characters;
|
|
36
|
+
if (remaining <= 0) {
|
|
37
|
+
truncated = true;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
const text = message.text.length <= remaining ? message.text : `${message.text.slice(0, Math.max(0, remaining - 1))}…`;
|
|
41
|
+
messages.unshift({ role: item.kind, text, createdAt: item.createdAt });
|
|
42
|
+
characters += text.length;
|
|
43
|
+
truncated ||= message.truncated || text.length < message.text.length;
|
|
44
|
+
}
|
|
45
|
+
return { messages, truncated };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* A sub-agent's replies sit in the thread too, and belong to its own conversation rather than the chat's. */
|
|
49
|
+
export function recentChatMessages(items: Record<string, ChatItem>, order: readonly string[], limit: number): RecentChatMessages {
|
|
50
|
+
return recentMessages(
|
|
51
|
+
order.map((id) => items[id]).filter((item): item is MessageItem => isMessage(item) && !(item.kind === 'assistant' && item.parentToolUseId)),
|
|
52
|
+
limit
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Everything in a sub-agent's conversation is its own, so nothing is left out for being nested. */
|
|
57
|
+
export function recentSubagentMessages(items: readonly ChatItem[], limit: number): RecentChatMessages {
|
|
58
|
+
return recentMessages(items.filter(isMessage), limit);
|
|
59
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { useChatScope, type ChatScope } from '../scope';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Which turn a person answered "Keep full history" on, per chat. In memory only: the offer appears
|
|
6
|
+
* at most once per silence, so a reload asking again costs nothing, and a refusal is not a setting.
|
|
7
|
+
*/
|
|
8
|
+
const useDismissals = create<{ byKey: Record<string, string> }>(() => ({ byKey: {} }));
|
|
9
|
+
|
|
10
|
+
export function useResumeCompactionDismissal(chatId: string): string | null {
|
|
11
|
+
const { keyOf } = useChatScope();
|
|
12
|
+
return useDismissals((s) => s.byKey[keyOf(chatId)] ?? null);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function dismissResumeCompaction(scope: ChatScope, chatId: string, turnId: string): void {
|
|
16
|
+
useDismissals.setState((s) => ({ byKey: { ...s.byKey, [scope.keyOf(chatId)]: turnId } }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import i18next from 'i18next';
|
|
2
|
+
import { RuntimeModeSchema, type RuntimeMode } from '@adecore/agent-contracts';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* The runtime modes in the order they are offered, shared by the composer's mode picker, the
|
|
6
|
+
* settings dialog and the agent menus. That order is the schema's, which the host reads as the
|
|
7
|
+
* rank from narrowest to widest. Only the ids live here: the words are in `modes.<id>` of the `chat`
|
|
8
|
+
* namespace, read when a surface draws one rather than when this module loads.
|
|
9
|
+
*/
|
|
10
|
+
export const RUNTIME_MODES: readonly RuntimeMode[] = RuntimeModeSchema.options;
|
|
11
|
+
|
|
12
|
+
export function runtimeModeLabel(mode: RuntimeMode): string {
|
|
13
|
+
return i18next.t(`agent-chat:modes.${mode}.label`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function runtimeModeHint(mode: RuntimeMode): string {
|
|
17
|
+
return i18next.t(`agent-chat:modes.${mode}.hint`);
|
|
18
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { chatStorageKey, chatStorageLegacyKeys, createChatStore } from '../storage';
|
|
2
|
+
import { uploadBytes } from './attachments';
|
|
3
|
+
import { isEmptyDraft, type ChatDraft } from './drafts';
|
|
4
|
+
import { isRecord } from './logic/json';
|
|
5
|
+
import { persistedJson } from './persisted-json';
|
|
6
|
+
import { withQuote } from './quote';
|
|
7
|
+
import { shortcut } from '@adecore/ui';
|
|
8
|
+
|
|
9
|
+
/* Puts the draft away, or takes the last one back on an empty box. */
|
|
10
|
+
export const STASH_SHORTCUT = shortcut('Mod+S');
|
|
11
|
+
|
|
12
|
+
const storageKey = (): string => chatStorageKey('stash');
|
|
13
|
+
|
|
14
|
+
// Past this the list is an archive nobody reads; the oldest entry makes room for a new one.
|
|
15
|
+
export const STASH_LIMIT = 20;
|
|
16
|
+
|
|
17
|
+
/* What was attached when the prompt was put away. The bytes are not kept, only what they were. */
|
|
18
|
+
export interface StashedAttachment {
|
|
19
|
+
name: string;
|
|
20
|
+
mime: string;
|
|
21
|
+
size: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface StashedPrompt {
|
|
25
|
+
id: string;
|
|
26
|
+
text: string;
|
|
27
|
+
mentions: string[];
|
|
28
|
+
skills: string[];
|
|
29
|
+
attachments: StashedAttachment[];
|
|
30
|
+
createdAt: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function strings(value: unknown): string[] {
|
|
34
|
+
return Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === 'string') : [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function attachments(value: unknown): StashedAttachment[] {
|
|
38
|
+
if (!Array.isArray(value)) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return value.filter(isRecord).map((entry) => ({
|
|
42
|
+
name: typeof entry.name === 'string' ? entry.name : '',
|
|
43
|
+
mime: typeof entry.mime === 'string' ? entry.mime : '',
|
|
44
|
+
size: typeof entry.size === 'number' ? entry.size : 0
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Only the fields we know today, so anything an older or a broken write left behind falls away here. */
|
|
49
|
+
export function parseStash(raw: string | null): StashedPrompt[] {
|
|
50
|
+
if (raw === null) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const stored: unknown = JSON.parse(raw);
|
|
55
|
+
if (!Array.isArray(stored)) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
return stored
|
|
59
|
+
.filter(isRecord)
|
|
60
|
+
.filter((entry) => typeof entry.id === 'string' && typeof entry.text === 'string')
|
|
61
|
+
.map((entry) => ({
|
|
62
|
+
id: entry.id as string,
|
|
63
|
+
text: entry.text as string,
|
|
64
|
+
mentions: strings(entry.mentions),
|
|
65
|
+
skills: strings(entry.skills),
|
|
66
|
+
attachments: attachments(entry.attachments),
|
|
67
|
+
createdAt: typeof entry.createdAt === 'number' ? entry.createdAt : 0
|
|
68
|
+
}))
|
|
69
|
+
.slice(0, STASH_LIMIT);
|
|
70
|
+
} catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Newest first, capped: the entry that has been waiting longest is the one nobody comes back for. */
|
|
76
|
+
export function withStashed(prompts: StashedPrompt[], entry: StashedPrompt): StashedPrompt[] {
|
|
77
|
+
return [entry, ...prompts].slice(0, STASH_LIMIT);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* What a draft looks like on the shelf, its quote folded into the text; null when there is nothing worth putting there. */
|
|
81
|
+
export function stashedFrom(draft: ChatDraft, id: string, now: number): StashedPrompt | null {
|
|
82
|
+
if (isEmptyDraft(draft)) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
id,
|
|
87
|
+
text: withQuote(draft.quote, draft.text),
|
|
88
|
+
mentions: [...draft.mentions],
|
|
89
|
+
skills: [...draft.skills],
|
|
90
|
+
attachments: draft.attachments.map((attachment) => ({ name: attachment.name, mime: attachment.mime, size: uploadBytes(attachment) })),
|
|
91
|
+
createdAt: now
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const storage = persistedJson<StashedPrompt[]>(storageKey, parseStash, [], () => chatStorageLegacyKeys('stash'));
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* Prompts put aside for later can move to another chat, so the shelf is shared across chats.
|
|
99
|
+
*/
|
|
100
|
+
export const useStash = createChatStore<{ prompts: StashedPrompt[] }>(
|
|
101
|
+
() => ({ prompts: [] }),
|
|
102
|
+
(store) => store.setState({ prompts: storage.read() })
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
function write(prompts: StashedPrompt[]): void {
|
|
106
|
+
useStash.setState({ prompts });
|
|
107
|
+
storage.write(prompts);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Puts a draft on the shelf; false when the composer held nothing to put there. */
|
|
111
|
+
export function stashDraft(draft: ChatDraft): boolean {
|
|
112
|
+
const entry = stashedFrom(draft, `stash-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, Date.now());
|
|
113
|
+
if (!entry) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
write(withStashed(useStash.getState().prompts, entry));
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function forgetStashed(id: string): void {
|
|
121
|
+
write(useStash.getState().prompts.filter((entry) => entry.id !== id));
|
|
122
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import i18next from 'i18next';
|
|
2
|
+
import type { ChatItem, ChatSubagentResult } from '@adecore/agent-contracts';
|
|
3
|
+
import { errorCode, isConnectionError, type ChatTransport } from '../transport';
|
|
4
|
+
|
|
5
|
+
// What the panel opens on: the newest end of the conversation, which is where an agent that works is.
|
|
6
|
+
export const SUBAGENT_PAGE = 60;
|
|
7
|
+
|
|
8
|
+
export interface SubagentConversationState {
|
|
9
|
+
status: 'loading' | 'ready' | 'failed';
|
|
10
|
+
items: ChatItem[];
|
|
11
|
+
source: ChatSubagentResult['source'] | null;
|
|
12
|
+
context: NonNullable<ChatSubagentResult['context']> | null;
|
|
13
|
+
/* Where the page before the oldest item on screen starts; null once the start is there. */
|
|
14
|
+
cursor: string | null;
|
|
15
|
+
live: boolean;
|
|
16
|
+
loadingEarlier: boolean;
|
|
17
|
+
/* Why nothing could be read, as the machine said it. */
|
|
18
|
+
error: string | null;
|
|
19
|
+
/* The machine answered that it knows no such request, which is a machine from before this panel. */
|
|
20
|
+
unsupported: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const INITIAL_CONVERSATION: SubagentConversationState = {
|
|
24
|
+
status: 'loading',
|
|
25
|
+
items: [],
|
|
26
|
+
source: null,
|
|
27
|
+
context: null,
|
|
28
|
+
cursor: null,
|
|
29
|
+
live: false,
|
|
30
|
+
loadingEarlier: false,
|
|
31
|
+
error: null,
|
|
32
|
+
unsupported: false
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
* The newest page laid over what is on screen. An item that is already there is replaced where it
|
|
37
|
+
* stands and a new one goes after them all. A page that shares nothing with what is held means more
|
|
38
|
+
* happened than one page holds, so what is held is dropped for it rather than shown with a hole.
|
|
39
|
+
*/
|
|
40
|
+
export function mergeNewest(
|
|
41
|
+
current: ChatItem[],
|
|
42
|
+
currentCursor: string | null,
|
|
43
|
+
page: ChatItem[],
|
|
44
|
+
pageCursor: string | null
|
|
45
|
+
): { items: ChatItem[]; cursor: string | null } {
|
|
46
|
+
const known = new Map(current.map((item, index) => [item.id, index]));
|
|
47
|
+
if (current.length > 0 && page.length > 0 && !page.some((item) => known.has(item.id))) {
|
|
48
|
+
return { items: page, cursor: pageCursor };
|
|
49
|
+
}
|
|
50
|
+
const items = [...current];
|
|
51
|
+
for (const item of page) {
|
|
52
|
+
const index = known.get(item.id);
|
|
53
|
+
if (index === undefined) {
|
|
54
|
+
items.push(item);
|
|
55
|
+
} else {
|
|
56
|
+
items[index] = item;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { items, cursor: current.length === 0 ? pageCursor : currentCursor };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function messageOf(error: unknown): string {
|
|
63
|
+
return error instanceof Error ? error.message : i18next.t('agent-chat:subagents.noAnswer');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/*
|
|
67
|
+
* One subagent's conversation as a panel follows it. Opening asks for the newest page and holds the
|
|
68
|
+
* conversation on the machine, which then says whenever it grew; each time, the newest page is asked
|
|
69
|
+
* again and laid over what is there. A socket that comes back holds it again, and letting go is the
|
|
70
|
+
* last thing it says.
|
|
71
|
+
*/
|
|
72
|
+
export class SubagentConversation {
|
|
73
|
+
private readonly transport: ChatTransport;
|
|
74
|
+
private readonly chatId: string;
|
|
75
|
+
private readonly toolUseId: string;
|
|
76
|
+
private readonly onChange: (state: SubagentConversationState) => void;
|
|
77
|
+
private readonly unsubscribe: Array<() => void> = [];
|
|
78
|
+
private state: SubagentConversationState = INITIAL_CONVERSATION;
|
|
79
|
+
private disposed = false;
|
|
80
|
+
// One request at a time, so a refresh never lands under an older page that arrived after it.
|
|
81
|
+
private queue: Promise<void> = Promise.resolve();
|
|
82
|
+
private refreshWaiting = false;
|
|
83
|
+
private earlier: Promise<void> | null = null;
|
|
84
|
+
|
|
85
|
+
constructor(transport: ChatTransport, chatId: string, toolUseId: string, onChange: (state: SubagentConversationState) => void) {
|
|
86
|
+
this.transport = transport;
|
|
87
|
+
this.chatId = chatId;
|
|
88
|
+
this.toolUseId = toolUseId;
|
|
89
|
+
this.onChange = onChange;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get current(): SubagentConversationState {
|
|
93
|
+
return this.state;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
start(): Promise<void> {
|
|
97
|
+
this.unsubscribe.push(
|
|
98
|
+
this.transport.on('chat.subagentChanged', (event) => {
|
|
99
|
+
if (event.chatId === this.chatId && event.toolUseId === this.toolUseId) {
|
|
100
|
+
void this.refresh();
|
|
101
|
+
}
|
|
102
|
+
}),
|
|
103
|
+
this.transport.subscribeStatus((status) => {
|
|
104
|
+
if (status === 'open') {
|
|
105
|
+
void this.enqueue(() => this.readNewest(true));
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
);
|
|
109
|
+
return this.enqueue(() => this.readNewest(true));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* The newest page again; calls that arrive while one waits become that one. */
|
|
113
|
+
refresh(): Promise<void> {
|
|
114
|
+
if (this.refreshWaiting) {
|
|
115
|
+
return this.queue;
|
|
116
|
+
}
|
|
117
|
+
this.refreshWaiting = true;
|
|
118
|
+
return this.enqueue(() => {
|
|
119
|
+
this.refreshWaiting = false;
|
|
120
|
+
return this.readNewest(false);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
loadEarlier(): Promise<void> {
|
|
125
|
+
// A page on its way answers every call made meanwhile; a scroll asks for one on every frame.
|
|
126
|
+
this.earlier ??= this.enqueue(async () => {
|
|
127
|
+
const cursor = this.state.cursor;
|
|
128
|
+
if (cursor === null) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
this.set({ loadingEarlier: true });
|
|
132
|
+
try {
|
|
133
|
+
const page = await this.ask({ cursor, limit: SUBAGENT_PAGE });
|
|
134
|
+
this.set({ items: [...page.items, ...this.state.items], cursor: page.history.cursor, live: page.live, loadingEarlier: false });
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (errorCode(error) === 'history-expired') {
|
|
137
|
+
// The record was written again under this panel; what it holds now is the only truth.
|
|
138
|
+
this.set({ items: [], cursor: null, loadingEarlier: false });
|
|
139
|
+
await this.readNewest(false);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this.set({ loadingEarlier: false, error: messageOf(error) });
|
|
143
|
+
}
|
|
144
|
+
}).finally(() => {
|
|
145
|
+
this.earlier = null;
|
|
146
|
+
});
|
|
147
|
+
return this.earlier;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
dispose(): void {
|
|
151
|
+
if (this.disposed) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.disposed = true;
|
|
155
|
+
for (const off of this.unsubscribe.splice(0)) {
|
|
156
|
+
off();
|
|
157
|
+
}
|
|
158
|
+
if (this.transport.status === 'open') {
|
|
159
|
+
void this.transport.request('chat.subagent', { chatId: this.chatId, toolUseId: this.toolUseId, limit: 1, watch: false }).catch(() => undefined);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private async readNewest(watch: boolean): Promise<void> {
|
|
164
|
+
try {
|
|
165
|
+
const page = await this.ask({ limit: SUBAGENT_PAGE, ...(watch ? { watch: true } : {}) });
|
|
166
|
+
const merged = mergeNewest(this.state.items, this.state.cursor, page.items, page.history.cursor);
|
|
167
|
+
this.set({
|
|
168
|
+
status: 'ready',
|
|
169
|
+
items: merged.items,
|
|
170
|
+
cursor: merged.cursor,
|
|
171
|
+
live: page.live,
|
|
172
|
+
source: page.source,
|
|
173
|
+
context: page.context ?? null,
|
|
174
|
+
error: null
|
|
175
|
+
});
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (this.state.status === 'ready' && isConnectionError(error)) {
|
|
178
|
+
// What is on screen stays; the socket that comes back asks again.
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const unsupported = errorCode(error) === 'unknown-request';
|
|
182
|
+
this.set({ status: 'failed', error: messageOf(error), unsupported });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private ask(extra: { cursor?: string; limit: number; watch?: boolean }): Promise<ChatSubagentResult> {
|
|
187
|
+
return this.transport.request('chat.subagent', { chatId: this.chatId, toolUseId: this.toolUseId, ...extra });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private enqueue(work: () => Promise<void>): Promise<void> {
|
|
191
|
+
const next = this.queue.then(() => (this.disposed ? undefined : work()));
|
|
192
|
+
this.queue = next.catch(() => undefined);
|
|
193
|
+
return next;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private set(patch: Partial<SubagentConversationState>): void {
|
|
197
|
+
if (this.disposed) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
this.state = { ...this.state, ...patch };
|
|
201
|
+
this.onChange(this.state);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import i18next from 'i18next';
|
|
2
|
+
import type { ChatInfo, ChatItem, ChatSubagentItem } from '@adecore/agent-contracts';
|
|
3
|
+
import { formatMoment, formatElapsedShort, formatTokens } from '@adecore/ui/format';
|
|
4
|
+
import type { StatusWord } from '../agents/status-look';
|
|
5
|
+
import type { SubagentTask } from '../host';
|
|
6
|
+
|
|
7
|
+
export function subagentTitle(item: ChatSubagentItem): string {
|
|
8
|
+
return item.description || item.summary || item.subagentType || i18next.t('agent-chat:rows.subagent.label');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* What the bar over a sub-agent's conversation says of it beside its state and its time; the model is named by the caller, which has the catalog. */
|
|
12
|
+
export function subagentFacts(item: ChatSubagentItem, modelName: string | null): string[] {
|
|
13
|
+
return [
|
|
14
|
+
modelName,
|
|
15
|
+
item.subagentType,
|
|
16
|
+
item.background ? i18next.t('agent-chat:rows.subagent.background') : null,
|
|
17
|
+
item.usage !== null && item.usage.totalTokens > 0
|
|
18
|
+
? i18next.t('agent-chat:subagents.info.tokens', { tokens: formatTokens(item.usage.totalTokens) })
|
|
19
|
+
: null,
|
|
20
|
+
item.usage !== null && item.usage.toolUses > 0 ? i18next.t('agent-chat:group.toolCalls', { count: item.usage.toolUses }) : null
|
|
21
|
+
].filter((fact): fact is string => fact !== null && fact !== '');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* The task a row stands for, whose id the host wrote into the row's own. */
|
|
25
|
+
export function taskIdOf(item: ChatSubagentItem): string | null {
|
|
26
|
+
return item.origin === 'ruimte' && item.id.startsWith('task-') ? item.id.slice(5) : null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* A cancelled task is a failed row on the wire and a paused one a running row, and only the task itself still says which it is. */
|
|
30
|
+
export function statusWordOf(item: ChatSubagentItem, task: SubagentTask | null): StatusWord {
|
|
31
|
+
if (item.status === 'failed' && task?.status === 'cancelled') {
|
|
32
|
+
return 'cancelled';
|
|
33
|
+
}
|
|
34
|
+
if (item.status === 'running' && task?.status === 'open' && task.paused !== undefined) {
|
|
35
|
+
return 'paused';
|
|
36
|
+
}
|
|
37
|
+
return item.status;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* What a task's row says its agent is doing: the task's own state, or that the agent waits on a person while the task runs. */
|
|
41
|
+
export type TaskRowState = StatusWord | 'waiting';
|
|
42
|
+
|
|
43
|
+
export function taskRowStateOf(item: ChatSubagentItem, task: SubagentTask | null, child: ChatInfo | undefined): TaskRowState {
|
|
44
|
+
const word = statusWordOf(item, task);
|
|
45
|
+
if (word === 'running' && (child?.status === 'needs-you' || (child?.requests?.length ?? 0) > 0)) {
|
|
46
|
+
return 'waiting';
|
|
47
|
+
}
|
|
48
|
+
return word;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
* What the flyout over the composer lists: every sub-agent still at work, and the ones that settled
|
|
53
|
+
* since the last message, so a finished batch stays until the next message is sent. That includes an
|
|
54
|
+
* older row a message woke again. A settled one from before that message stays too while a sibling of
|
|
55
|
+
* its turn still runs. Older ones are only in the thread.
|
|
56
|
+
*/
|
|
57
|
+
export function flyoutSubagents(order: readonly string[], structure: Readonly<Record<string, ChatItem>>): ChatSubagentItem[] {
|
|
58
|
+
const lastMessage = order.findLastIndex((id) => structure[id]?.kind === 'user');
|
|
59
|
+
const lastMessageAt = lastMessage < 0 ? null : (structure[order[lastMessage]!]?.createdAt ?? null);
|
|
60
|
+
const items = order.flatMap((id, index) => {
|
|
61
|
+
const item = structure[id];
|
|
62
|
+
return item?.kind === 'subagent' ? [{ item, index }] : [];
|
|
63
|
+
});
|
|
64
|
+
const turns = new Set(items.flatMap(({ item }) => (item.status === 'running' && item.turnId !== null ? [item.turnId] : [])));
|
|
65
|
+
return items
|
|
66
|
+
.filter(
|
|
67
|
+
({ item, index }) =>
|
|
68
|
+
item.status === 'running' ||
|
|
69
|
+
index > lastMessage ||
|
|
70
|
+
(lastMessageAt !== null && item.finishedAt !== null && item.finishedAt > lastMessageAt) ||
|
|
71
|
+
(item.turnId !== null && turns.has(item.turnId))
|
|
72
|
+
)
|
|
73
|
+
.map(({ item }) => item);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* The one state the badge shows for all of them: work in progress first, then work held up, then whatever went wrong. */
|
|
77
|
+
export function summaryWordOf(words: readonly StatusWord[]): StatusWord {
|
|
78
|
+
return (['running', 'paused', 'failed', 'cancelled'] as const).find((word) => words.includes(word)) ?? 'done';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* The number on the badge: the ones still at work, a paused one included, and all of them once none is. */
|
|
82
|
+
export function badgeCountOf(words: readonly StatusWord[]): number {
|
|
83
|
+
const active = words.filter((word) => word === 'running' || word === 'paused').length;
|
|
84
|
+
return active > 0 ? active : words.length;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* The time on the right of an entry: how long it has run so far, or how long it took once it settled. */
|
|
88
|
+
export function entryTimeOf(item: ChatSubagentItem, task: SubagentTask | null, now: number): string {
|
|
89
|
+
// A task's own record says when it was given and settled; the row copies those, but may lag behind it.
|
|
90
|
+
const startedAt = task?.createdAt ?? item.startedAt;
|
|
91
|
+
const finishedAt = task === null ? item.finishedAt : (task.settledAt ?? item.finishedAt);
|
|
92
|
+
const word = statusWordOf(item, task);
|
|
93
|
+
if (word === 'paused') {
|
|
94
|
+
const until = task?.paused?.until;
|
|
95
|
+
return until === undefined
|
|
96
|
+
? i18next.t('agent-chat:common.status.paused')
|
|
97
|
+
: i18next.t('agent-chat:activity.pausedUntil', { time: formatMoment(until, now) });
|
|
98
|
+
}
|
|
99
|
+
if (item.status === 'running') {
|
|
100
|
+
return startedAt > 0 ? formatElapsedShort(now - startedAt) : '';
|
|
101
|
+
}
|
|
102
|
+
if (startedAt <= 0 || finishedAt === null || finishedAt < startedAt) {
|
|
103
|
+
return i18next.t(`agent-chat:common.status.${word}`);
|
|
104
|
+
}
|
|
105
|
+
return i18next.t(`agent-chat:activity.took.${word}`, { duration: formatElapsedShort(finishedAt - startedAt) });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/*
|
|
109
|
+
* What the Stop of an active entry does: a task stops the node working on it, and a subagent of the
|
|
110
|
+
* CLI's own is only marked stopped, since no CLI stops one on its own. While the chat's turn runs that
|
|
111
|
+
* turn may still wait on it, so stopping the turn (the composer's Stop) is the only offer then.
|
|
112
|
+
*/
|
|
113
|
+
export type SubagentStop = 'task' | 'mark';
|
|
114
|
+
|
|
115
|
+
export function stopOf(item: ChatSubagentItem, turnRunning: boolean): SubagentStop | null {
|
|
116
|
+
if (item.status !== 'running') {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
if (item.origin === 'ruimte') {
|
|
120
|
+
return item.childId === undefined ? null : 'task';
|
|
121
|
+
}
|
|
122
|
+
return turnRunning ? null : 'mark';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function stopLabel(stop: SubagentStop): string {
|
|
126
|
+
return stop === 'task' ? i18next.t('agent-chat:subagents.stop.task') : i18next.t('agent-chat:subagents.stop.mark');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/*
|
|
130
|
+
* What the composer's Stop does. A plain click stops the turn alone, as it always has; Shift also ends
|
|
131
|
+
* every agent the chat opened and marks its CLI's own sub-agents stopped, which a turn alone leaves running.
|
|
132
|
+
*/
|
|
133
|
+
export type ComposerStop = 'turn' | 'turn-and-subagents';
|
|
134
|
+
|
|
135
|
+
export function composerStopOf(shiftKey: boolean): ComposerStop {
|
|
136
|
+
return shiftKey ? 'turn-and-subagents' : 'turn';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function composerStopLabel(): string {
|
|
140
|
+
return i18next.t('agent-chat:composer.stop');
|
|
141
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
|
|
3
|
+
interface SubagentSupportStore {
|
|
4
|
+
/* The hosts, by scope id, that answered that they cannot open a subagent's conversation, for this page's life. */
|
|
5
|
+
unsupported: Record<string, true>;
|
|
6
|
+
markUnsupported(scopeId: string): void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* Whether a host can open a subagent's conversation is learned by asking once rather than from a
|
|
11
|
+
* version: the first refusal hides the button on that host's rows that carry no pointer of their own.
|
|
12
|
+
*/
|
|
13
|
+
export const useSubagentSupport = create<SubagentSupportStore>((set, get) => ({
|
|
14
|
+
unsupported: {},
|
|
15
|
+
markUnsupported(scopeId) {
|
|
16
|
+
if (get().unsupported[scopeId]) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
set({ unsupported: { ...get().unsupported, [scopeId]: true } });
|
|
20
|
+
}
|
|
21
|
+
}));
|