@adecore/agents-react 0.31.12 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/agents/AccountDot.js +57 -6
- package/dist/agents/AgentIcon.js +88 -12
- package/dist/agents/ModelOptionControl.js +153 -18
- package/dist/agents/ProviderLogo.js +49 -4
- package/dist/agents/account-limits.js +17 -14
- package/dist/agents/accounts.js +99 -67
- package/dist/agents/agent-marks.js +5 -3
- package/dist/agents/model-name.js +42 -31
- package/dist/agents/model-options.js +8 -8
- package/dist/agents/model-requirement.js +17 -14
- package/dist/agents/option-words.d.ts +7 -0
- package/dist/agents/option-words.js +35 -0
- package/dist/agents/provider-paths.js +6 -6
- package/dist/agents/status-look.js +32 -12
- package/dist/chat/account-choice.d.ts +13 -0
- package/dist/chat/account-choice.js +90 -20
- package/dist/chat/actions.js +126 -59
- package/dist/chat/activity.js +5 -5
- package/dist/chat/attachments.js +74 -53
- package/dist/chat/bookmarks.js +58 -48
- package/dist/chat/chat-client.js +344 -263
- package/dist/chat/chat-references.js +3 -3
- package/dist/chat/composer-effort.d.ts +5 -0
- package/dist/chat/composer-effort.js +26 -0
- package/dist/chat/composer-send.js +13 -13
- package/dist/chat/drafts.js +115 -79
- package/dist/chat/forks.js +81 -24
- package/dist/chat/guards.js +15 -43
- package/dist/chat/index.d.ts +3 -1
- package/dist/chat/index.js +3 -2
- package/dist/chat/logic/ansi.js +170 -167
- package/dist/chat/logic/answers.js +2 -2
- package/dist/chat/logic/bookmarks.js +19 -19
- package/dist/chat/logic/command-looks.js +138 -96
- package/dist/chat/logic/context-usage.js +13 -9
- package/dist/chat/logic/continue-after-stop.js +15 -19
- package/dist/chat/logic/effort.d.ts +14 -0
- package/dist/chat/logic/effort.js +34 -0
- package/dist/chat/logic/embedded-code.js +387 -326
- package/dist/chat/logic/fork.js +134 -110
- package/dist/chat/logic/generated-image.js +54 -23
- package/dist/chat/logic/goal.js +28 -20
- package/dist/chat/logic/handback.js +6 -6
- package/dist/chat/logic/json.js +2 -2
- package/dist/chat/logic/limit.js +46 -30
- package/dist/chat/logic/picker-entries.d.ts +5 -3
- package/dist/chat/logic/picker-entries.js +83 -43
- package/dist/chat/logic/resume-compaction.d.ts +14 -1
- package/dist/chat/logic/resume-compaction.js +93 -24
- package/dist/chat/logic/scrubber.js +188 -136
- package/dist/chat/logic/thought.js +13 -16
- package/dist/chat/logic/thread-cards.js +60 -48
- package/dist/chat/logic/timeline-copy.js +30 -38
- package/dist/chat/logic/timeline-target.js +92 -63
- package/dist/chat/logic/timeline.js +499 -355
- package/dist/chat/logic/tool-catalog.js +54 -34
- package/dist/chat/logic/tools.js +106 -82
- package/dist/chat/logic/visual-height.js +18 -18
- package/dist/chat/logic/visual-viewport.js +6 -3
- package/dist/chat/logic/welcome.js +9 -9
- package/dist/chat/logic/workflow.d.ts +10 -0
- package/dist/chat/logic/workflow.js +130 -32
- package/dist/chat/mentions.js +149 -109
- package/dist/chat/persisted-json.js +51 -54
- package/dist/chat/preferences.d.ts +4 -0
- package/dist/chat/preferences.js +162 -84
- package/dist/chat/prompt-hold.js +9 -9
- package/dist/chat/quote.js +21 -30
- package/dist/chat/reading-width.js +12 -10
- package/dist/chat/recent-messages.js +34 -21
- package/dist/chat/resume-compaction-dismissals.js +25 -5
- package/dist/chat/runtime-modes.js +3 -3
- package/dist/chat/send-key.js +11 -7
- package/dist/chat/stash.js +89 -69
- package/dist/chat/subagent-conversation.js +181 -139
- package/dist/chat/subagent-list.js +65 -67
- package/dist/chat/subagent-support.js +12 -7
- package/dist/chat/subagent-view.js +132 -44
- package/dist/chat/thumbnails.js +88 -60
- package/dist/chat/timeline-flash.js +24 -12
- package/dist/chat/timeline-scroll.js +116 -86
- package/dist/chat/ui/AccountPill.js +126 -15
- package/dist/chat/ui/AnsiOutput.js +62 -6
- package/dist/chat/ui/BookmarkMarker.js +231 -41
- package/dist/chat/ui/BookmarkSubmenu.js +165 -18
- package/dist/chat/ui/ChatActivity.js +649 -70
- package/dist/chat/ui/ChatReferenceChip.js +104 -9
- package/dist/chat/ui/CodeBlock.js +132 -58
- package/dist/chat/ui/Composer.js +866 -132
- package/dist/chat/ui/ComposerInput.js +309 -120
- package/dist/chat/ui/DiffPool.js +75 -14
- package/dist/chat/ui/EditDiff.js +160 -21
- package/dist/chat/ui/FadingWords.js +32 -3
- package/dist/chat/ui/GoalDock.js +235 -42
- package/dist/chat/ui/ImageView.js +617 -61
- package/dist/chat/ui/LimitState.js +446 -72
- package/dist/chat/ui/Markdown.js +991 -173
- package/dist/chat/ui/MermaidBlock.js +285 -79
- package/dist/chat/ui/MessageActions.js +251 -39
- package/dist/chat/ui/ModelMarks.js +55 -5
- package/dist/chat/ui/Pickers.js +664 -113
- package/dist/chat/ui/PromptComposer.js +281 -51
- package/dist/chat/ui/QuoteButton.js +151 -57
- package/dist/chat/ui/ResumeCompactionDock.d.ts +6 -2
- package/dist/chat/ui/ResumeCompactionDock.js +141 -6
- package/dist/chat/ui/RowTime.js +59 -5
- package/dist/chat/ui/RunSettings.js +1744 -154
- package/dist/chat/ui/Scrubber.js +517 -88
- package/dist/chat/ui/ShellCommand.js +183 -107
- package/dist/chat/ui/SubagentControls.js +212 -24
- package/dist/chat/ui/SubagentInfo.js +170 -13
- package/dist/chat/ui/SubagentStopButton.js +115 -24
- package/dist/chat/ui/SubagentTimeline.js +357 -91
- package/dist/chat/ui/Timeline.js +988 -476
- package/dist/chat/ui/TimelineMenu.js +552 -37
- package/dist/chat/ui/UnifiedDiff.js +234 -27
- package/dist/chat/ui/UploadThumb.js +37 -7
- package/dist/chat/ui/VisualDialogs.js +251 -20
- package/dist/chat/ui/VisualFrame.js +321 -95
- package/dist/chat/ui/Welcome.js +72 -7
- package/dist/chat/ui/chips.js +1 -1
- package/dist/chat/ui/code-lines.js +36 -36
- package/dist/chat/ui/code-streaming.js +1 -1
- package/dist/chat/ui/code-theme.js +7 -7
- package/dist/chat/ui/composer/ComposerControls.js +329 -21
- package/dist/chat/ui/composer/ComposerMenu.js +358 -30
- package/dist/chat/ui/composer/DraftParts.js +185 -11
- package/dist/chat/ui/composer/QueuedMessages.js +138 -13
- package/dist/chat/ui/composer/chips.js +29 -19
- package/dist/chat/ui/composer/editor.js +79 -63
- package/dist/chat/ui/composer/insert.js +26 -12
- package/dist/chat/ui/composer/key-handler.js +180 -127
- package/dist/chat/ui/composer/keys.js +90 -73
- package/dist/chat/ui/composer/menu-aria.js +18 -9
- package/dist/chat/ui/composer/placeholder.js +78 -31
- package/dist/chat/ui/composer/surface.js +13 -37
- package/dist/chat/ui/composer/use-chat-controls.js +230 -90
- package/dist/chat/ui/composer/use-compaction-offer.js +50 -18
- package/dist/chat/ui/composer/use-composer-draft.js +65 -34
- package/dist/chat/ui/composer/use-composer-menus.js +478 -193
- package/dist/chat/ui/composer/use-composer-submit.js +251 -109
- package/dist/chat/ui/composer/use-continuable-turn.js +33 -6
- package/dist/chat/ui/composer/use-draft-files.js +245 -93
- package/dist/chat/ui/composer/use-effort-stepper.d.ts +13 -0
- package/dist/chat/ui/composer/use-effort-stepper.js +88 -0
- package/dist/chat/ui/composer/use-notice.js +61 -16
- package/dist/chat/ui/composer/use-pending-requests.js +38 -17
- package/dist/chat/ui/composer/use-prompt-history.js +117 -45
- package/dist/chat/ui/composer/use-prompt-hold.js +119 -35
- package/dist/chat/ui/composer/use-take-back.js +94 -72
- package/dist/chat/ui/diff/ApprovalReview.js +101 -23
- package/dist/chat/ui/diff/DiffFallbackBoundary.js +21 -13
- package/dist/chat/ui/diff/DiffOmitted.js +56 -6
- package/dist/chat/ui/diff/DiffStack.js +92 -8
- package/dist/chat/ui/diff/DiffUnavailable.js +56 -6
- package/dist/chat/ui/diff/EditorEditDiff.js +263 -51
- package/dist/chat/ui/diff/EditorPatchDiff.js +39 -14
- package/dist/chat/ui/diff/LegacyDiffPool.js +27 -10
- package/dist/chat/ui/diff/LegacyEditDiff.js +97 -20
- package/dist/chat/ui/diff/LegacyUnifiedDiff.js +172 -42
- package/dist/chat/ui/diff/RawDiff.js +65 -5
- package/dist/chat/ui/diff/ReviewDiff.js +77 -5
- package/dist/chat/ui/diff/diff-props.js +1 -1
- package/dist/chat/ui/diff/diff-source-budget.js +46 -35
- package/dist/chat/ui/diff/editor-edit-model.js +18 -14
- package/dist/chat/ui/diff/review-plan.js +63 -40
- package/dist/chat/ui/diff/review-settle.js +24 -9
- package/dist/chat/ui/diff/syntax-runtime.js +236 -201
- package/dist/chat/ui/diff/use-review-stack.js +146 -25
- package/dist/chat/ui/diff-syntax.js +70 -58
- package/dist/chat/ui/diff-theme.js +31 -12
- package/dist/chat/ui/file-links.js +39 -10
- package/dist/chat/ui/find-reveal.js +29 -8
- package/dist/chat/ui/full-diff.js +17 -12
- package/dist/chat/ui/highlighter.js +143 -113
- package/dist/chat/ui/icons.js +16 -7
- package/dist/chat/ui/index.js +1 -1
- package/dist/chat/ui/intelligent-ui/UiBlockFrame.js +556 -82
- package/dist/chat/ui/intelligent-ui/UiFallbackPart.js +73 -5
- package/dist/chat/ui/intelligent-ui/UiReply.js +666 -262
- package/dist/chat/ui/intelligent-ui/block-local.js +59 -31
- package/dist/chat/ui/intelligent-ui/chart-data.js +77 -67
- package/dist/chat/ui/intelligent-ui/node-text.js +77 -91
- package/dist/chat/ui/intelligent-ui/registry.js +46 -46
- package/dist/chat/ui/intelligent-ui/render-context.js +1 -1
- package/dist/chat/ui/intelligent-ui/renderers/base-land.js +1 -1
- package/dist/chat/ui/intelligent-ui/renderers/binding.js +3 -3
- package/dist/chat/ui/intelligent-ui/renderers/chart.js +1223 -123
- package/dist/chat/ui/intelligent-ui/renderers/choices.js +312 -34
- package/dist/chat/ui/intelligent-ui/renderers/content.js +1241 -86
- package/dist/chat/ui/intelligent-ui/renderers/data.js +619 -54
- package/dist/chat/ui/intelligent-ui/renderers/inputs.js +868 -67
- package/dist/chat/ui/intelligent-ui/renderers/links.js +366 -62
- package/dist/chat/ui/intelligent-ui/renderers/map-slots.js +118 -60
- package/dist/chat/ui/intelligent-ui/renderers/map-style.js +85 -78
- package/dist/chat/ui/intelligent-ui/renderers/map-view.js +282 -217
- package/dist/chat/ui/intelligent-ui/renderers/map.js +621 -69
- package/dist/chat/ui/intelligent-ui/renderers/place-point.js +15 -8
- package/dist/chat/ui/intelligent-ui/renderers/structure.js +262 -20
- package/dist/chat/ui/intelligent-ui/renderers/text.js +674 -49
- package/dist/chat/ui/intelligent-ui/reply-parts.js +24 -15
- package/dist/chat/ui/intelligent-ui/table-data.js +97 -57
- package/dist/chat/ui/intelligent-ui/tones.js +31 -31
- package/dist/chat/ui/intelligent-ui/use-in-view.js +30 -12
- package/dist/chat/ui/intelligent-ui/use-later.js +28 -13
- package/dist/chat/ui/intelligent-ui/use-truncated.js +45 -21
- package/dist/chat/ui/intelligent-ui/use-ui-links.js +296 -109
- package/dist/chat/ui/intelligent-ui/use-ui-queries.js +256 -246
- package/dist/chat/ui/markdown-blocks.js +296 -262
- package/dist/chat/ui/mermaid.d.ts +8 -20
- package/dist/chat/ui/mermaid.js +47 -84
- package/dist/chat/ui/quote-selection.js +32 -29
- package/dist/chat/ui/rehype-chips.js +35 -23
- package/dist/chat/ui/rehype-fade.js +28 -17
- package/dist/chat/ui/remark-html-as-text.js +22 -15
- package/dist/chat/ui/reply-context.js +1 -1
- package/dist/chat/ui/reveal.js +161 -72
- package/dist/chat/ui/rows/ForksRow.js +226 -24
- package/dist/chat/ui/rows/GeneratedImageRow.js +541 -87
- package/dist/chat/ui/rows/MessageRows.js +1706 -159
- package/dist/chat/ui/rows/RowContainer.js +57 -7
- package/dist/chat/ui/rows/Rows.js +463 -54
- package/dist/chat/ui/rows/SubagentRow.js +577 -60
- package/dist/chat/ui/rows/TaskRow.js +359 -40
- package/dist/chat/ui/rows/VisualRow.js +233 -11
- package/dist/chat/ui/rows/WorkRows.d.ts +3 -0
- package/dist/chat/ui/rows/WorkRows.js +1945 -164
- package/dist/chat/ui/rows/WorkflowRow.js +756 -37
- package/dist/chat/ui/rows/row-rhythm.js +27 -17
- package/dist/chat/ui/theme-watch.js +26 -23
- package/dist/chat/ui/timestamp-format.js +4 -1
- package/dist/chat/ui/use-diff-source-tokens.js +70 -34
- package/dist/chat/ui/useToggleSet.js +51 -19
- package/dist/chat/ui/visual-theme.js +153 -128
- package/dist/chat/visual-bridge.js +123 -123
- package/dist/chat/visual-viewport.js +94 -83
- package/dist/chat/visuals.js +13 -5
- package/dist/host-hooks.d.ts +29 -0
- package/dist/host-hooks.js +78 -0
- package/dist/host.js +120 -48
- package/dist/index.js +1 -1
- package/dist/lazy.js +38 -32
- package/dist/locales/en/agent-chat.d.ts +42 -0
- package/dist/locales/en/agent-chat.js +755 -713
- package/dist/locales/en/agent-prompts.js +42 -42
- package/dist/locales/en/agent-providers.d.ts +1 -0
- package/dist/locales/en/agent-providers.js +166 -165
- package/dist/locales/en/agent-usage.d.ts +6 -0
- package/dist/locales/en/agent-usage.js +129 -123
- package/dist/locales/nl/agent-chat.d.ts +42 -0
- package/dist/locales/nl/agent-chat.js +755 -713
- package/dist/locales/nl/agent-prompts.js +42 -42
- package/dist/locales/nl/agent-providers.d.ts +1 -0
- package/dist/locales/nl/agent-providers.js +166 -165
- package/dist/locales/nl/agent-usage.d.ts +6 -0
- package/dist/locales/nl/agent-usage.js +129 -123
- package/dist/locales.js +14 -14
- package/dist/mounted-registry.js +31 -30
- package/dist/port-transport.js +122 -111
- package/dist/prompts/index.js +1 -1
- package/dist/prompts/logic/focus.js +15 -14
- package/dist/prompts/logic/keys.js +93 -59
- package/dist/prompts/logic/prompts.js +27 -14
- package/dist/prompts/logic/subjects.js +60 -46
- package/dist/prompts/logic/useFocusAfterAnswer.js +60 -20
- package/dist/prompts/logic/usePromptSession.js +88 -77
- package/dist/prompts/ui/ApprovalBody.js +246 -27
- package/dist/prompts/ui/PromptActions.js +215 -9
- package/dist/prompts/ui/PromptCard.js +215 -36
- package/dist/prompts/ui/PromptView.js +372 -34
- package/dist/prompts/ui/QuestionBody.js +269 -73
- package/dist/providers/AccountColors.js +36 -5
- package/dist/providers/AccountDetail.js +683 -50
- package/dist/providers/AccountVariables.js +139 -32
- package/dist/providers/AddAccountForm.js +125 -38
- package/dist/providers/CliDetail.js +343 -36
- package/dist/providers/InstallPage.js +644 -59
- package/dist/providers/ProviderList.d.ts +2 -1
- package/dist/providers/ProviderList.js +215 -23
- package/dist/providers/ProvidersPane.d.ts +4 -2
- package/dist/providers/ProvidersPane.js +244 -64
- package/dist/providers/account-actions.js +54 -30
- package/dist/providers/index.js +1 -1
- package/dist/providers/install-steps.js +12 -9
- package/dist/providers/pane-target.d.ts +3 -0
- package/dist/providers/pane-target.js +34 -13
- package/dist/providers/parts.js +62 -12
- package/dist/providers/provider-abilities.js +5 -5
- package/dist/providers/provider-groups.js +75 -40
- package/dist/providers/variables.js +73 -39
- package/dist/scope.js +6 -6
- package/dist/settings/sections.js +18 -18
- package/dist/state/chats.js +404 -192
- package/dist/state/index.js +1 -1
- package/dist/state/provider-accounts.js +70 -47
- package/dist/state/providers.js +45 -16
- package/dist/state/usage.js +154 -77
- package/dist/storage.js +69 -68
- package/dist/testing/index.js +1 -1
- package/dist/testing/prompts.js +105 -67
- package/dist/transport.js +10 -10
- package/dist/ui/PlainTextarea.js +27 -3
- package/dist/usage/LimitsList.js +646 -40
- package/dist/usage/UsageBreakdown.js +879 -38
- package/dist/usage/UsageChart.js +311 -32
- package/dist/usage/UsageDialog.js +46 -4
- package/dist/usage/UsageLimits.js +467 -34
- package/dist/usage/UsageLimitsCard.js +95 -11
- package/dist/usage/UsagePage.js +817 -96
- package/dist/usage/UsagePane.js +164 -10
- package/dist/usage/UsageSummary.js +151 -6
- package/dist/usage/UsageTiles.js +164 -14
- package/dist/usage/format.js +32 -32
- package/dist/usage/index.d.ts +1 -1
- package/dist/usage/index.js +2 -1
- package/dist/usage/limit-groups.d.ts +2 -0
- package/dist/usage/limit-groups.js +77 -59
- package/dist/usage/limits.js +78 -43
- package/dist/usage/money.js +20 -4
- package/dist/usage/summary.js +85 -64
- package/package.json +9 -9
- package/src/agents/ModelOptionControl.tsx +3 -1
- package/src/agents/option-words.ts +27 -0
- package/src/chat/account-choice.ts +41 -1
- package/src/chat/composer-effort.ts +32 -0
- package/src/chat/forks.ts +4 -5
- package/src/chat/index.ts +3 -1
- package/src/chat/logic/effort.ts +32 -0
- package/src/chat/logic/picker-entries.ts +45 -10
- package/src/chat/logic/resume-compaction.ts +95 -17
- package/src/chat/logic/timeline.ts +11 -3
- package/src/chat/logic/workflow.ts +86 -4
- package/src/chat/preferences.ts +35 -0
- package/src/chat/subagent-view.ts +2 -3
- package/src/chat/ui/AnsiOutput.tsx +1 -2
- package/src/chat/ui/BookmarkSubmenu.tsx +3 -3
- package/src/chat/ui/ChatActivity.tsx +14 -22
- package/src/chat/ui/ChatReferenceChip.tsx +2 -3
- package/src/chat/ui/Composer.tsx +15 -7
- package/src/chat/ui/EditDiff.tsx +2 -3
- package/src/chat/ui/LimitState.tsx +2 -2
- package/src/chat/ui/Markdown.tsx +10 -8
- package/src/chat/ui/MermaidBlock.tsx +57 -49
- package/src/chat/ui/Pickers.tsx +49 -29
- package/src/chat/ui/PromptComposer.tsx +7 -9
- package/src/chat/ui/ResumeCompactionDock.tsx +16 -4
- package/src/chat/ui/RunSettings.tsx +67 -28
- package/src/chat/ui/Scrubber.tsx +3 -3
- package/src/chat/ui/ShellCommand.tsx +2 -0
- package/src/chat/ui/SubagentInfo.tsx +2 -3
- package/src/chat/ui/SubagentTimeline.tsx +12 -16
- package/src/chat/ui/Timeline.tsx +6 -6
- package/src/chat/ui/UnifiedDiff.tsx +3 -4
- package/src/chat/ui/VisualFrame.tsx +5 -8
- package/src/chat/ui/code-theme.ts +4 -4
- package/src/chat/ui/composer/placeholder.ts +2 -2
- package/src/chat/ui/composer/use-compaction-offer.ts +36 -16
- package/src/chat/ui/composer/use-composer-menus.ts +4 -9
- package/src/chat/ui/composer/use-composer-submit.ts +2 -1
- package/src/chat/ui/composer/use-effort-stepper.ts +56 -0
- package/src/chat/ui/composer/use-notice.ts +2 -2
- package/src/chat/ui/diff/EditorEditDiff.tsx +9 -13
- package/src/chat/ui/diff/LegacyEditDiff.tsx +12 -17
- package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +13 -21
- package/src/chat/ui/diff/use-review-stack.ts +4 -4
- package/src/chat/ui/diff-theme.ts +3 -3
- package/src/chat/ui/file-links.ts +2 -2
- package/src/chat/ui/intelligent-ui/UiReply.tsx +2 -0
- package/src/chat/ui/intelligent-ui/renderers/chart.tsx +3 -3
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +5 -6
- package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +2 -1
- package/src/chat/ui/intelligent-ui/renderers/map.tsx +2 -1
- package/src/chat/ui/intelligent-ui/use-truncated.ts +16 -19
- package/src/chat/ui/intelligent-ui/use-ui-links.ts +3 -3
- package/src/chat/ui/mermaid.ts +52 -96
- package/src/chat/ui/rows/ForksRow.tsx +3 -5
- package/src/chat/ui/rows/GeneratedImageRow.tsx +2 -1
- package/src/chat/ui/rows/MessageRows.tsx +12 -21
- package/src/chat/ui/rows/SubagentRow.tsx +3 -3
- package/src/chat/ui/rows/TaskRow.tsx +1 -1
- package/src/chat/ui/rows/WorkRows.tsx +11 -6
- package/src/chat/ui/rows/WorkflowRow.tsx +144 -54
- package/src/chat/ui/useToggleSet.ts +15 -18
- package/src/chat/ui/visual-theme.ts +2 -3
- package/src/host-hooks.ts +113 -0
- package/src/lazy.tsx +2 -0
- package/src/locales/en/agent-chat.ts +142 -108
- package/src/locales/en/agent-prompts.ts +7 -7
- package/src/locales/en/agent-providers.ts +35 -34
- package/src/locales/en/agent-usage.ts +26 -20
- package/src/locales/nl/agent-chat.ts +134 -100
- package/src/locales/nl/agent-prompts.ts +5 -5
- package/src/locales/nl/agent-providers.ts +33 -32
- package/src/locales/nl/agent-usage.ts +25 -20
- package/src/providers/CliDetail.tsx +2 -1
- package/src/providers/InstallPage.tsx +2 -4
- package/src/providers/ProviderList.tsx +12 -2
- package/src/providers/ProvidersPane.tsx +45 -15
- package/src/providers/pane-target.ts +20 -4
- package/src/state/chats.ts +2 -2
- package/src/usage/LimitsList.tsx +12 -13
- package/src/usage/UsageBreakdown.tsx +17 -6
- package/src/usage/UsageLimits.tsx +2 -1
- package/src/usage/UsagePage.tsx +3 -3
- package/src/usage/index.ts +1 -1
- package/src/usage/limit-groups.ts +12 -0
- package/src/usage/limits.ts +2 -2
- package/src/usage/money.ts +1 -2
|
@@ -2,20 +2,32 @@ import { useTranslation } from 'react-i18next';
|
|
|
2
2
|
import { Minimize2 } from 'lucide-react';
|
|
3
3
|
import { formatTokens } from '@adecore/ui/format';
|
|
4
4
|
import { Button, Icon } from '@adecore/ui';
|
|
5
|
+
import type { ResumeCompactionReason } from '../logic/resume-compaction';
|
|
6
|
+
|
|
7
|
+
interface ResumeCompactionDockProps {
|
|
8
|
+
tokens: number;
|
|
9
|
+
/* Why the next message pays for the whole thread; without it the chat only lay still. */
|
|
10
|
+
reason?: ResumeCompactionReason;
|
|
11
|
+
onCompact(): void;
|
|
12
|
+
onDismiss(): void;
|
|
13
|
+
}
|
|
5
14
|
|
|
6
15
|
/*
|
|
7
|
-
* The offer at the top of the composer, for a chat
|
|
16
|
+
* The offer at the top of the composer, for a chat whose prompt cache went cold, so the next message
|
|
8
17
|
* pays for the whole thread again. It takes no keys and blocks nothing: typing and sending keeps the
|
|
9
18
|
* full history, which is what a person who ignores this does.
|
|
10
19
|
*/
|
|
11
|
-
export function ResumeCompactionDock({ tokens, onCompact, onDismiss }:
|
|
20
|
+
export function ResumeCompactionDock({ tokens, reason = 'idle', onCompact, onDismiss }: ResumeCompactionDockProps) {
|
|
12
21
|
const { t } = useTranslation('agent-chat');
|
|
22
|
+
const cold = reason === 'cache-expired';
|
|
13
23
|
return (
|
|
14
24
|
<div className="flex items-center gap-2 border-b border-border px-3.5 py-2.5">
|
|
15
25
|
<Icon icon={Minimize2} size={16} className="shrink-0 text-text-faint" />
|
|
16
26
|
<span className="flex min-w-0 grow flex-col">
|
|
17
|
-
<span className="truncate text-sm text-text">{t('resumeCompaction.title')}</span>
|
|
18
|
-
<span className="truncate text-xs tabular-nums text-text-faint">
|
|
27
|
+
<span className="truncate text-sm text-text">{cold ? t('resumeCompaction.cacheExpired.title') : t('resumeCompaction.title')}</span>
|
|
28
|
+
<span className="truncate text-xs tabular-nums text-text-faint">
|
|
29
|
+
{t(cold ? 'resumeCompaction.cacheExpired.carried' : 'resumeCompaction.carried', { tokens: formatTokens(tokens) })}
|
|
30
|
+
</span>
|
|
19
31
|
</span>
|
|
20
32
|
<Button size="sm" onClick={onDismiss}>
|
|
21
33
|
{t('resumeCompaction.keep')}
|
|
@@ -15,9 +15,10 @@ import {
|
|
|
15
15
|
Shield,
|
|
16
16
|
ShieldAlert,
|
|
17
17
|
SlidersHorizontal,
|
|
18
|
+
Star,
|
|
18
19
|
type LucideIcon
|
|
19
20
|
} from 'lucide-react';
|
|
20
|
-
import type { AgentKind, ChatUsage, ModelInfo, ModelOptionDescriptor, ModelSelection, ProviderInfo, RuntimeMode } from '@adecore/agent-contracts';
|
|
21
|
+
import type { AgentKind, ChatUsage, ModelInfo, ModelOptionDescriptor, ModelSelection, ProviderInfo, RuntimeMode, UsageWindow } from '@adecore/agent-contracts';
|
|
21
22
|
import { AccountDot } from '../../agents/AccountDot';
|
|
22
23
|
import { limitsOfAccount, sessionWindow } from '../../agents/account-limits';
|
|
23
24
|
import { canContinueOn } from '../../agents/accounts';
|
|
@@ -25,12 +26,15 @@ import { AgentIcon } from '../../agents/AgentIcon';
|
|
|
25
26
|
import type { AccountChoice } from '../account-choice';
|
|
26
27
|
import { modelName, shortModelName } from '../../agents/model-name';
|
|
27
28
|
import { ModelMarks } from './ModelMarks';
|
|
28
|
-
import {
|
|
29
|
+
import { favoritesFirst, searchModels, shortlistOf, wantsModelSearch } from '../logic/picker-entries';
|
|
30
|
+
import { favoriteModelsOf, toggleFavoriteModel, useChatPreferences } from '../preferences';
|
|
29
31
|
import { CONTEXT_OPTION, contextFraction, contextSegments, orderOptions, type ContextPart } from '../logic/context-usage';
|
|
30
32
|
import { RUNTIME_MODES } from '../runtime-modes';
|
|
33
|
+
import { localizedOption } from '../../agents/option-words';
|
|
31
34
|
import { formatClock, formatWeekdayClock, isSameDay, formatPercent, formatTokens } from '@adecore/ui/format';
|
|
32
35
|
import { Icon, Menu, Tooltip, useNow } from '@adecore/ui';
|
|
33
36
|
import { useChatHost } from '../../host';
|
|
37
|
+
import { evenPace, paceText } from '../../usage/limit-groups';
|
|
34
38
|
import { useUsageLimits } from '../../usage/limits';
|
|
35
39
|
|
|
36
40
|
const MINUTE_MS = 60_000;
|
|
@@ -143,7 +147,7 @@ export function RunSettings({
|
|
|
143
147
|
const owner = providers.find((entry) => entry.kind === provider);
|
|
144
148
|
const current = modelName(selection.model, owner?.models);
|
|
145
149
|
const short = shortModelName(current, owner?.models);
|
|
146
|
-
const options = orderOptions(model?.options ?? []);
|
|
150
|
+
const options = orderOptions((model?.options ?? []).map(localizedOption));
|
|
147
151
|
const summary = options.flatMap((option) => {
|
|
148
152
|
const label = optionSummary(option, selection);
|
|
149
153
|
return label === null ? [] : [{ id: option.id, label, ring: option.id === CONTEXT_OPTION }];
|
|
@@ -171,7 +175,9 @@ export function RunSettings({
|
|
|
171
175
|
};
|
|
172
176
|
|
|
173
177
|
const searchable = wantsModelSearch(providers);
|
|
174
|
-
const
|
|
178
|
+
const favorites = useChatPreferences((preferences) => preferences.favoriteModels);
|
|
179
|
+
const favoritesOf = (kind: AgentKind): ReadonlySet<string> => favoriteModelsOf(favorites, kind);
|
|
180
|
+
const found = query.trim() === '' ? null : searchModels(providers, query, favoritesOf);
|
|
175
181
|
|
|
176
182
|
// The menu moves the focus to its popup as it opens, so the search field takes it a frame later.
|
|
177
183
|
useEffect(() => {
|
|
@@ -226,28 +232,49 @@ export function RunSettings({
|
|
|
226
232
|
}
|
|
227
233
|
};
|
|
228
234
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
<
|
|
234
|
-
|
|
235
|
-
|
|
235
|
+
// The name wraps rather than truncates, so a long name a provider gives is read whole.
|
|
236
|
+
const modelRow = (entry: ProviderInfo, row: ModelInfo) => {
|
|
237
|
+
const starred = favoritesOf(entry.kind).has(row.slug);
|
|
238
|
+
return (
|
|
239
|
+
<Menu.Row key={row.slug} aria-label={row.name}>
|
|
240
|
+
<Menu.RadioItem value={modelValue(entry.kind, row.slug)} closeOnClick>
|
|
241
|
+
<AgentIcon kind={entry.kind} size={14} className="shrink-0" />
|
|
242
|
+
<span className="min-w-0 break-words">{row.name}</span>
|
|
243
|
+
<ModelMarks model={row} />
|
|
244
|
+
</Menu.RadioItem>
|
|
245
|
+
<Menu.RowAction
|
|
246
|
+
icon={Star}
|
|
247
|
+
label={starred ? t('pickers.model.unstar') : t('pickers.model.star')}
|
|
248
|
+
iconClassName={starred ? 'fill-current text-accent' : 'text-text-faint'}
|
|
249
|
+
closeOnClick={false}
|
|
250
|
+
onClick={() => toggleFavoriteModel(entry.kind, row.slug)}
|
|
251
|
+
/>
|
|
252
|
+
</Menu.Row>
|
|
253
|
+
);
|
|
254
|
+
};
|
|
236
255
|
|
|
237
|
-
// Every model of one provider, under a label per maker where the provider names them.
|
|
238
|
-
const byMaker = (entry: ProviderInfo, models: ModelInfo[]) =>
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
256
|
+
// Every model of one provider, the starred ones first and the rest under a label per maker where the provider names them.
|
|
257
|
+
const byMaker = (entry: ProviderInfo, models: ModelInfo[]) => {
|
|
258
|
+
const starred = favoritesOf(entry.kind);
|
|
259
|
+
return favoritesFirst(models, starred).map((row, i, sorted) => {
|
|
260
|
+
const previous = sorted[i - 1];
|
|
261
|
+
const labeled =
|
|
262
|
+
row.vendor !== undefined && !starred.has(row.slug) && (previous === undefined || starred.has(previous.slug) || previous.vendor !== row.vendor);
|
|
263
|
+
return (
|
|
264
|
+
<Fragment key={row.slug}>
|
|
265
|
+
{labeled && <Menu.GroupLabel>{row.vendor}</Menu.GroupLabel>}
|
|
266
|
+
{modelRow(entry, row)}
|
|
267
|
+
</Fragment>
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
};
|
|
245
271
|
|
|
246
|
-
// A provider with many models shows the default and the chosen one, with all of them one submenu away.
|
|
272
|
+
// A provider with many models shows the starred ones, the default and the chosen one, with all of them one submenu away.
|
|
247
273
|
const providerModels = (entry: ProviderInfo, models: ModelInfo[]) => {
|
|
248
|
-
const
|
|
274
|
+
const starred = favoritesOf(entry.kind);
|
|
275
|
+
const shortlist = shortlistOf(entry.kind, models, { provider, model: selection.model }, starred);
|
|
249
276
|
if (shortlist === null) {
|
|
250
|
-
return models.map((row) => modelRow(entry, row));
|
|
277
|
+
return [...models.filter((row) => starred.has(row.slug)), ...models.filter((row) => !starred.has(row.slug))].map((row) => modelRow(entry, row));
|
|
251
278
|
}
|
|
252
279
|
return (
|
|
253
280
|
<>
|
|
@@ -488,7 +515,7 @@ function AccountRows({ choice, locked }: { choice: AccountChoice; locked(id: str
|
|
|
488
515
|
<AccountDot color={entry.account.color} />
|
|
489
516
|
<span className="truncate">{choice.nameOf(entry)}</span>
|
|
490
517
|
</span>
|
|
491
|
-
{session !== null && <SessionLine
|
|
518
|
+
{session !== null && <SessionLine window={session} now={now} />}
|
|
492
519
|
</span>
|
|
493
520
|
</Menu.RadioItem>
|
|
494
521
|
);
|
|
@@ -502,16 +529,28 @@ function AccountRows({ choice, locked }: { choice: AccountChoice; locked(id: str
|
|
|
502
529
|
});
|
|
503
530
|
}
|
|
504
531
|
|
|
505
|
-
function SessionLine({
|
|
532
|
+
function SessionLine({ window, now }: { window: UsageWindow; now: number }) {
|
|
506
533
|
const { t } = useTranslation('agent-chat');
|
|
507
|
-
const percent = Math.round(used * 100);
|
|
508
|
-
const tone = sessionTone(used);
|
|
534
|
+
const percent = Math.round(window.used * 100);
|
|
535
|
+
const tone = sessionTone(window.used);
|
|
536
|
+
const mark = evenPace(window, now)?.percent ?? null;
|
|
537
|
+
const resetsAt = window.resetsAt;
|
|
509
538
|
return (
|
|
510
539
|
<span className="flex items-center gap-2 text-xs text-text-faint tabular-nums">
|
|
511
|
-
<span aria-hidden className="relative h-1 w-14 shrink-0
|
|
540
|
+
<span aria-hidden className="relative h-1 w-14 shrink-0 rounded-full bg-surface-sunken">
|
|
512
541
|
<span className={clsx('absolute inset-y-0 left-0 rounded-full', tone.bar)} style={{ width: `${Math.min(100, percent)}%` }} />
|
|
542
|
+
{/* Where an even spread over the window would stand, as the usage bars mark it. */}
|
|
543
|
+
{mark !== null && (
|
|
544
|
+
<span
|
|
545
|
+
className={clsx('absolute -inset-y-0.5 w-px -translate-x-1/2', mark <= percent ? 'bg-surface-raised' : 'bg-text-muted')}
|
|
546
|
+
style={{ left: `${mark}%` }}
|
|
547
|
+
/>
|
|
548
|
+
)}
|
|
549
|
+
</span>
|
|
550
|
+
<span aria-hidden className={tone.text}>
|
|
551
|
+
{formatPercent(percent)}
|
|
513
552
|
</span>
|
|
514
|
-
<span className=
|
|
553
|
+
<span className="sr-only">{paceText(window, now)}</span>
|
|
515
554
|
{resetsAt !== null && (
|
|
516
555
|
<span className="whitespace-nowrap">
|
|
517
556
|
· {t('pickers.account.resets', { time: isSameDay(resetsAt, now) ? formatClock(resetsAt) : formatWeekdayClock(resetsAt) })}
|
package/src/chat/ui/Scrubber.tsx
CHANGED
|
@@ -104,9 +104,9 @@ export const Scrubber = memo(function Scrubber({
|
|
|
104
104
|
return () => observer.disconnect();
|
|
105
105
|
}, [strip]);
|
|
106
106
|
|
|
107
|
-
const kinds =
|
|
108
|
-
const marked =
|
|
109
|
-
const layout =
|
|
107
|
+
const kinds = ticks.map((tick) => tick.kind);
|
|
108
|
+
const marked = ticks.map((tick) => tick.bookmark !== null);
|
|
109
|
+
const layout = layoutTicks(kinds, height, marked, found);
|
|
110
110
|
const currentSlot = foundCurrent === null ? null : slotOf(layout, foundCurrent);
|
|
111
111
|
const inView = firstInView === null || lastInView === null ? null : { first: firstInView, last: lastInView };
|
|
112
112
|
const hoveredTick = hovered === null ? null : (ticks[hovered] ?? null);
|
|
@@ -166,6 +166,8 @@ function drawNamed(command: string, runs: readonly Run[] | null, names: readonly
|
|
|
166
166
|
* caller knows, and `names` draws words as the names a host gave them, both as offsets into `command`.
|
|
167
167
|
*/
|
|
168
168
|
export function ShellCommand({ command, code = NO_CODE, names = NO_NAMES }: { command: string; code?: readonly EmbeddedCode[]; names?: readonly NamedWord[] }) {
|
|
169
|
+
// The highlighter's cache fills in place; compiled, the runs it gave before its grammars loaded would stay.
|
|
170
|
+
'use no memo';
|
|
169
171
|
const theme = useCodeTheme();
|
|
170
172
|
const { custom } = useChatHost().code;
|
|
171
173
|
const highlighter = useCodeHighlighter();
|
|
@@ -7,20 +7,19 @@ import { SubagentActions, SubagentBreadcrumb } from './SubagentControls';
|
|
|
7
7
|
import { useNow } from '@adecore/ui';
|
|
8
8
|
import { useModelName } from '../../agents/model-name';
|
|
9
9
|
import { useChatRow } from '../../state/chats';
|
|
10
|
-
import { useChatHost } from '../../host';
|
|
11
10
|
import { useChatScope } from '../../scope';
|
|
11
|
+
import { useHostTask } from '../../host-hooks';
|
|
12
12
|
|
|
13
13
|
/*
|
|
14
14
|
* What a sub-agent is, over its conversation: the way down to it, its state and time, the model it runs on
|
|
15
15
|
* and what it spent, and the way out. Only the way down and out for a conversation whose row the thread lost.
|
|
16
16
|
*/
|
|
17
17
|
export function SubagentInfo({ chatId, toolUseId }: { chatId: string; toolUseId: string }) {
|
|
18
|
-
const host = useChatHost();
|
|
19
18
|
const { t } = useTranslation('agent-chat');
|
|
20
19
|
const { id } = useChatScope();
|
|
21
20
|
const item = useSubagentItem(chatId, toolUseId);
|
|
22
21
|
const provider = useChatRow(chatId, (row) => row?.info.provider);
|
|
23
|
-
const task =
|
|
22
|
+
const task = useHostTask(id, item === null ? null : taskIdOf(item));
|
|
24
23
|
const modelName = useModelName(provider, item?.model ?? '');
|
|
25
24
|
const now = useNow(1000, item?.status === 'running');
|
|
26
25
|
const word = item === null ? null : statusWordOf(item, task);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useLayoutEffect,
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { Bot, LoaderCircle } from 'lucide-react';
|
|
4
4
|
import { deriveTimelineRows } from '../logic/timeline';
|
|
@@ -34,8 +34,8 @@ export function SubagentTimeline({ chatId, toolUseId }: { chatId: string; toolUs
|
|
|
34
34
|
const scope = useChatScope();
|
|
35
35
|
const { commandLooks } = useChatHost();
|
|
36
36
|
const connected = useSyncExternalStore(
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
(changed) => scope.transport.subscribeStatus(changed),
|
|
38
|
+
() => scope.transport.status === 'open',
|
|
39
39
|
() => false
|
|
40
40
|
);
|
|
41
41
|
const { trail, show } = useSubagentTrail(chatId);
|
|
@@ -46,9 +46,9 @@ export function SubagentTimeline({ chatId, toolUseId }: { chatId: string; toolUs
|
|
|
46
46
|
const threadRef = useRef<HTMLDivElement>(null);
|
|
47
47
|
const [target, setTarget] = useState<TimelineTarget>(EMPTY_TARGET);
|
|
48
48
|
const followRef = useRef(true);
|
|
49
|
-
const stopFollowing =
|
|
49
|
+
const stopFollowing = () => {
|
|
50
50
|
followRef.current = false;
|
|
51
|
-
}
|
|
51
|
+
};
|
|
52
52
|
const groups = useToggleSet(stopFollowing);
|
|
53
53
|
const subagents = useToggleSet(stopFollowing);
|
|
54
54
|
// The row being read when an earlier page was asked for, so it stays where it was once that page is in.
|
|
@@ -72,17 +72,13 @@ export function SubagentTimeline({ chatId, toolUseId }: { chatId: string; toolUs
|
|
|
72
72
|
}
|
|
73
73
|
}, [state.unsupported, scope]);
|
|
74
74
|
|
|
75
|
-
const rows =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
commandLooks
|
|
83
|
-
}),
|
|
84
|
-
[state.items, groups.ids, subagents.ids, commandLooks]
|
|
85
|
-
);
|
|
75
|
+
const rows = deriveTimelineRows(state.items, {
|
|
76
|
+
expandedGroups: groups.ids,
|
|
77
|
+
expandedTurns: NO_TURNS,
|
|
78
|
+
expandedSubagents: subagents.ids,
|
|
79
|
+
activeTurnId: null,
|
|
80
|
+
commandLooks
|
|
81
|
+
});
|
|
86
82
|
|
|
87
83
|
useLayoutEffect(() => {
|
|
88
84
|
const element = scrollRef.current;
|
package/src/chat/ui/Timeline.tsx
CHANGED
|
@@ -79,6 +79,7 @@ import { FileLinkContext } from './file-links';
|
|
|
79
79
|
import { AgentIcon } from '../../agents/AgentIcon';
|
|
80
80
|
import { useModelName } from '../../agents/model-name';
|
|
81
81
|
import { ContextMenu, EmptyState, ErrorBoundary, Icon, SectionLabel } from '@adecore/ui';
|
|
82
|
+
import { useHostContextSources, useHostHidesFolder, useHostThreadCards, useHostTimelineFind, useHostWelcome } from '../../host-hooks';
|
|
82
83
|
|
|
83
84
|
const ESTIMATED_ROW_PX = 56;
|
|
84
85
|
// How long a jump waits for a thread that is still loading before it calls the item missing.
|
|
@@ -95,12 +96,11 @@ function showRowAtTop(virtualizer: Virtualizer<HTMLDivElement, Element>, followR
|
|
|
95
96
|
* and the folder it works in, so a first question can lean on all three.
|
|
96
97
|
*/
|
|
97
98
|
function EmptyThread({ chatId }: { chatId: string }) {
|
|
98
|
-
const host = useChatHost();
|
|
99
99
|
const { t } = useTranslation('agent-chat');
|
|
100
100
|
const info = useChatRow(chatId, (row) => row?.info ?? null);
|
|
101
101
|
const model = useModelName(info?.provider, info?.selection.model ?? '');
|
|
102
|
-
const sources =
|
|
103
|
-
const hidesFolder =
|
|
102
|
+
const sources = useHostContextSources(chatId);
|
|
103
|
+
const hidesFolder = useHostHidesFolder(chatId);
|
|
104
104
|
return (
|
|
105
105
|
<div className="flex grow items-center justify-center">
|
|
106
106
|
<div className="flex max-w-sm flex-col items-center gap-3 px-6 py-8 text-center">
|
|
@@ -262,7 +262,7 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
262
262
|
}
|
|
263
263
|
);
|
|
264
264
|
}, [order, items, groups.ids, turns.ids, subagents.ids, activeTurnId, forkedTurns, host.commandLooks]);
|
|
265
|
-
const cards =
|
|
265
|
+
const cards = useHostThreadCards(scopeId, chatId);
|
|
266
266
|
const drawsVisuals = host.visuals !== null;
|
|
267
267
|
// Where the part of the thread this client holds begins, while there is a page before it.
|
|
268
268
|
const heldFrom = cursor === null ? null : (items?.[order?.[0] ?? '']?.createdAt ?? null);
|
|
@@ -270,7 +270,7 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
270
270
|
() => withTimedRows(threadRows, timedRowsOf(cards, drawsVisuals ? (visuals ?? []) : null, heldFrom)),
|
|
271
271
|
[threadRows, cards, drawsVisuals, visuals, heldFrom]
|
|
272
272
|
);
|
|
273
|
-
const welcomes =
|
|
273
|
+
const welcomes = useHostWelcome(chatId);
|
|
274
274
|
|
|
275
275
|
const empty = rows.length === 0;
|
|
276
276
|
// A chat that took a turn before is one whose thread has not come in yet, not one to greet.
|
|
@@ -475,7 +475,7 @@ export function Timeline({ chatId, composer, overlay }: { chatId: string; compos
|
|
|
475
475
|
}),
|
|
476
476
|
[chatId, onMainAgent, rows, items, order, virtualizer, turns.add, groups.add, subagents.add, stopFollowing]
|
|
477
477
|
);
|
|
478
|
-
const chatFind =
|
|
478
|
+
const chatFind = useHostTimelineFind(findOptions);
|
|
479
479
|
const searching = chatFind.open;
|
|
480
480
|
// A find covers the whole thread, so every page before this one comes in while it is open.
|
|
481
481
|
useEffect(() => {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
2
1
|
import { LazyBoundary } from '@adecore/ui';
|
|
3
2
|
import { useTranslation } from 'react-i18next';
|
|
4
3
|
import { lazyNamed } from '../../lazy';
|
|
@@ -20,11 +19,11 @@ export default function UnifiedDiff(props: UnifiedDiffProps) {
|
|
|
20
19
|
const loadEngine = host.code.editor;
|
|
21
20
|
const oldText = contents?.old ?? '';
|
|
22
21
|
const newText = contents?.new ?? '';
|
|
23
|
-
const sourceAllowed =
|
|
22
|
+
const sourceAllowed = diffSourceBudget(oldText, newText) && patchSourceBudget(change.diff);
|
|
24
23
|
const legacyMode = !allowEditor || loadEngine === undefined;
|
|
25
24
|
const hasHunks = /^@@/m.test(change.diff);
|
|
26
|
-
const legacyAllowed =
|
|
27
|
-
const rawAllowed =
|
|
25
|
+
const legacyAllowed = legacyMode && legacyDiffBudget(oldText, newText, change.diff);
|
|
26
|
+
const rawAllowed = hasHunks || legacyDiffBudget('', '', change.diff);
|
|
28
27
|
const omitted = <DiffOmitted path={change.path} />;
|
|
29
28
|
const fallback = legacyAllowed ? (
|
|
30
29
|
<DiffFallbackBoundary fallback={omitted} resetKeys={[change.path, change.diff, oldText, newText, diffStyle]}>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
4
|
import { visualThemeFragment, type ChatVisual, type VisualAppearance, type VisualTheme } from '@adecore/agent-contracts/visual';
|
|
@@ -44,13 +44,10 @@ export function VisualFrame({ chatId, visual, fill = false, className }: VisualF
|
|
|
44
44
|
const controllerRef = useRef<VisualViewportController | null>(null);
|
|
45
45
|
const [viewportReady, setViewportReady] = useState(false);
|
|
46
46
|
const [measure, width] = useMeasuredWidth();
|
|
47
|
-
const setBox =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
[measure]
|
|
53
|
-
);
|
|
47
|
+
const setBox = (node: HTMLDivElement | null) => {
|
|
48
|
+
boxRef.current = node;
|
|
49
|
+
measure(node);
|
|
50
|
+
};
|
|
54
51
|
const theme = useVisualTheme(boxRef);
|
|
55
52
|
const widthRef = useRef(width);
|
|
56
53
|
const [reported, setReported] = useState<number | null>(null);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ThemeRegistration } from 'shiki';
|
|
2
|
-
import {
|
|
2
|
+
import { type ChatHost } from '../../host';
|
|
3
|
+
import { useHostCodeMode, useHostCodeThemes } from '../../host-hooks';
|
|
3
4
|
|
|
4
5
|
/* The Shiki theme code is drawn in under the app's current light or dark. */
|
|
5
6
|
export function useCodeTheme(): string {
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const themes = code.useThemes();
|
|
7
|
+
const mode = useHostCodeMode();
|
|
8
|
+
const themes = useHostCodeThemes();
|
|
9
9
|
return mode === 'dark' ? themes.dark : themes.light;
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import { useChatHost } from '../../../host';
|
|
4
3
|
import { useChatScope } from '../../../scope';
|
|
4
|
+
import { useHostComposerPlaceholder } from '../../../host-hooks';
|
|
5
5
|
|
|
6
6
|
/* The empty box names its sigils as key caps, so they read as keys to press rather than as punctuation. */
|
|
7
7
|
function hintedPlaceholder(lead: string, joiner: string, hints: ReadonlyArray<{ key: string; label: string }>): HTMLElement {
|
|
@@ -31,7 +31,7 @@ interface PlaceholderInput {
|
|
|
31
31
|
export function useHintedPlaceholder({ chatId, disabled, readOnly, mentionable, hasSkills }: PlaceholderInput): string | HTMLElement {
|
|
32
32
|
const { t } = useTranslation('agent-chat');
|
|
33
33
|
const scope = useChatScope();
|
|
34
|
-
const lead =
|
|
34
|
+
const lead = useHostComposerPlaceholder(scope.id, chatId);
|
|
35
35
|
return useMemo(() => {
|
|
36
36
|
if (disabled) {
|
|
37
37
|
return t('composer.placeholder.disconnected');
|
|
@@ -1,25 +1,45 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
1
2
|
import type { ChatInfo, ChatItem, ProviderCapabilities } from '@adecore/agent-contracts';
|
|
2
|
-
import { useNow } from '@adecore/ui';
|
|
3
3
|
import { useChatRow } from '../../../state/chats';
|
|
4
|
-
import { RESUME_COMPACTION_TOKENS, resumeCompactionOffer } from '../../logic/resume-compaction';
|
|
4
|
+
import { RESUME_COMPACTION_TOKENS, resumeCompactionDueAt, resumeCompactionOffer, watchDueAt, type DueClock } from '../../logic/resume-compaction';
|
|
5
5
|
import { useResumeCompactionDismissal } from '../../resume-compaction-dismissals';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const PAGE_CLOCK: DueClock = {
|
|
8
|
+
now: () => Date.now(),
|
|
9
|
+
setTimer: (run, ms) => setTimeout(run, ms),
|
|
10
|
+
clearTimer: (timer) => clearTimeout(timer as ReturnType<typeof setTimeout>),
|
|
11
|
+
onWake: (wake) => {
|
|
12
|
+
if (typeof window === 'undefined') {
|
|
13
|
+
return () => {};
|
|
14
|
+
}
|
|
15
|
+
window.addEventListener('focus', wake);
|
|
16
|
+
document.addEventListener('visibilitychange', wake);
|
|
17
|
+
return () => {
|
|
18
|
+
window.removeEventListener('focus', wake);
|
|
19
|
+
document.removeEventListener('visibilitychange', wake);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/* The offer to fold a long context before a chat whose cache went cold goes on, or null when there is none. */
|
|
8
25
|
export function useCompactionOffer(chatId: string, info: ChatInfo, compaction: ProviderCapabilities['compaction'] | undefined) {
|
|
9
26
|
const order = useChatRow(chatId, (row) => row?.order);
|
|
10
|
-
const
|
|
27
|
+
const structure = useChatRow(chatId, (row) => row?.structure);
|
|
11
28
|
const dismissedTurnId = useResumeCompactionDismissal(chatId);
|
|
12
|
-
|
|
29
|
+
const [now, setNow] = useState(Date.now);
|
|
30
|
+
// The cheap half of the offer, so a chat that can never make one walks no thread.
|
|
13
31
|
const couldOffer = compaction !== undefined && compaction !== 'none' && info.usage.contextTokens >= RESUME_COMPACTION_TOKENS;
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
});
|
|
32
|
+
const items = couldOffer ? (order ?? []).map((id) => structure?.[id]).filter((item): item is ChatItem => item !== undefined) : [];
|
|
33
|
+
const input = { info, compaction, items, dismissedTurnId };
|
|
34
|
+
const dueAt = couldOffer ? resumeCompactionDueAt(input) : null;
|
|
35
|
+
|
|
36
|
+
// A watch for the moment the offer comes due, so a chat that can never make one keeps none.
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (dueAt === null) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
return watchDueAt(dueAt, setNow, PAGE_CLOCK);
|
|
42
|
+
}, [dueAt]);
|
|
43
|
+
|
|
44
|
+
return dueAt === null ? null : resumeCompactionOffer({ ...input, now });
|
|
25
45
|
}
|
|
@@ -23,6 +23,7 @@ import type { ComposerInputHandle } from '../ComposerInput';
|
|
|
23
23
|
import type { ComposerDraft } from './use-composer-draft';
|
|
24
24
|
import { inCode } from './keys';
|
|
25
25
|
import { clampMenuIndex, composerMenuAria } from './menu-aria';
|
|
26
|
+
import { useHostReferableChats } from '../../../host-hooks';
|
|
26
27
|
|
|
27
28
|
const SEARCH_DEBOUNCE_MS = 80;
|
|
28
29
|
// What fits above the composer without turning the picker into a file tree.
|
|
@@ -62,7 +63,7 @@ export function useComposerMenus({ chatId, info, capabilities, providerName, dra
|
|
|
62
63
|
const scope = useChatScope();
|
|
63
64
|
const host = useChatHost();
|
|
64
65
|
const { searchFiles } = host;
|
|
65
|
-
const chats =
|
|
66
|
+
const chats = useHostReferableChats();
|
|
66
67
|
const [menuIndex, setMenuIndex] = useState(0);
|
|
67
68
|
const [mention, setMention] = useState<MentionQuery | null>(null);
|
|
68
69
|
const [skillQuery, setSkillQuery] = useState<MentionQuery | null>(null);
|
|
@@ -156,10 +157,7 @@ export function useComposerMenus({ chatId, info, capabilities, providerName, dra
|
|
|
156
157
|
const mentionMenuOpen = !commandMenuOpen && !skillMenuOpen && mention !== null;
|
|
157
158
|
// Results belong to the query that asked for them; a closed picker shows none while the next answer is on its way.
|
|
158
159
|
const files = mention === null ? [] : searched.files;
|
|
159
|
-
const chatMatches =
|
|
160
|
-
() => (mention === null ? [] : chatSuggestions(chats, mention.query, [chatId, ...draft.chats], CHAT_RESULTS)),
|
|
161
|
-
[chatId, chats, draft.chats, mention]
|
|
162
|
-
);
|
|
160
|
+
const chatMatches = mention === null ? [] : chatSuggestions(chats, mention.query, [chatId, ...draft.chats], CHAT_RESULTS);
|
|
163
161
|
const mentionCount = chatMatches.length + files.length;
|
|
164
162
|
// One menu is open at a time, so they share an id.
|
|
165
163
|
const listId = useId();
|
|
@@ -167,10 +165,7 @@ export function useComposerMenus({ chatId, info, capabilities, providerName, dra
|
|
|
167
165
|
const menuCount = commandMenuOpen ? commands.length : skillMenuOpen ? skillMatches.length : mentionCount;
|
|
168
166
|
// The list can shrink under a stale index; every reader takes the one that still points at an option.
|
|
169
167
|
const activeIndex = clampMenuIndex(menuIndex, menuCount);
|
|
170
|
-
const inputAria =
|
|
171
|
-
() => composerMenuAria({ label: t('composer.inputLabel'), listId, open: menuOpen, activeIndex, count: menuCount }),
|
|
172
|
-
[t, listId, menuOpen, activeIndex, menuCount]
|
|
173
|
-
);
|
|
168
|
+
const inputAria = composerMenuAria({ label: t('composer.inputLabel'), listId, open: menuOpen, activeIndex, count: menuCount });
|
|
174
169
|
|
|
175
170
|
/* Which picker the caret opens: `@` for a file, `$` for a skill, or neither. In code a sigil is just text. */
|
|
176
171
|
const trackTriggers = (value: string, selection: TextRange, state: EditorState): void => {
|
|
@@ -19,6 +19,7 @@ import type { ComposerDraft } from './use-composer-draft';
|
|
|
19
19
|
import type { ComposerMenus } from './use-composer-menus';
|
|
20
20
|
import type { PromptHistory } from './use-prompt-history';
|
|
21
21
|
import type { ComposerPromptHold } from './use-prompt-hold';
|
|
22
|
+
import { useHostReferableChats } from '../../../host-hooks';
|
|
22
23
|
|
|
23
24
|
interface SubmitInput {
|
|
24
25
|
chatId: string;
|
|
@@ -58,7 +59,7 @@ export function useComposerSubmit({
|
|
|
58
59
|
const host = useChatHost();
|
|
59
60
|
const scope = useChatScope();
|
|
60
61
|
const { draft, setDraft } = box;
|
|
61
|
-
const referable =
|
|
62
|
+
const referable = useHostReferableChats();
|
|
62
63
|
|
|
63
64
|
/* Opens the host's new chat once the host took the message; a host that answers nothing took it. */
|
|
64
65
|
const afterSend = (sent: void | Promise<boolean>, andNew: boolean): void => {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import type { ChatInfo, ModelInfo } from '@adecore/agent-contracts';
|
|
4
|
+
import { useChatScope } from '../../../scope';
|
|
5
|
+
import { choiceWord } from '../../../agents/option-words';
|
|
6
|
+
import { registerEffortStepper } from '../../composer-effort';
|
|
7
|
+
import { EFFORT_OPTION, stepEffort, type EffortStep } from '../../logic/effort';
|
|
8
|
+
import type { ChatControls } from './use-chat-controls';
|
|
9
|
+
import type { ComposerNotice } from './use-notice';
|
|
10
|
+
|
|
11
|
+
interface EffortStepperInput {
|
|
12
|
+
chatId: string;
|
|
13
|
+
info: ChatInfo;
|
|
14
|
+
model: ModelInfo | undefined;
|
|
15
|
+
writable: boolean;
|
|
16
|
+
controls: ChatControls;
|
|
17
|
+
setNotice: ComposerNotice['setNotice'];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Lets a command of the host step this chat's reasoning effort, as picking it in the pill's menu does, and says where it landed. */
|
|
21
|
+
export function useEffortStepper({ chatId, info, model, writable, controls, setNotice }: EffortStepperInput): void {
|
|
22
|
+
const { t } = useTranslation('agent-chat');
|
|
23
|
+
const scope = useChatScope();
|
|
24
|
+
|
|
25
|
+
const step = (direction: EffortStep): void => {
|
|
26
|
+
// The pill is disabled then too, and a second configuration would be dropped anyway.
|
|
27
|
+
if (!writable || controls.configurationPending) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const name = model?.name ?? info.selection.model;
|
|
31
|
+
const result = stepEffort(model, info.selection, direction);
|
|
32
|
+
if (result.kind === 'none') {
|
|
33
|
+
setNotice(t('composer.notice.noEffort', { model: name }), 'info');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (result.kind === 'edge') {
|
|
37
|
+
setNotice(
|
|
38
|
+
t(direction === 'up' ? 'composer.notice.effortHighest' : 'composer.notice.effortLowest', {
|
|
39
|
+
effort: choiceWord(EFFORT_OPTION, result.choice),
|
|
40
|
+
model: name
|
|
41
|
+
}),
|
|
42
|
+
'info'
|
|
43
|
+
);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
controls.configure({ selection: result.selection });
|
|
47
|
+
setNotice(t('composer.notice.effort', { effort: choiceWord(EFFORT_OPTION, result.choice) }), 'info');
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// A command means the chat as it stands when it is given, so the registration reads the latest step.
|
|
51
|
+
const stepRef = useRef(step);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
stepRef.current = step;
|
|
54
|
+
});
|
|
55
|
+
useEffect(() => registerEffortStepper(scope.keyOf(chatId), (direction) => stepRef.current(direction)), [scope, chatId]);
|
|
56
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
const NOTICE_MS = 4000;
|
|
4
4
|
|
|
@@ -24,7 +24,7 @@ export function useNotice(): ComposerNotice {
|
|
|
24
24
|
return () => clearTimeout(timer);
|
|
25
25
|
}, [shown]);
|
|
26
26
|
|
|
27
|
-
const setNotice =
|
|
27
|
+
const setNotice = (notice: string | null, tone: NoticeTone = 'error'): void => setShown(notice ? { text: notice, tone } : null);
|
|
28
28
|
|
|
29
29
|
return {
|
|
30
30
|
notice: shown?.text ?? null,
|