@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,65 @@
|
|
|
1
|
+
import type { TimelineRow } from './timeline';
|
|
2
|
+
import { stripAnsi } from './ansi';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* What a row of the thread puts on the clipboard. An answer is markdown on the wire and rendered
|
|
6
|
+
* text on the screen, so copying it as text has to undo the marks the renderer swallowed; the
|
|
7
|
+
* source itself stays available as "Copy as markdown".
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* The marks that only exist to be rendered come off, what they marked stays. `_` is left alone on
|
|
12
|
+
* purpose: it is a letter in half the identifiers an agent writes, and no italic is worth mangling
|
|
13
|
+
* `file_path`.
|
|
14
|
+
*/
|
|
15
|
+
export function stripMarkdown(text: string): string {
|
|
16
|
+
return text
|
|
17
|
+
.replace(/^ *```[^\n]*\n?/gm, '')
|
|
18
|
+
.replace(/^ *(#{1,6}) +/gm, '')
|
|
19
|
+
.replace(/^ *> ?/gm, '')
|
|
20
|
+
.replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
21
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
22
|
+
.replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, '$2')
|
|
23
|
+
.replace(/\*(?=\S)([^*\n]*\S)\*/g, '$1')
|
|
24
|
+
.replace(/`+([^`]+)`+/g, '$1')
|
|
25
|
+
.trim();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* What a tool call has to show for itself: what it answered, or what it has answered so far. */
|
|
29
|
+
function outputOf(tool: { output: string | null; progress?: { output: string | null } }): string | null {
|
|
30
|
+
const output = tool.output ?? tool.progress?.output ?? null;
|
|
31
|
+
return output === null ? null : stripAnsi(output);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* The whole item under the pointer as plain text, or null for a row that carries no message. */
|
|
35
|
+
export function messageTextOf(row: TimelineRow): string | null {
|
|
36
|
+
switch (row.kind) {
|
|
37
|
+
case 'user':
|
|
38
|
+
return row.item.text || null;
|
|
39
|
+
case 'assistant':
|
|
40
|
+
return stripMarkdown(row.item.text) || null;
|
|
41
|
+
case 'report':
|
|
42
|
+
return stripMarkdown(row.text) || null;
|
|
43
|
+
case 'thinking':
|
|
44
|
+
return row.item.text || null;
|
|
45
|
+
case 'work':
|
|
46
|
+
case 'work-live':
|
|
47
|
+
case 'workflow':
|
|
48
|
+
return outputOf(row.tool);
|
|
49
|
+
case 'work-group': {
|
|
50
|
+
const outputs = row.tools.map(outputOf).filter((output): output is string => output !== null);
|
|
51
|
+
return outputs.length > 0 ? outputs.join('\n\n') : null;
|
|
52
|
+
}
|
|
53
|
+
case 'subagent':
|
|
54
|
+
return row.item.result ?? row.item.summary ?? null;
|
|
55
|
+
case 'note':
|
|
56
|
+
return row.text;
|
|
57
|
+
default:
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* The markdown an answer was written in; every other row has none to copy. */
|
|
63
|
+
export function markdownOf(row: TimelineRow): string | null {
|
|
64
|
+
return row.kind === 'assistant' ? row.item.text || null : row.kind === 'report' ? row.text || null : null;
|
|
65
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ChatItem } from '@adecore/agent-contracts';
|
|
2
|
+
import type { TimelineRow } from './timeline';
|
|
3
|
+
import { selectionWithin } from '@adecore/ui';
|
|
4
|
+
|
|
5
|
+
/* What the right-click landed on, read once when the menu opens. */
|
|
6
|
+
export interface TimelineTarget {
|
|
7
|
+
/* The text selected inside the thread; a selection elsewhere is none of this menu's business. */
|
|
8
|
+
selection: string;
|
|
9
|
+
row: TimelineRow | null;
|
|
10
|
+
/* The code block under the pointer, when the click landed in one. */
|
|
11
|
+
code: string | null;
|
|
12
|
+
/* The file a mention chip, a changed-files row or a link in an answer names. */
|
|
13
|
+
path: string | null;
|
|
14
|
+
/* The line that file reference named, where it named one. */
|
|
15
|
+
line: number | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const EMPTY_TARGET: TimelineTarget = { selection: '', row: null, code: null, path: null, line: null };
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* The rows with the text a reply or a thought holds now. Rows are derived from the structure of a
|
|
22
|
+
* thread, which a delta leaves alone, so a row still carries the text it was derived with.
|
|
23
|
+
*/
|
|
24
|
+
export function withCurrentText(rows: TimelineRow[], items: Record<string, ChatItem> | undefined): TimelineRow[] {
|
|
25
|
+
if (!items) {
|
|
26
|
+
return rows;
|
|
27
|
+
}
|
|
28
|
+
return rows.map((row) => {
|
|
29
|
+
const item = items[row.id];
|
|
30
|
+
if (row.kind === 'assistant' && item?.kind === 'assistant') {
|
|
31
|
+
return { ...row, item };
|
|
32
|
+
}
|
|
33
|
+
if (row.kind === 'thinking' && item?.kind === 'thinking') {
|
|
34
|
+
return { ...row, item };
|
|
35
|
+
}
|
|
36
|
+
return row;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* The row a click landed in is the one whose id it sits under; the rest comes from the same walk up. */
|
|
41
|
+
export function readTimelineTarget(element: HTMLElement, scroller: HTMLElement | null, rows: TimelineRow[]): TimelineTarget {
|
|
42
|
+
const id = element.closest<HTMLElement>('[data-item-id]')?.dataset.itemId;
|
|
43
|
+
const file = element.closest<HTMLElement>('[data-file-path]');
|
|
44
|
+
const line = Number(file?.dataset.fileLine);
|
|
45
|
+
return {
|
|
46
|
+
selection: selectionWithin(scroller),
|
|
47
|
+
row: rows.find((row) => row.id === id) ?? null,
|
|
48
|
+
code: element.closest('pre')?.textContent ?? null,
|
|
49
|
+
path: file?.dataset.filePath ?? null,
|
|
50
|
+
line: Number.isFinite(line) && line > 0 ? line : null
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import i18next from 'i18next';
|
|
2
|
+
import type {
|
|
3
|
+
ChatApprovalItem,
|
|
4
|
+
ChatAssistantItem,
|
|
5
|
+
ChatCheckpointDiff,
|
|
6
|
+
ChatItem,
|
|
7
|
+
ChatQuestionItem,
|
|
8
|
+
ChatSubagentItem,
|
|
9
|
+
ChatThinkingItem,
|
|
10
|
+
ChatToolItem,
|
|
11
|
+
ChatTurnItem,
|
|
12
|
+
ChatUserItem,
|
|
13
|
+
ChatWorkflow
|
|
14
|
+
} from '@adecore/agent-contracts';
|
|
15
|
+
import { abortedByMachine } from '@adecore/agent-contracts';
|
|
16
|
+
import type { ThreadCard } from '../../host';
|
|
17
|
+
import { formatElapsedShort } from '@adecore/ui/format';
|
|
18
|
+
import { handbackReportOf } from './handback';
|
|
19
|
+
import { toolEntry } from './tool-catalog';
|
|
20
|
+
import { hasFileChanges, isFileChange } from './tools';
|
|
21
|
+
|
|
22
|
+
/* A subagent's row: the work the thread kept of it, and the agents it opened in turn, each a row of its own under it. */
|
|
23
|
+
export interface SubagentBranch {
|
|
24
|
+
id: string;
|
|
25
|
+
item: ChatSubagentItem;
|
|
26
|
+
children: ChatItem[];
|
|
27
|
+
nested: SubagentBranch[];
|
|
28
|
+
expanded: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
* What the thread shows, row by row. Items are what the host knows; rows are what a reader
|
|
33
|
+
* wants: one line per tool call, runs of tool calls folded into a summary, past turns folded
|
|
34
|
+
* behind "Worked for 12s" and what the turn did, and the file changes of a turn gathered into one card.
|
|
35
|
+
*/
|
|
36
|
+
export type TimelineRow =
|
|
37
|
+
| { kind: 'user'; id: string; item: ChatUserItem }
|
|
38
|
+
| { kind: 'turn-start'; id: string; turn: ChatTurnItem; label: string }
|
|
39
|
+
| { kind: 'assistant'; id: string; item: ChatAssistantItem }
|
|
40
|
+
// A subagent's `SubagentHandback` call, drawn as the report it carries rather than as a tool call.
|
|
41
|
+
| { kind: 'report'; id: string; text: string }
|
|
42
|
+
| { kind: 'thinking'; id: string; item: ChatThinkingItem }
|
|
43
|
+
| { kind: 'work'; id: string; tool: ChatToolItem }
|
|
44
|
+
| { kind: 'work-group'; id: string; tools: ChatToolItem[]; summary: string; expanded: boolean }
|
|
45
|
+
| { kind: 'work-live'; id: string; tool: ChatToolItem }
|
|
46
|
+
// A Workflow call the machine reports phases and agents for, drawn with them under it.
|
|
47
|
+
| { kind: 'workflow'; id: string; tool: ChatToolItem; workflow: ChatWorkflow }
|
|
48
|
+
| ({ kind: 'subagent' } & SubagentBranch)
|
|
49
|
+
| { kind: 'approval'; id: string; item: ChatApprovalItem }
|
|
50
|
+
| { kind: 'question'; id: string; item: ChatQuestionItem }
|
|
51
|
+
| { kind: 'note'; id: string; level: 'info' | 'warning' | 'error'; text: string; from?: string }
|
|
52
|
+
| { kind: 'compaction'; id: string; preTokens: number | null }
|
|
53
|
+
| { kind: 'changed-files'; id: string; turnId: string; tools: ChatToolItem[]; diff: ChatCheckpointDiff | null; checkpoint: boolean }
|
|
54
|
+
| { kind: 'turn-fold'; id: string; turn: ChatTurnItem; label: string; work: string[]; hiddenCount: number; expanded: boolean }
|
|
55
|
+
| { kind: 'forks'; id: string; turnId: string }
|
|
56
|
+
| { kind: 'working'; id: string; startedAt: number }
|
|
57
|
+
// A card of the app's own (`thread-cards.ts`), never derived from an item.
|
|
58
|
+
| { kind: 'app-card'; id: string; card: ThreadCard };
|
|
59
|
+
|
|
60
|
+
interface TimelineOptions {
|
|
61
|
+
expandedGroups: ReadonlySet<string>;
|
|
62
|
+
expandedTurns: ReadonlySet<string>;
|
|
63
|
+
expandedSubagents: ReadonlySet<string>;
|
|
64
|
+
activeTurnId: string | null;
|
|
65
|
+
/* The settled turns a fork went on after; a thread that cannot fork leaves it out. */
|
|
66
|
+
forkedTurns?: ReadonlySet<string>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
* A turn runs in two rhythms. The tool lines are a list and read as one when they sit tight
|
|
71
|
+
* against each other; prose and cards are blocks and need room around them. Where the two meet,
|
|
72
|
+
* the block gap marks the seam, so an answer never looks glued to the call above it.
|
|
73
|
+
*/
|
|
74
|
+
const BLOCK_KINDS = new Set<TimelineRow['kind']>(['assistant', 'report', 'thinking', 'changed-files', 'compaction', 'app-card']);
|
|
75
|
+
|
|
76
|
+
export function isBlock(row: TimelineRow): boolean {
|
|
77
|
+
return BLOCK_KINDS.has(row.kind);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* "Read 4 files", "Ran 2 commands", or "12 tool calls" when the run mixes kinds. */
|
|
81
|
+
export function summarizeGroup(tools: ChatToolItem[]): string {
|
|
82
|
+
const names = new Set(tools.map((tool) => tool.name));
|
|
83
|
+
const only = names.size === 1 ? tools[0]!.name : null;
|
|
84
|
+
if (only !== null && toolEntry(only)?.grouped === true) {
|
|
85
|
+
return i18next.t(`agent-chat:group.tools.${only}`, { count: tools.length });
|
|
86
|
+
}
|
|
87
|
+
/* A run that only touches files says so once, whether it took one tool or three, and counts the
|
|
88
|
+
files rather than the calls: three edits to one file are one file edited. */
|
|
89
|
+
if (tools.every((tool) => isFileChange(tool.name))) {
|
|
90
|
+
const paths = new Set(tools.map((tool) => (tool.input as { file_path?: string })?.file_path ?? tool.id));
|
|
91
|
+
return i18next.t('agent-chat:group.edited', { count: paths.size });
|
|
92
|
+
}
|
|
93
|
+
if (only !== null) {
|
|
94
|
+
return i18next.t('agent-chat:group.calls', { name: only, count: tools.length });
|
|
95
|
+
}
|
|
96
|
+
return i18next.t('agent-chat:group.toolCalls', { count: tools.length });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
* What a folded turn did, a sentence per kind of call in the order it first came: "Read 11 files",
|
|
101
|
+
* "Searched 2 patterns". Every file change reads as one sentence, and calls without a sentence of
|
|
102
|
+
* their own are counted together at the end.
|
|
103
|
+
*/
|
|
104
|
+
export function summarizeTurn(tools: readonly ChatToolItem[]): string[] {
|
|
105
|
+
// Keyed by the sentence, so its place is where the first call of that kind came.
|
|
106
|
+
const counts = new Map<string, number>();
|
|
107
|
+
const edited = new Set<string>();
|
|
108
|
+
let other = 0;
|
|
109
|
+
for (const tool of tools) {
|
|
110
|
+
if (isFileChange(tool.name)) {
|
|
111
|
+
edited.add((tool.input as { file_path?: string })?.file_path ?? tool.id);
|
|
112
|
+
counts.set('agent-chat:group.edited', edited.size);
|
|
113
|
+
} else if (toolEntry(tool.name)?.grouped === true) {
|
|
114
|
+
const key = `agent-chat:group.tools.${tool.name}`;
|
|
115
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
116
|
+
} else {
|
|
117
|
+
other += 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const parts = [...counts].map(([key, count]) => i18next.t(key, { count }));
|
|
121
|
+
if (other > 0) {
|
|
122
|
+
parts.push(i18next.t(parts.length === 0 ? 'agent-chat:group.toolCalls' : 'agent-chat:group.otherCalls', { count: other }));
|
|
123
|
+
}
|
|
124
|
+
return parts;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
* What a turn nobody asked for is about. The CLI wakes the agent when a background subagent settles
|
|
129
|
+
* and hands over its summary; without one all that is known is that the agent went on by itself.
|
|
130
|
+
*/
|
|
131
|
+
export function agentTurnLabel(turn: ChatTurnItem): string {
|
|
132
|
+
const about = (what: string): string => (turn.label ? i18next.t('agent-chat:turn.about', { what, label: turn.label }) : what);
|
|
133
|
+
// A turn the machine opened with the results of tasks this chat gave; the label is their titles.
|
|
134
|
+
if (turn.taskIds !== undefined && turn.taskIds.length > 0) {
|
|
135
|
+
return about(i18next.t('agent-chat:turn.wokenByTask', { count: turn.taskIds.length }));
|
|
136
|
+
}
|
|
137
|
+
// A turn the machine opened over a message another node sent; the label names who sent it.
|
|
138
|
+
if (turn.messageFrom !== undefined && turn.messageFrom.length > 0) {
|
|
139
|
+
return about(i18next.t('agent-chat:turn.wokenByMessage', { count: turn.messageFrom.length }));
|
|
140
|
+
}
|
|
141
|
+
return turn.label ? i18next.t('agent-chat:turn.subagentFinished', { label: turn.label }) : i18next.t('agent-chat:turn.continued');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* The items of the turn tell a turn a person stopped from one the machine ended after a restart. */
|
|
145
|
+
export function turnLabel(turn: ChatTurnItem, items: readonly ChatItem[] = []): string {
|
|
146
|
+
const duration = formatElapsedShort((turn.endedAt ?? turn.createdAt) - turn.createdAt);
|
|
147
|
+
switch (turn.state) {
|
|
148
|
+
case 'aborted':
|
|
149
|
+
return abortedByMachine(turn, items) ? i18next.t('agent-chat:turn.stopped', { duration }) : i18next.t('agent-chat:turn.youStopped', { duration });
|
|
150
|
+
case 'error':
|
|
151
|
+
return i18next.t('agent-chat:turn.failed', { duration });
|
|
152
|
+
default:
|
|
153
|
+
return i18next.t('agent-chat:turn.worked', { duration });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// A subagent's own work, and an agent it opened, belong to its row, not to the thread; an old record has no row for it.
|
|
158
|
+
function parentOf(item: ChatItem): string | null {
|
|
159
|
+
return item.kind === 'tool' || item.kind === 'assistant' || item.kind === 'subagent' ? (item.parentToolUseId ?? null) : null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Deeper than the CLI forwards any call; it also ends a chain of rows that would name each other.
|
|
163
|
+
const MAX_NESTING = 8;
|
|
164
|
+
|
|
165
|
+
function branchOf(item: ChatSubagentItem, options: TimelineOptions, children: Map<string, ChatItem[]>, depth = 0): SubagentBranch {
|
|
166
|
+
const own = children.get(item.toolUseId) ?? [];
|
|
167
|
+
return {
|
|
168
|
+
id: item.id,
|
|
169
|
+
item,
|
|
170
|
+
children: own.filter((child) => child.kind !== 'subagent'),
|
|
171
|
+
nested:
|
|
172
|
+
depth >= MAX_NESTING
|
|
173
|
+
? []
|
|
174
|
+
: own.filter((child): child is ChatSubagentItem => child.kind === 'subagent').map((child) => branchOf(child, options, children, depth + 1)),
|
|
175
|
+
expanded: options.expandedSubagents.has(item.id)
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* The branch a subagent's row sits in, its own or one it hangs under, anywhere in the thread's rows. */
|
|
180
|
+
export function findSubagentBranch(rows: readonly TimelineRow[], toolUseId: string): { index: number; branch: SubagentBranch } | null {
|
|
181
|
+
const search = (branch: SubagentBranch): SubagentBranch | null => {
|
|
182
|
+
if (branch.item.toolUseId === toolUseId) {
|
|
183
|
+
return branch;
|
|
184
|
+
}
|
|
185
|
+
for (const child of branch.nested) {
|
|
186
|
+
const found = search(child);
|
|
187
|
+
if (found) {
|
|
188
|
+
return found;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
};
|
|
193
|
+
for (let index = 0; index < rows.length; index++) {
|
|
194
|
+
const row = rows[index]!;
|
|
195
|
+
const branch = row.kind === 'subagent' ? search(row) : null;
|
|
196
|
+
if (branch) {
|
|
197
|
+
return { index, branch };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* What each subagent did, keyed by the call that spawned it, in the order it happened. */
|
|
204
|
+
function groupChildren(items: ChatItem[]): Map<string, ChatItem[]> {
|
|
205
|
+
const children = new Map<string, ChatItem[]>();
|
|
206
|
+
for (const item of items) {
|
|
207
|
+
const parent = parentOf(item);
|
|
208
|
+
if (parent === null) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const bucket = children.get(parent);
|
|
212
|
+
if (bucket) {
|
|
213
|
+
bucket.push(item);
|
|
214
|
+
} else {
|
|
215
|
+
children.set(parent, [item]);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return children;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function flushTools(buffer: ChatToolItem[], rows: TimelineRow[], options: TimelineOptions): void {
|
|
222
|
+
if (buffer.length === 0) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const tools = buffer.splice(0);
|
|
226
|
+
const live = tools.filter((tool) => tool.state === 'running');
|
|
227
|
+
const settled = tools.filter((tool) => tool.state !== 'running');
|
|
228
|
+
if (settled.length === 1) {
|
|
229
|
+
rows.push({ kind: 'work', id: settled[0]!.id, tool: settled[0]! });
|
|
230
|
+
} else if (settled.length > 1) {
|
|
231
|
+
const id = `group-${settled[0]!.id}`;
|
|
232
|
+
const expanded = options.expandedGroups.has(id);
|
|
233
|
+
rows.push({ kind: 'work-group', id, tools: settled, summary: summarizeGroup(settled), expanded });
|
|
234
|
+
if (expanded) {
|
|
235
|
+
for (const tool of settled) {
|
|
236
|
+
rows.push({ kind: 'work', id: tool.id, tool });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
for (const tool of live) {
|
|
241
|
+
rows.push({ kind: 'work-live', id: tool.id, tool });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Rows for a run of items, in order, with tool runs folded and a subagent's work under its own row. */
|
|
246
|
+
function rowsForItems(items: ChatItem[], options: TimelineOptions, children: Map<string, ChatItem[]>): TimelineRow[] {
|
|
247
|
+
const rows: TimelineRow[] = [];
|
|
248
|
+
const tools: ChatToolItem[] = [];
|
|
249
|
+
for (const item of items) {
|
|
250
|
+
if (parentOf(item) !== null) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
const report = handbackReportOf(item);
|
|
254
|
+
if (item.kind === 'tool' && item.workflow !== undefined) {
|
|
255
|
+
flushTools(tools, rows, options);
|
|
256
|
+
rows.push({ kind: 'workflow', id: item.id, tool: item, workflow: item.workflow });
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (item.kind === 'tool' && report === null) {
|
|
260
|
+
tools.push(item);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (report !== null) {
|
|
264
|
+
flushTools(tools, rows, options);
|
|
265
|
+
rows.push({ kind: 'report', id: item.id, text: report });
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
flushTools(tools, rows, options);
|
|
269
|
+
switch (item.kind) {
|
|
270
|
+
case 'subagent':
|
|
271
|
+
rows.push({ kind: 'subagent', ...branchOf(item, options, children) });
|
|
272
|
+
break;
|
|
273
|
+
case 'user':
|
|
274
|
+
rows.push({ kind: 'user', id: item.id, item });
|
|
275
|
+
break;
|
|
276
|
+
case 'assistant':
|
|
277
|
+
if (item.text !== '' || item.streaming) {
|
|
278
|
+
rows.push({ kind: 'assistant', id: item.id, item });
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
case 'thinking':
|
|
282
|
+
rows.push({ kind: 'thinking', id: item.id, item });
|
|
283
|
+
break;
|
|
284
|
+
case 'approval':
|
|
285
|
+
// A pending one sits on the composer; only its outcome belongs in the transcript.
|
|
286
|
+
if (item.decision !== 'pending') {
|
|
287
|
+
rows.push({ kind: 'approval', id: item.id, item });
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
case 'question':
|
|
291
|
+
if (item.state !== 'pending') {
|
|
292
|
+
rows.push({ kind: 'question', id: item.id, item });
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
case 'note':
|
|
296
|
+
rows.push({ kind: 'note', id: item.id, level: item.level, text: item.text, ...(item.from === undefined ? {} : { from: item.from }) });
|
|
297
|
+
break;
|
|
298
|
+
case 'compaction':
|
|
299
|
+
rows.push({ kind: 'compaction', id: item.id, preTokens: item.preTokens });
|
|
300
|
+
break;
|
|
301
|
+
case 'turn':
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
flushTools(tools, rows, options);
|
|
306
|
+
return rows;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/* The card of a settled turn: the checkpoint diff when the host took one, else what the tool calls carry. */
|
|
310
|
+
function changedFilesRow(turn: ChatTurnItem, items: ChatItem[]): TimelineRow | null {
|
|
311
|
+
const edits = items.filter(
|
|
312
|
+
(item): item is ChatToolItem => item.kind === 'tool' && item.state === 'done' && isFileChange(item.name) && hasFileChanges(item)
|
|
313
|
+
);
|
|
314
|
+
const diff = turn.checkpointDiff ?? null;
|
|
315
|
+
const empty = diff === null ? edits.length === 0 : diff.files.length === 0;
|
|
316
|
+
if (empty) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
return { kind: 'changed-files', id: `files-${turn.id}`, turnId: turn.id, tools: edits, diff, checkpoint: turn.checkpoint !== undefined };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function lastAssistantRow(rows: TimelineRow[]): TimelineRow | null {
|
|
323
|
+
for (let i = rows.length - 1; i >= 0; i--) {
|
|
324
|
+
const row = rows[i]!;
|
|
325
|
+
if (row.kind === 'assistant') {
|
|
326
|
+
return row;
|
|
327
|
+
}
|
|
328
|
+
if (row.kind !== 'note') {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* The whole thread as rows; items without a turn (older records) are shown as they are. */
|
|
336
|
+
export function deriveTimelineRows(items: ChatItem[], options: TimelineOptions): TimelineRow[] {
|
|
337
|
+
const rows: TimelineRow[] = [];
|
|
338
|
+
const children = groupChildren(items);
|
|
339
|
+
// Items are grouped by turn, in order of first appearance; runs of turnless items keep their place.
|
|
340
|
+
const chunks: Array<{ turnId: string | null; items: ChatItem[] }> = [];
|
|
341
|
+
const byTurn = new Map<string, ChatItem[]>();
|
|
342
|
+
for (const item of items) {
|
|
343
|
+
if (item.turnId === null) {
|
|
344
|
+
const last = chunks[chunks.length - 1];
|
|
345
|
+
if (last && last.turnId === null) {
|
|
346
|
+
last.items.push(item);
|
|
347
|
+
} else {
|
|
348
|
+
chunks.push({ turnId: null, items: [item] });
|
|
349
|
+
}
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
let bucket = byTurn.get(item.turnId);
|
|
353
|
+
if (!bucket) {
|
|
354
|
+
bucket = [];
|
|
355
|
+
byTurn.set(item.turnId, bucket);
|
|
356
|
+
chunks.push({ turnId: item.turnId, items: bucket });
|
|
357
|
+
}
|
|
358
|
+
bucket.push(item);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
for (const chunk of chunks) {
|
|
362
|
+
if (chunk.turnId === null) {
|
|
363
|
+
rows.push(...rowsForItems(chunk.items, options, children));
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
const turnId = chunk.turnId;
|
|
367
|
+
const turn = chunk.items.find((item): item is ChatTurnItem => item.kind === 'turn');
|
|
368
|
+
const user = chunk.items.filter((item) => item.kind === 'user');
|
|
369
|
+
const rest = chunk.items.filter((item) => item.kind !== 'user' && item.kind !== 'turn');
|
|
370
|
+
if (turn?.origin === 'agent') {
|
|
371
|
+
// The agent started this one itself, so there is no message of the person to show above it.
|
|
372
|
+
rows.push({ kind: 'turn-start', id: `start-${turnId}`, turn, label: agentTurnLabel(turn) });
|
|
373
|
+
}
|
|
374
|
+
rows.push(...rowsForItems(user, options, children));
|
|
375
|
+
const active = turnId === options.activeTurnId || turn?.state === 'running';
|
|
376
|
+
const work = rowsForItems(rest, options, children);
|
|
377
|
+
if (!turn || active) {
|
|
378
|
+
rows.push(...work);
|
|
379
|
+
if (active) {
|
|
380
|
+
rows.push({ kind: 'working', id: `working-${turnId}`, startedAt: turn?.createdAt ?? Date.now() });
|
|
381
|
+
}
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
// The closing answer stays visible; everything before it folds behind the label.
|
|
385
|
+
const finalAssistant = lastAssistantRow(work);
|
|
386
|
+
const before = finalAssistant ? work.slice(0, work.indexOf(finalAssistant)) : work;
|
|
387
|
+
// A workflow runs on past the turn that launched it, so its row stays in sight until it ends.
|
|
388
|
+
const standing = before.filter((row) => row.kind === 'workflow' && row.tool.state === 'running');
|
|
389
|
+
const folded = before.filter((row) => !standing.includes(row));
|
|
390
|
+
const expanded = options.expandedTurns.has(turnId);
|
|
391
|
+
if (folded.length > 0) {
|
|
392
|
+
const tools = rest.filter((item): item is ChatToolItem => item.kind === 'tool' && parentOf(item) === null && handbackReportOf(item) === null);
|
|
393
|
+
rows.push({
|
|
394
|
+
kind: 'turn-fold',
|
|
395
|
+
id: `fold-${turnId}`,
|
|
396
|
+
turn,
|
|
397
|
+
label: turnLabel(turn, chunk.items),
|
|
398
|
+
work: summarizeTurn(tools),
|
|
399
|
+
hiddenCount: folded.length,
|
|
400
|
+
expanded
|
|
401
|
+
});
|
|
402
|
+
if (expanded) {
|
|
403
|
+
rows.push(...folded);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
rows.push(...standing);
|
|
407
|
+
const changed = changedFilesRow(turn, rest);
|
|
408
|
+
if (changed) {
|
|
409
|
+
rows.push(changed);
|
|
410
|
+
}
|
|
411
|
+
if (finalAssistant) {
|
|
412
|
+
rows.push(finalAssistant);
|
|
413
|
+
}
|
|
414
|
+
if (options.forkedTurns?.has(turnId) === true) {
|
|
415
|
+
rows.push({ kind: 'forks', id: `forks-${turnId}`, turnId });
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return rows;
|
|
419
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Bot, Eye, FolderSearch, Globe, ListTodo, Search, SquarePen, Terminal, Workflow, Zap, type LucideIcon } from 'lucide-react';
|
|
2
|
+
|
|
3
|
+
/* Everything the thread knows about one tool by name. */
|
|
4
|
+
export interface ToolEntry {
|
|
5
|
+
/* The mark in the gutter of the call's row. */
|
|
6
|
+
icon: LucideIcon;
|
|
7
|
+
/* The input keys that hold the one line saying what the call is about, best first. */
|
|
8
|
+
summary: readonly string[];
|
|
9
|
+
/* Whether a run of calls of this name reads as a sentence of its own (`agent-chat:group.tools.<name>`).
|
|
10
|
+
One that changes files without a sentence falls to `agent-chat:group.edited`, which counts files rather than calls. */
|
|
11
|
+
grouped: boolean;
|
|
12
|
+
/* Whether the call writes to a file, which is what a mixed run of calls is named after. */
|
|
13
|
+
changesFiles: boolean;
|
|
14
|
+
/* Whether the call reads a path the thread may draw as an image. */
|
|
15
|
+
readsImage: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function entry(icon: LucideIcon, summary: readonly string[], rest: Partial<ToolEntry> = {}): ToolEntry {
|
|
19
|
+
return {
|
|
20
|
+
icon,
|
|
21
|
+
summary,
|
|
22
|
+
grouped: true,
|
|
23
|
+
changesFiles: false,
|
|
24
|
+
readsImage: false,
|
|
25
|
+
...rest
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
* The icon, the summary line, the group sentence and the file-changing flag of every tool the thread
|
|
31
|
+
* has a word for, in one table, because they used to sit in four and a name added to one of them was
|
|
32
|
+
* missing from the rest. A name that is not here still draws, reads and groups; it falls back to the
|
|
33
|
+
* wrench and to the first string of its input.
|
|
34
|
+
*/
|
|
35
|
+
export const TOOL_CATALOG: Record<string, ToolEntry> = {
|
|
36
|
+
Read: entry(Eye, ['file_path'], { readsImage: true }),
|
|
37
|
+
// Only `readImagePath` ever had this one, which is why it has no sentence of its own yet.
|
|
38
|
+
NotebookRead: entry(Eye, ['file_path', 'notebook_path'], { grouped: false, readsImage: true }),
|
|
39
|
+
// These three read as "Edited 3 files" through `changesFiles`, so they carry no copy of that sentence.
|
|
40
|
+
Edit: entry(SquarePen, ['file_path'], { grouped: false, changesFiles: true }),
|
|
41
|
+
Write: entry(SquarePen, ['file_path'], { changesFiles: true }),
|
|
42
|
+
MultiEdit: entry(SquarePen, ['file_path'], { grouped: false, changesFiles: true }),
|
|
43
|
+
NotebookEdit: entry(SquarePen, ['file_path', 'notebook_path']),
|
|
44
|
+
// Codex reports a patch as the paths it touches; its own input carries no path of its own.
|
|
45
|
+
ApplyPatch: entry(SquarePen, ['summary'], { grouped: false, changesFiles: true }),
|
|
46
|
+
Bash: entry(Terminal, ['description', 'command']),
|
|
47
|
+
Grep: entry(Search, ['pattern']),
|
|
48
|
+
Glob: entry(FolderSearch, ['pattern']),
|
|
49
|
+
WebFetch: entry(Globe, ['url', 'query']),
|
|
50
|
+
WebSearch: entry(Globe, ['url', 'query']),
|
|
51
|
+
Task: entry(Bot, ['description']),
|
|
52
|
+
Agent: entry(Bot, ['description']),
|
|
53
|
+
Skill: entry(Zap, ['skill']),
|
|
54
|
+
// A plan is a list of objects, so there is no line to lift out of it.
|
|
55
|
+
TodoWrite: entry(ListTodo, []),
|
|
56
|
+
// Its input is the whole script; the line takes what the CLI says the workflow is about instead.
|
|
57
|
+
Workflow: entry(Workflow, [], { grouped: false })
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export function toolEntry(name: string): ToolEntry | undefined {
|
|
61
|
+
return TOOL_CATALOG[name];
|
|
62
|
+
}
|