@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,30 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { accountName, accountsOfKind, hasAccountChoice, offeredAccounts } from '../agents/accounts.js';
|
|
3
|
+
import { useProviderAccounts } from '../state/provider-accounts.js';
|
|
4
|
+
import { useProviders } from '../state/providers.js';
|
|
5
|
+
/*
|
|
6
|
+
* The accounts of a chat's CLI on the machine in scope and the one the chat runs under, or null while
|
|
7
|
+
* the CLI has fewer than two accounts that are on: with one there is nothing to choose or tell apart.
|
|
8
|
+
*/
|
|
9
|
+
export function useAccountChoice(kind, account) {
|
|
10
|
+
const accounts = useProviderAccounts((row) => row.accounts);
|
|
11
|
+
const providerName = useProviders((row) => row.providers.find((entry) => entry.kind === kind)?.name) ?? kind ?? '';
|
|
12
|
+
return useMemo(() => {
|
|
13
|
+
if (kind === null || accounts === null) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const entries = accountsOfKind(accounts, kind);
|
|
17
|
+
if (!hasAccountChoice(entries)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const currentId = account ?? kind;
|
|
21
|
+
return {
|
|
22
|
+
accounts,
|
|
23
|
+
offered: offeredAccounts(entries, currentId),
|
|
24
|
+
currentId,
|
|
25
|
+
current: entries.find((entry) => entry.id === currentId) ?? null,
|
|
26
|
+
providerName,
|
|
27
|
+
nameOf: (entry) => accountName(entry, providerName)
|
|
28
|
+
};
|
|
29
|
+
}, [accounts, kind, account, providerName]);
|
|
30
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ChatActions } from '../host.js';
|
|
2
|
+
import { type ChatScope } from '../scope.js';
|
|
3
|
+
import { type ChatTransport } from '../transport.js';
|
|
4
|
+
export declare function transportActions(transport: ChatTransport): ChatActions;
|
|
5
|
+
export declare function actionsOf(scope: ChatScope): ChatActions;
|
|
6
|
+
export declare function useChatActions(): ChatActions;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import i18next from 'i18next';
|
|
2
|
+
import { chatHost } from '../host.js';
|
|
3
|
+
import { useChatScope } from '../scope.js';
|
|
4
|
+
import { ChatTransportError } from '../transport.js';
|
|
5
|
+
/* Every action as the request a chat host answers, for an app that runs them through nothing of its own. */
|
|
6
|
+
export function transportActions(transport) {
|
|
7
|
+
return {
|
|
8
|
+
clear: async (chatId, force) => {
|
|
9
|
+
await transport.request('chat.clear', { chatId, ...(force ? { force: true } : {}) });
|
|
10
|
+
},
|
|
11
|
+
stopTurn: async (chatId, subagents) => {
|
|
12
|
+
await transport.request('chat.cancel', { chatId, ...(subagents ? { subagents: true } : {}) });
|
|
13
|
+
},
|
|
14
|
+
unqueue: async (chatId, messageId) => {
|
|
15
|
+
const { message } = await transport.request('chat.unqueue', { chatId, messageId });
|
|
16
|
+
// A host answers without the message once it went out, which is what the composer tells a person.
|
|
17
|
+
if (message === undefined) {
|
|
18
|
+
throw new ChatTransportError('request-not-found', i18next.t('agent-chat:composer.queue.alreadySent'));
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
sendNow: async (chatId, messageId) => {
|
|
22
|
+
await transport.request('chat.sendNow', { chatId, messageId });
|
|
23
|
+
},
|
|
24
|
+
compact: async (chatId) => {
|
|
25
|
+
await transport.request('chat.compact', { chatId });
|
|
26
|
+
},
|
|
27
|
+
configure: async (chatId, patch) => {
|
|
28
|
+
await transport.request('chat.configure', { chatId, ...patch });
|
|
29
|
+
},
|
|
30
|
+
continueOn: async (chatId, account) => {
|
|
31
|
+
await transport.request('chat.continueOn', { chatId, account });
|
|
32
|
+
},
|
|
33
|
+
turnDiff: async (chatId, turnId) => (await transport.request('chat.turnDiff', { chatId, turnId })).diff,
|
|
34
|
+
stopSubagent: async (chatId, toolUseId) => {
|
|
35
|
+
await transport.request('chat.stopSubagent', { chatId, toolUseId });
|
|
36
|
+
},
|
|
37
|
+
stopTask: async (chatId, taskId) => {
|
|
38
|
+
await transport.request('chat.stopTask', { chatId, taskId });
|
|
39
|
+
},
|
|
40
|
+
approve: async (chatId, requestId, decision, message) => {
|
|
41
|
+
await transport.request('chat.approve', { chatId, requestId, decision, ...(message === undefined ? {} : { message }) });
|
|
42
|
+
},
|
|
43
|
+
answer: async (chatId, requestId, answers) => {
|
|
44
|
+
await transport.request('chat.answer', { chatId, requestId, answers });
|
|
45
|
+
},
|
|
46
|
+
dismiss: async (chatId, itemId) => {
|
|
47
|
+
await transport.request('chat.dismiss', { chatId, itemId });
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const byTransport = new WeakMap();
|
|
52
|
+
/* The app's own actions when it has them, and the requests of this scope's host otherwise. */
|
|
53
|
+
export function actionsOf(scope) {
|
|
54
|
+
const own = chatHost().actions;
|
|
55
|
+
if (own !== null) {
|
|
56
|
+
return own;
|
|
57
|
+
}
|
|
58
|
+
const { transport } = scope;
|
|
59
|
+
let actions = byTransport.get(transport);
|
|
60
|
+
if (actions === undefined) {
|
|
61
|
+
actions = transportActions(transport);
|
|
62
|
+
byTransport.set(transport, actions);
|
|
63
|
+
}
|
|
64
|
+
return actions;
|
|
65
|
+
}
|
|
66
|
+
export function useChatActions() {
|
|
67
|
+
return actionsOf(useChatScope());
|
|
68
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ChatAttachment, type ChatAttachmentUpload } from '@adecore/agent-contracts';
|
|
2
|
+
interface IncomingFile {
|
|
3
|
+
name: string;
|
|
4
|
+
mime: string;
|
|
5
|
+
bytes: number;
|
|
6
|
+
}
|
|
7
|
+
interface AttachmentCheck<T extends IncomingFile> {
|
|
8
|
+
accepted: T[];
|
|
9
|
+
rejected: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
reason: string;
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
export declare function isImageAttachment(mime: string): boolean;
|
|
15
|
+
export declare function fileBadge(name: string): string | null;
|
|
16
|
+
export declare function formatBytes(bytes: number): string;
|
|
17
|
+
export declare function checkAttachmentLimits<T extends IncomingFile>(current: number, incoming: T[], currentBytes?: number): AttachmentCheck<T>;
|
|
18
|
+
export declare function filesOf(transfer: DataTransfer | null): File[];
|
|
19
|
+
export declare function readAttachments(files: File[]): Promise<ChatAttachmentUpload[]>;
|
|
20
|
+
export declare function readStoredAttachments(read: (attachment: ChatAttachment) => Promise<Blob>, stored: readonly ChatAttachment[]): Promise<ChatAttachmentUpload[]>;
|
|
21
|
+
export declare function uploadPreviewUrl(upload: ChatAttachmentUpload): string;
|
|
22
|
+
export declare function uploadBytes(upload: ChatAttachmentUpload): number;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import i18next from 'i18next';
|
|
2
|
+
import { attachmentBytes, attachmentImageMime, CHAT_ATTACHMENTS_MAX_BYTES, CHAT_ATTACHMENTS_MAX_COUNT, CHAT_ATTACHMENT_MAX_BYTES } from '@adecore/agent-contracts';
|
|
3
|
+
import { formatBytes as bytesOf } from '@adecore/ui/format';
|
|
4
|
+
export function isImageAttachment(mime) {
|
|
5
|
+
return mime.startsWith('image/');
|
|
6
|
+
}
|
|
7
|
+
/* The type on a file card: the extension, as long as it is short enough to read as one. */
|
|
8
|
+
export function fileBadge(name) {
|
|
9
|
+
const dot = name.lastIndexOf('.');
|
|
10
|
+
if (dot <= 0 || dot === name.length - 1) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const extension = name.slice(dot + 1);
|
|
14
|
+
return extension.length <= 5 ? extension.toUpperCase() : null;
|
|
15
|
+
}
|
|
16
|
+
/* Whole kilobytes, which is all a row under a file name has room for, and a decimal once it runs into megabytes. */
|
|
17
|
+
export function formatBytes(bytes) {
|
|
18
|
+
return bytesOf(bytes, bytes < 1024 * 1024);
|
|
19
|
+
}
|
|
20
|
+
/* Which of the incoming files fit next to what the composer already holds, and why the rest do not. */
|
|
21
|
+
export function checkAttachmentLimits(current, incoming, currentBytes = 0) {
|
|
22
|
+
const accepted = [];
|
|
23
|
+
const rejected = [];
|
|
24
|
+
let bytes = currentBytes;
|
|
25
|
+
for (const file of incoming) {
|
|
26
|
+
if (file.bytes === 0) {
|
|
27
|
+
rejected.push({ name: file.name, reason: i18next.t('agent-chat:attachments.rejected.empty') });
|
|
28
|
+
}
|
|
29
|
+
else if (file.bytes > CHAT_ATTACHMENT_MAX_BYTES) {
|
|
30
|
+
rejected.push({ name: file.name, reason: i18next.t('agent-chat:attachments.rejected.tooLarge', { size: formatBytes(CHAT_ATTACHMENT_MAX_BYTES) }) });
|
|
31
|
+
}
|
|
32
|
+
else if (current + accepted.length >= CHAT_ATTACHMENTS_MAX_COUNT) {
|
|
33
|
+
rejected.push({ name: file.name, reason: i18next.t('agent-chat:attachments.rejected.tooMany', { count: CHAT_ATTACHMENTS_MAX_COUNT }) });
|
|
34
|
+
}
|
|
35
|
+
else if (bytes + file.bytes > CHAT_ATTACHMENTS_MAX_BYTES) {
|
|
36
|
+
rejected.push({
|
|
37
|
+
name: file.name,
|
|
38
|
+
reason: i18next.t('agent-chat:attachments.rejected.tooHeavy', { size: formatBytes(CHAT_ATTACHMENTS_MAX_BYTES) })
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
accepted.push(file);
|
|
43
|
+
bytes += file.bytes;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { accepted, rejected };
|
|
47
|
+
}
|
|
48
|
+
/* The files in a paste or a drop; a paste of plain text gives none. */
|
|
49
|
+
export function filesOf(transfer) {
|
|
50
|
+
return transfer ? [...transfer.files] : [];
|
|
51
|
+
}
|
|
52
|
+
function readAsBase64(file) {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const reader = new FileReader();
|
|
55
|
+
reader.onerror = () => reject(reader.error ?? new Error(i18next.t('agent-chat:attachments.unreadable', { name: file.name })));
|
|
56
|
+
reader.onload = () => {
|
|
57
|
+
const url = String(reader.result);
|
|
58
|
+
resolve(url.slice(url.indexOf(',') + 1));
|
|
59
|
+
};
|
|
60
|
+
reader.readAsDataURL(file);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// A pasted screenshot has no name; the clock gives it one so the row and the agent can tell them apart.
|
|
64
|
+
function nameFor(file, index) {
|
|
65
|
+
return file.name || `pasted-${Date.now()}-${index + 1}.${file.type.split('/')[1] ?? 'bin'}`;
|
|
66
|
+
}
|
|
67
|
+
export async function readAttachments(files) {
|
|
68
|
+
return Promise.all(files.map(async (file, index) => ({
|
|
69
|
+
name: nameFor(file, index),
|
|
70
|
+
mime: attachmentImageMime({ name: file.name, mime: file.type }) ?? (file.type || 'application/octet-stream'),
|
|
71
|
+
data: await readAsBase64(file)
|
|
72
|
+
})));
|
|
73
|
+
}
|
|
74
|
+
/* Files the host already stored for a chat, back as uploads the composer can hold again. */
|
|
75
|
+
export async function readStoredAttachments(read, stored) {
|
|
76
|
+
const files = await Promise.all(stored.map(async (attachment) => new File([await read(attachment)], attachment.name, { type: attachment.mime })));
|
|
77
|
+
return readAttachments(files);
|
|
78
|
+
}
|
|
79
|
+
/* A preview of a file the composer still holds; the bytes have not reached the host yet. */
|
|
80
|
+
export function uploadPreviewUrl(upload) {
|
|
81
|
+
return `data:${upload.mime};base64,${upload.data}`;
|
|
82
|
+
}
|
|
83
|
+
/* What the file weighs, from the base64 the composer is holding: three bytes per four characters. */
|
|
84
|
+
export function uploadBytes(upload) {
|
|
85
|
+
return attachmentBytes(upload.data);
|
|
86
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ChatBookmark } from '@adecore/agent-contracts';
|
|
2
|
+
import type { ChatScope } from '../scope.js';
|
|
3
|
+
interface BookmarkNaming {
|
|
4
|
+
chatKey: string | null;
|
|
5
|
+
itemId: string | null;
|
|
6
|
+
open(chatKey: string, itemId: string): void;
|
|
7
|
+
close(): void;
|
|
8
|
+
}
|
|
9
|
+
export declare const useBookmarkNaming: import("zustand").UseBoundStore<import("zustand").StoreApi<BookmarkNaming>>;
|
|
10
|
+
export declare function placeBookmark(scope: ChatScope, chatId: string, itemId: string): Promise<void>;
|
|
11
|
+
export declare function nameBookmark(scope: ChatScope, chatId: string, itemId: string, name: string, previous: string | undefined): Promise<void>;
|
|
12
|
+
export declare function removeBookmark(scope: ChatScope, chatId: string, bookmark: ChatBookmark): Promise<void>;
|
|
13
|
+
export declare function goToBookmark(scope: ChatScope, chatId: string, itemId: string, rename?: boolean): boolean;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import i18next from 'i18next';
|
|
2
|
+
import { create } from 'zustand';
|
|
3
|
+
import { chatHost } from '../host.js';
|
|
4
|
+
import { jumpToTimelineItem } from './timeline-scroll.js';
|
|
5
|
+
export const useBookmarkNaming = create((set) => ({
|
|
6
|
+
chatKey: null,
|
|
7
|
+
itemId: null,
|
|
8
|
+
open: (chatKey, itemId) => set({ chatKey, itemId }),
|
|
9
|
+
close: () => set({ chatKey: null, itemId: null })
|
|
10
|
+
}));
|
|
11
|
+
function failed(title, e) {
|
|
12
|
+
chatHost().notify({ title, description: e instanceof Error ? e.message : String(e), kind: 'error' });
|
|
13
|
+
}
|
|
14
|
+
async function add(scope, chatId, itemId, name) {
|
|
15
|
+
try {
|
|
16
|
+
await scope.chats.addBookmark(chatId, itemId, name);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
failed(i18next.t('agent-chat:bookmarks.addFailed'), e);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/* Marks a message and opens the field for its name at once, before the host answered. */
|
|
25
|
+
export async function placeBookmark(scope, chatId, itemId) {
|
|
26
|
+
useBookmarkNaming.getState().open(scope.keyOf(chatId), itemId);
|
|
27
|
+
if (!(await add(scope, chatId, itemId))) {
|
|
28
|
+
useBookmarkNaming.getState().close();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/*
|
|
32
|
+
* The name a field settles on. A name goes through `addBookmark`, which also names a bookmark that
|
|
33
|
+
* already stands, so a name typed before the mark itself landed is not refused.
|
|
34
|
+
*/
|
|
35
|
+
export async function nameBookmark(scope, chatId, itemId, name, previous) {
|
|
36
|
+
const next = name.trim();
|
|
37
|
+
if (next === (previous ?? '')) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (next !== '') {
|
|
41
|
+
await add(scope, chatId, itemId, next);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
await scope.chats.renameBookmark(chatId, itemId, '');
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
failed(i18next.t('agent-chat:bookmarks.renameFailed'), e);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/* Takes a bookmark away at once; the toast puts it back, name and all. */
|
|
52
|
+
export async function removeBookmark(scope, chatId, bookmark) {
|
|
53
|
+
try {
|
|
54
|
+
await scope.chats.removeBookmark(chatId, bookmark.itemId);
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
failed(i18next.t('agent-chat:bookmarks.removeFailed'), e);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
chatHost().notify({
|
|
61
|
+
title: i18next.t('agent-chat:bookmarks.removed'),
|
|
62
|
+
kind: 'deleted',
|
|
63
|
+
action: { label: i18next.t('agent-chat:common.action.undo'), run: () => void add(scope, chatId, bookmark.itemId, bookmark.name) }
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/* Scrolls the chat's thread to the message, and opens its name field for a rename. False when no thread of it is on screen yet. */
|
|
67
|
+
export function goToBookmark(scope, chatId, itemId, rename = false) {
|
|
68
|
+
const key = scope.keyOf(chatId);
|
|
69
|
+
if (rename) {
|
|
70
|
+
useBookmarkNaming.getState().open(key, itemId);
|
|
71
|
+
}
|
|
72
|
+
return jumpToTimelineItem(key, itemId);
|
|
73
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { AgentKind, ChatAttachmentUpload, ChatAttachResult, ChatBookmark, ChatPreferencesPayload, ChatSkill, ModelSelection, ProviderInfo, RuntimeMode } from '@adecore/agent-contracts';
|
|
2
|
+
import type { ChatSink } from '../state/chats.js';
|
|
3
|
+
import { type ChatTransport } from '../transport.js';
|
|
4
|
+
interface ChatOpenOptions {
|
|
5
|
+
provider?: AgentKind;
|
|
6
|
+
account?: string;
|
|
7
|
+
cwd?: string;
|
|
8
|
+
resume?: string;
|
|
9
|
+
selection?: ModelSelection;
|
|
10
|
+
runtimeMode?: RuntimeMode;
|
|
11
|
+
}
|
|
12
|
+
export interface ChatSendExtras {
|
|
13
|
+
mentions?: string[];
|
|
14
|
+
skills?: string[];
|
|
15
|
+
chats?: string[];
|
|
16
|
+
attachments?: ChatAttachmentUpload[];
|
|
17
|
+
}
|
|
18
|
+
interface ProviderSink {
|
|
19
|
+
setProviders(providers: ProviderInfo[]): void;
|
|
20
|
+
}
|
|
21
|
+
export declare class ChatClient {
|
|
22
|
+
private readonly transport;
|
|
23
|
+
private readonly sink;
|
|
24
|
+
private readonly providers;
|
|
25
|
+
private readonly mounted;
|
|
26
|
+
private readonly unsubscribe;
|
|
27
|
+
private readonly earlier;
|
|
28
|
+
private preferences;
|
|
29
|
+
constructor(transport: ChatTransport, sink: ChatSink, providers?: ProviderSink | null);
|
|
30
|
+
private readonly inspections;
|
|
31
|
+
inspect(chatId: string): Promise<ChatAttachResult>;
|
|
32
|
+
open(chatId: string, options: ChatOpenOptions): Promise<boolean>;
|
|
33
|
+
retarget(chatId: string, provider: AgentKind, selection: ModelSelection): Promise<void>;
|
|
34
|
+
setPreferences(preferences: ChatPreferencesPayload): void;
|
|
35
|
+
detach(chatId: string): Promise<void>;
|
|
36
|
+
send(chatId: string, text: string, extras?: ChatSendExtras): Promise<{
|
|
37
|
+
queued: boolean;
|
|
38
|
+
turnId?: string;
|
|
39
|
+
}>;
|
|
40
|
+
addBookmark(chatId: string, itemId: string, name?: string): Promise<ChatBookmark[]>;
|
|
41
|
+
renameBookmark(chatId: string, itemId: string, name: string): Promise<ChatBookmark[]>;
|
|
42
|
+
removeBookmark(chatId: string, itemId: string): Promise<ChatBookmark[]>;
|
|
43
|
+
listSkills(chatId: string): Promise<ChatSkill[]>;
|
|
44
|
+
clear(chatId: string, force?: boolean): Promise<void>;
|
|
45
|
+
kill(chatId: string): Promise<void>;
|
|
46
|
+
loadStatuses(): Promise<void>;
|
|
47
|
+
loadProviders(): Promise<void>;
|
|
48
|
+
loadEarlier(chatId: string, cursor: string): Promise<void>;
|
|
49
|
+
isMounted(chatId: string): boolean;
|
|
50
|
+
dispose(): void;
|
|
51
|
+
private attach;
|
|
52
|
+
private sendPreferences;
|
|
53
|
+
private onStatus;
|
|
54
|
+
}
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { MountedRegistry } from '../mounted-registry.js';
|
|
2
|
+
import { errorCode, isConnectionError } from '../transport.js';
|
|
3
|
+
/* How many items a chat reads at once, on attach and for every page before it. */
|
|
4
|
+
const HISTORY_PAGE = 60;
|
|
5
|
+
/*
|
|
6
|
+
* One chat per chat id on one host. A chat opens on mount and detaches on unmount, and every mounted
|
|
7
|
+
* chat is attached again when the transport comes back. The provider list is fetched once per
|
|
8
|
+
* connection and handed to its own store. The transport it is given never changes hosts, so a
|
|
9
|
+
* reattach stays on the host these chats run on.
|
|
10
|
+
*/
|
|
11
|
+
export class ChatClient {
|
|
12
|
+
transport;
|
|
13
|
+
sink;
|
|
14
|
+
providers;
|
|
15
|
+
mounted = new MountedRegistry();
|
|
16
|
+
unsubscribe = [];
|
|
17
|
+
earlier = new Map();
|
|
18
|
+
preferences = null;
|
|
19
|
+
constructor(transport, sink, providers = null) {
|
|
20
|
+
this.transport = transport;
|
|
21
|
+
this.sink = sink;
|
|
22
|
+
this.providers = providers;
|
|
23
|
+
this.unsubscribe.push(transport.on('chat.event', ({ chatId, event, seq }) => {
|
|
24
|
+
this.sink.apply(chatId, event);
|
|
25
|
+
const entry = this.mounted.get(chatId);
|
|
26
|
+
if (entry && seq !== undefined) {
|
|
27
|
+
entry.seq = seq;
|
|
28
|
+
}
|
|
29
|
+
}),
|
|
30
|
+
// Every chat on the machine, attached or not, so a node waiting on a person says so on a view nobody has open.
|
|
31
|
+
transport.on('chat.status', ({ chatId, info }) => this.sink.status(chatId, info)), transport.on('chat.bookmarks', ({ chatId, bookmarks }) => this.sink.bookmarks(chatId, bookmarks)), transport.subscribeStatus((status) => this.onStatus(status)));
|
|
32
|
+
if (transport.status === 'open') {
|
|
33
|
+
void this.loadProviders();
|
|
34
|
+
void this.loadStatuses();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
inspections = new Map();
|
|
38
|
+
inspect(chatId) {
|
|
39
|
+
const pending = this.inspections.get(chatId);
|
|
40
|
+
if (pending) {
|
|
41
|
+
return pending;
|
|
42
|
+
}
|
|
43
|
+
const read = this.transport.request('chat.attach', { chatId, historyLimit: 100 }).finally(async () => {
|
|
44
|
+
this.inspections.delete(chatId);
|
|
45
|
+
// A visible chat owns its attachment; an inspection must never release it.
|
|
46
|
+
if (!this.mounted.get(chatId)) {
|
|
47
|
+
await this.transport.request('chat.detach', { chatId }).catch(() => undefined);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
this.inspections.set(chatId, read);
|
|
51
|
+
return read;
|
|
52
|
+
}
|
|
53
|
+
/* Answers false when the transport is not connected; the chat opens once it is. */
|
|
54
|
+
async open(chatId, options) {
|
|
55
|
+
this.mounted.set(chatId, { ...options, attached: false });
|
|
56
|
+
try {
|
|
57
|
+
await this.attach(chatId);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
if (isConnectionError(e)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
throw e;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/*
|
|
68
|
+
* Points a chat that has not spoken yet at another CLI. The host fixes a chat's provider when
|
|
69
|
+
* it registers the chat, so the only way over is to drop the empty one and register it again.
|
|
70
|
+
* The row stays until the attach of the new one replaces it, so a view keeps what it drew.
|
|
71
|
+
*/
|
|
72
|
+
async retarget(chatId, provider, selection) {
|
|
73
|
+
const entry = this.mounted.get(chatId);
|
|
74
|
+
if (!entry) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// The account belonged to the CLI the chat leaves, and the seq to its stream: the new chat's
|
|
78
|
+
// stream starts over, so offering it the old seq would read as nothing missed and reset nothing.
|
|
79
|
+
const { attached: _attached, account: _account, seq: _seq, ...options } = entry;
|
|
80
|
+
this.mounted.delete(chatId);
|
|
81
|
+
await this.transport.request('chat.kill', { chatId });
|
|
82
|
+
try {
|
|
83
|
+
await this.open(chatId, { ...options, provider, selection });
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
this.sink.forget(chatId);
|
|
87
|
+
throw e;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/*
|
|
91
|
+
* Tells the host what a chat it starts on its own is made with while this socket is connected,
|
|
92
|
+
* since no client mounting that chat sends a composer preference. Said again on every fresh socket.
|
|
93
|
+
*/
|
|
94
|
+
setPreferences(preferences) {
|
|
95
|
+
this.preferences = preferences;
|
|
96
|
+
this.sendPreferences();
|
|
97
|
+
}
|
|
98
|
+
async detach(chatId) {
|
|
99
|
+
const entry = this.mounted.get(chatId);
|
|
100
|
+
this.mounted.delete(chatId);
|
|
101
|
+
if (!entry?.attached) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
await this.transport.request('chat.detach', { chatId });
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// The socket closing detaches every chat server-side anyway.
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/* Current hosts return the stable turn id; older compatible ones only report whether the message queued. */
|
|
112
|
+
async send(chatId, text, extras = {}) {
|
|
113
|
+
return this.transport.request('chat.send', {
|
|
114
|
+
chatId,
|
|
115
|
+
text,
|
|
116
|
+
mentions: extras.mentions,
|
|
117
|
+
skills: extras.skills,
|
|
118
|
+
chats: extras.chats,
|
|
119
|
+
attachments: extras.attachments
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/* Marks a message; the list every client of the chat holds comes back as `chat.bookmarks`. */
|
|
123
|
+
async addBookmark(chatId, itemId, name) {
|
|
124
|
+
const { bookmarks } = await this.transport.request('chat.addBookmark', { chatId, itemId, ...(name === undefined ? {} : { name }) });
|
|
125
|
+
return bookmarks;
|
|
126
|
+
}
|
|
127
|
+
/* An empty name takes the name away. */
|
|
128
|
+
async renameBookmark(chatId, itemId, name) {
|
|
129
|
+
const { bookmarks } = await this.transport.request('chat.renameBookmark', { chatId, itemId, name });
|
|
130
|
+
return bookmarks;
|
|
131
|
+
}
|
|
132
|
+
async removeBookmark(chatId, itemId) {
|
|
133
|
+
const { bookmarks } = await this.transport.request('chat.removeBookmark', { chatId, itemId });
|
|
134
|
+
return bookmarks;
|
|
135
|
+
}
|
|
136
|
+
/* What this chat's CLI would run as a skill, for the composer's `$` picker. */
|
|
137
|
+
async listSkills(chatId) {
|
|
138
|
+
const { skills } = await this.transport.request('skills.list', { chatId });
|
|
139
|
+
return skills;
|
|
140
|
+
}
|
|
141
|
+
/* Starts the chat over; refused with `chat-busy` while a turn runs, unless `force` stops that turn. */
|
|
142
|
+
async clear(chatId, force = false) {
|
|
143
|
+
await this.transport.request('chat.clear', { chatId, ...(force ? { force: true } : {}) });
|
|
144
|
+
}
|
|
145
|
+
async kill(chatId) {
|
|
146
|
+
this.mounted.delete(chatId);
|
|
147
|
+
this.sink.forget(chatId);
|
|
148
|
+
await this.transport.request('chat.kill', { chatId });
|
|
149
|
+
}
|
|
150
|
+
/*
|
|
151
|
+
* What every chat on this machine is doing right now. `chat.status` only carries a change, so a
|
|
152
|
+
* window that just opened or came back from a lost socket asks for the standing answer.
|
|
153
|
+
*/
|
|
154
|
+
async loadStatuses() {
|
|
155
|
+
try {
|
|
156
|
+
const { chats } = await this.transport.request('chat.list', {});
|
|
157
|
+
for (const info of chats) {
|
|
158
|
+
this.sink.status(info.chatId, info);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// The next status event says it instead; an older host never answers this at all.
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async loadProviders() {
|
|
166
|
+
if (!this.providers) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const { providers } = await this.transport.request('provider.list', {});
|
|
171
|
+
this.providers.setProviders(providers);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// The list comes with the next connection; pickers show what they had.
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/*
|
|
178
|
+
* The page before the oldest one this chat holds, one request per chat at a time. A cursor the
|
|
179
|
+
* host no longer knows means the thread changed under it, so the chat is read again from its end.
|
|
180
|
+
*/
|
|
181
|
+
loadEarlier(chatId, cursor) {
|
|
182
|
+
const running = this.earlier.get(chatId);
|
|
183
|
+
if (running) {
|
|
184
|
+
return running;
|
|
185
|
+
}
|
|
186
|
+
const read = this.transport
|
|
187
|
+
.request('chat.history', { chatId, cursor, limit: HISTORY_PAGE })
|
|
188
|
+
.then((page) => this.sink.prepend(chatId, cursor, page))
|
|
189
|
+
.catch(async (e) => {
|
|
190
|
+
const entry = this.mounted.get(chatId);
|
|
191
|
+
if (errorCode(e) !== 'history-expired' || !entry) {
|
|
192
|
+
throw e;
|
|
193
|
+
}
|
|
194
|
+
delete entry.seq;
|
|
195
|
+
await this.attach(chatId);
|
|
196
|
+
})
|
|
197
|
+
.finally(() => this.earlier.delete(chatId));
|
|
198
|
+
this.earlier.set(chatId, read);
|
|
199
|
+
return read;
|
|
200
|
+
}
|
|
201
|
+
isMounted(chatId) {
|
|
202
|
+
return this.mounted.has(chatId);
|
|
203
|
+
}
|
|
204
|
+
/* Lets go of the machine; the chats keep running there, the host only stops streaming them here. */
|
|
205
|
+
dispose() {
|
|
206
|
+
for (const off of this.unsubscribe) {
|
|
207
|
+
off();
|
|
208
|
+
}
|
|
209
|
+
this.unsubscribe.length = 0;
|
|
210
|
+
for (const chatId of [...this.mounted.keys()]) {
|
|
211
|
+
void this.detach(chatId);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/*
|
|
215
|
+
* A reattach offers the last seq the store holds. The host answers only what came after it when
|
|
216
|
+
* it still has all of that, and the newest page of the thread otherwise, so a short drop costs a
|
|
217
|
+
* few events.
|
|
218
|
+
*/
|
|
219
|
+
async attach(chatId) {
|
|
220
|
+
const entry = this.mounted.get(chatId);
|
|
221
|
+
if (!entry) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
await this.transport.request('chat.create', {
|
|
225
|
+
chatId,
|
|
226
|
+
provider: entry.provider,
|
|
227
|
+
account: entry.account,
|
|
228
|
+
cwd: entry.cwd,
|
|
229
|
+
resume: entry.resume,
|
|
230
|
+
selection: entry.selection,
|
|
231
|
+
runtimeMode: entry.runtimeMode
|
|
232
|
+
});
|
|
233
|
+
// Closed or opened again in the meantime: an opening that is still there attaches itself.
|
|
234
|
+
if (this.mounted.get(chatId) !== entry) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const since = entry.seq;
|
|
238
|
+
const result = await this.transport.request('chat.attach', { chatId, historyLimit: HISTORY_PAGE, ...(since === undefined ? {} : { since }) });
|
|
239
|
+
const current = this.mounted.get(chatId);
|
|
240
|
+
if (current !== entry) {
|
|
241
|
+
// Its detach found nothing attached yet, and the host would stream this chat here until the socket closes.
|
|
242
|
+
if (!current) {
|
|
243
|
+
await this.transport.request('chat.detach', { chatId }).catch(() => undefined);
|
|
244
|
+
}
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
entry.attached = true;
|
|
248
|
+
if (result.seq !== undefined) {
|
|
249
|
+
entry.seq = result.seq;
|
|
250
|
+
}
|
|
251
|
+
if (result.events && since !== undefined) {
|
|
252
|
+
for (const event of result.events) {
|
|
253
|
+
this.sink.apply(chatId, event);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
const page = result.history === undefined ? undefined : { history: result.history, pending: result.pending ?? [] };
|
|
258
|
+
this.sink.reset(chatId, result.info, result.items, page);
|
|
259
|
+
}
|
|
260
|
+
// A host without bookmarks sends none, and then this chat has none.
|
|
261
|
+
this.sink.bookmarks(chatId, result.bookmarks ?? []);
|
|
262
|
+
}
|
|
263
|
+
sendPreferences() {
|
|
264
|
+
if (this.preferences === null) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
void this.transport.request('chat.setPreferences', this.preferences).catch(() => undefined);
|
|
268
|
+
}
|
|
269
|
+
onStatus(status) {
|
|
270
|
+
if (status === 'open') {
|
|
271
|
+
this.sendPreferences();
|
|
272
|
+
void this.loadProviders();
|
|
273
|
+
void this.loadStatuses();
|
|
274
|
+
void this.mounted.reattachAll((chatId) => this.attach(chatId).then(() => undefined));
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
this.mounted.detachAll();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/* The chats an `@query` offers: never the chat typing it or one already attached. */
|
|
2
|
+
export function chatSuggestions(chats, query, exclude, limit) {
|
|
3
|
+
const needle = query.toLowerCase();
|
|
4
|
+
return chats.filter((chat) => !exclude.includes(chat.id) && chat.title.toLowerCase().includes(needle)).slice(0, limit);
|
|
5
|
+
}
|