@adecore/agents-react 0.31.12 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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.js +45 -34
- 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 +238 -68
- 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.js +60 -72
- 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.js +1945 -164
- package/dist/chat/ui/rows/WorkflowRow.js +293 -34
- 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 +34 -0
- package/dist/locales/en/agent-chat.js +747 -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 +34 -0
- package/dist/locales/nl/agent-chat.js +747 -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 +8 -8
- 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/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 +2 -3
- 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/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 +10 -5
- 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 +134 -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 +126 -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
package/src/chat/ui/Composer.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect,
|
|
1
|
+
import { useEffect, useRef, useState, useSyncExternalStore, type MouseEvent, type PointerEvent } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
4
|
import { ChevronDown } from 'lucide-react';
|
|
@@ -35,6 +35,7 @@ import { useCompactionOffer } from './composer/use-compaction-offer';
|
|
|
35
35
|
import { useComposerDraft } from './composer/use-composer-draft';
|
|
36
36
|
import { useComposerMenus } from './composer/use-composer-menus';
|
|
37
37
|
import { useComposerSubmit } from './composer/use-composer-submit';
|
|
38
|
+
import { useEffortStepper } from './composer/use-effort-stepper';
|
|
38
39
|
import { LEAVE_DROPS_TO_THE_CARD, useDraftFiles } from './composer/use-draft-files';
|
|
39
40
|
import { useContinuableTurn } from './composer/use-continuable-turn';
|
|
40
41
|
import { useNotice } from './composer/use-notice';
|
|
@@ -43,6 +44,7 @@ import { usePromptHistory } from './composer/use-prompt-history';
|
|
|
43
44
|
import { usePromptHold } from './composer/use-prompt-hold';
|
|
44
45
|
import { surfacePartOf } from './composer/surface';
|
|
45
46
|
import { useTakeBack } from './composer/use-take-back';
|
|
47
|
+
import { useHostExtraPrompts, useHostSendDelivery } from '../../host-hooks';
|
|
46
48
|
|
|
47
49
|
interface ComposerProps {
|
|
48
50
|
chatId: string;
|
|
@@ -78,7 +80,7 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
78
80
|
const { t } = useTranslation('agent-chat');
|
|
79
81
|
const host = useChatHost();
|
|
80
82
|
const scope = useChatScope();
|
|
81
|
-
const sendDelivery =
|
|
83
|
+
const sendDelivery = useHostSendDelivery();
|
|
82
84
|
const sendKey = useChatPreferences((preferences) => preferences.sendKey);
|
|
83
85
|
const provider = useProviders((row) => row.providers.find((entry) => entry.kind === info.provider));
|
|
84
86
|
// Absent until the host answered `provider.list`, so only an explicit false hides anything.
|
|
@@ -96,11 +98,19 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
96
98
|
const menus = useComposerMenus({ chatId, info, capabilities, providerName: provider?.name, draft: box, inputRef });
|
|
97
99
|
const files = useDraftFiles({ capabilities, providerName: provider?.name, writable, draft: box, inputRef, setNotice: notice.setNotice });
|
|
98
100
|
const controls = useChatControls({ chatId, info, notice, onRetarget });
|
|
101
|
+
useEffortStepper({
|
|
102
|
+
chatId,
|
|
103
|
+
info,
|
|
104
|
+
model: provider?.models.find((entry) => entry.slug === info.selection.model),
|
|
105
|
+
writable,
|
|
106
|
+
controls,
|
|
107
|
+
setNotice: notice.setNotice
|
|
108
|
+
});
|
|
99
109
|
const guard = promptGuard(withQuote(draft.quote, draft.text));
|
|
100
110
|
const continuable = useContinuableTurn(chatId, info);
|
|
101
111
|
// The stopped turn something went out for, since the chat takes a moment to answer and Continue must not stand there meanwhile.
|
|
102
112
|
const [sentFor, setSentFor] = useState<string | null>(null);
|
|
103
|
-
const extraPrompts =
|
|
113
|
+
const extraPrompts = useHostExtraPrompts(scope.id, chatId);
|
|
104
114
|
const { submit, continueTurn, restoreStashed, toggleStash } = useComposerSubmit({
|
|
105
115
|
chatId,
|
|
106
116
|
writable,
|
|
@@ -144,10 +154,7 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
144
154
|
const pending = usePendingRequests(chatId);
|
|
145
155
|
const compactionOffer = useCompactionOffer(chatId, info, capabilities?.compaction);
|
|
146
156
|
const placeholder = useHintedPlaceholder({ chatId, disabled, readOnly, mentionable: capabilities?.mentions !== false, hasSkills: menus.hasSkills });
|
|
147
|
-
const editorExtensions =
|
|
148
|
-
() => [chipDecorations({ mentions: draft.mentions, skills: draft.skills }), LEAVE_DROPS_TO_THE_CARD],
|
|
149
|
-
[draft.mentions, draft.skills]
|
|
150
|
-
);
|
|
157
|
+
const editorExtensions = [chipDecorations({ mentions: draft.mentions, skills: draft.skills }), LEAVE_DROPS_TO_THE_CARD];
|
|
151
158
|
/* The timeline owns the scroller; this is the one bit of it the composer needs to know. */
|
|
152
159
|
const atEnd = useSyncExternalStore(
|
|
153
160
|
subscribeTimelineEnd,
|
|
@@ -232,6 +239,7 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
232
239
|
{compactionOffer && (
|
|
233
240
|
<ResumeCompactionDock
|
|
234
241
|
tokens={compactionOffer.tokens}
|
|
242
|
+
reason={compactionOffer.reason}
|
|
235
243
|
onCompact={controls.compact}
|
|
236
244
|
onDismiss={() => dismissResumeCompaction(scope, chatId, compactionOffer.turnId)}
|
|
237
245
|
/>
|
package/src/chat/ui/EditDiff.tsx
CHANGED
|
@@ -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';
|
|
@@ -17,9 +16,9 @@ export default function EditDiff({ change, allowEditor = true, maxHeight }: { ch
|
|
|
17
16
|
const host = useChatHost();
|
|
18
17
|
const { t } = useTranslation('agent-chat');
|
|
19
18
|
const loadEngine = host.code.editor;
|
|
20
|
-
const sourceAllowed =
|
|
19
|
+
const sourceAllowed = diffSourceBudget(change.before, change.after);
|
|
21
20
|
const legacyMode = !allowEditor || loadEngine === undefined;
|
|
22
|
-
const legacyAllowed =
|
|
21
|
+
const legacyAllowed = legacyMode && legacyDiffBudget(change.before, change.after);
|
|
23
22
|
const omitted = <DiffOmitted path={change.path} />;
|
|
24
23
|
const fallback = legacyAllowed ? (
|
|
25
24
|
<DiffFallbackBoundary fallback={omitted} resetKeys={[change.path, change.before, change.after]}>
|
|
@@ -14,6 +14,7 @@ import { useChatHost } from '../../host';
|
|
|
14
14
|
import { useChatScope, type ChatScope } from '../../scope';
|
|
15
15
|
import { useChatActions } from '../actions';
|
|
16
16
|
import { useUsageStore } from '../../state/usage';
|
|
17
|
+
import { useHostResumeAtReset } from '../../host-hooks';
|
|
17
18
|
|
|
18
19
|
// A time a limit names reads as a clock today and with its day after that, so the words follow the minute.
|
|
19
20
|
const MINUTE_MS = 60_000;
|
|
@@ -43,11 +44,10 @@ function readUnreadAccounts(scope: ChatScope): void {
|
|
|
43
44
|
* switch off it stands off and says where that switch is.
|
|
44
45
|
*/
|
|
45
46
|
function ResumeAtResetToggle({ chatId, info }: { chatId: string; info: ChatInfo }) {
|
|
46
|
-
const host = useChatHost();
|
|
47
47
|
const { t } = useTranslation('agent-chat');
|
|
48
48
|
const { id } = useChatScope();
|
|
49
49
|
const actions = useChatActions();
|
|
50
|
-
const machineAllows =
|
|
50
|
+
const machineAllows = useHostResumeAtReset(id);
|
|
51
51
|
const set = (on: boolean): void => {
|
|
52
52
|
void actions.configure(chatId, { resumeAtReset: on }).catch(() => undefined);
|
|
53
53
|
};
|
package/src/chat/ui/Markdown.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, memo, useContext,
|
|
1
|
+
import { createContext, memo, useContext, type ReactNode } from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
3
|
import { Zap } from 'lucide-react';
|
|
4
4
|
import ReactMarkdown, { type Components, type ExtraProps, type Options } from 'react-markdown';
|
|
@@ -85,7 +85,7 @@ function languageOf(className: string | undefined): string {
|
|
|
85
85
|
return /language-([\w-]+)/.exec(className ?? '')?.[1] ?? 'text';
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
const INLINE_CODE = 'rounded-sm bg-surface-sunken px-1 py-px font-mono text-code';
|
|
88
|
+
const INLINE_CODE = 'rounded-sm bg-surface-sunken px-1 py-px font-mono text-code box-decoration-clone';
|
|
89
89
|
|
|
90
90
|
/*
|
|
91
91
|
* A path an agent wrote down. It keeps the shape of the code chip it would otherwise be, so a sentence
|
|
@@ -99,7 +99,7 @@ function FileLink({ target, className, children }: { target: FileRef; className?
|
|
|
99
99
|
// A button is not selectable on its own, and a thread is copied as often as it is clicked.
|
|
100
100
|
<button
|
|
101
101
|
type="button"
|
|
102
|
-
className={clsx('cursor-pointer select-text', className)}
|
|
102
|
+
className={clsx('cursor-pointer select-text text-left', className)}
|
|
103
103
|
data-file-path={target.path}
|
|
104
104
|
data-file-line={target.line}
|
|
105
105
|
data-file-column={target.column}
|
|
@@ -121,8 +121,10 @@ function InlineCode({ text }: { text: string }) {
|
|
|
121
121
|
return <code className={INLINE_CODE}>{text}</code>;
|
|
122
122
|
}
|
|
123
123
|
return (
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
// The chip sits on a code inside the button, since a button is always a box of its own and its
|
|
125
|
+
// background would fill the whole box once a long path wraps.
|
|
126
|
+
<FileLink target={target} className="text-accent">
|
|
127
|
+
<code className={clsx(INLINE_CODE, 'hover:underline')}>{text}</code>
|
|
126
128
|
</FileLink>
|
|
127
129
|
);
|
|
128
130
|
}
|
|
@@ -393,7 +395,7 @@ export const ReplyMarkdown = memo(function ReplyMarkdown({
|
|
|
393
395
|
arriving?: boolean;
|
|
394
396
|
reply?: ShellCodeBlockReply;
|
|
395
397
|
}) {
|
|
396
|
-
const blocks =
|
|
398
|
+
const blocks = splitMarkdownBlocks(text);
|
|
397
399
|
return (
|
|
398
400
|
<div className="chat-markdown prose prose-sm max-w-none text-sm" data-arriving={arriving || undefined}>
|
|
399
401
|
{blocks.map((block, index) =>
|
|
@@ -420,7 +422,7 @@ export const ReplyMarkdown = memo(function ReplyMarkdown({
|
|
|
420
422
|
* it was, and the files and skills picked in the composer are chips again.
|
|
421
423
|
*/
|
|
422
424
|
export const MessageMarkdown = memo(function MessageMarkdown({ text, mentions, skills }: { text: string; mentions?: string[]; skills?: string[] }) {
|
|
423
|
-
const rehypePlugins
|
|
425
|
+
const rehypePlugins: [typeof rehypeChips, ChipOptions][] = [[rehypeChips, { mentions, skills }]];
|
|
424
426
|
return (
|
|
425
427
|
<div className="chat-markdown prose prose-sm max-w-none text-sm">
|
|
426
428
|
<ReactMarkdown remarkPlugins={MESSAGE_PLUGINS} rehypePlugins={rehypePlugins} components={messageComponents}>
|
|
@@ -449,7 +451,7 @@ export const Markdown = memo(function Markdown({
|
|
|
449
451
|
rehypePlugins?: Options['rehypePlugins'];
|
|
450
452
|
componentOverrides?: Components;
|
|
451
453
|
}) {
|
|
452
|
-
const renderComponents =
|
|
454
|
+
const renderComponents = { ...(fileLinks ? components : plainComponents), ...componentOverrides };
|
|
453
455
|
return (
|
|
454
456
|
<div className="chat-markdown prose prose-sm max-w-none text-sm" data-muted={muted || undefined}>
|
|
455
457
|
<ReactMarkdown remarkPlugins={breaks ? PLUGINS_WITH_BREAKS : PLUGINS} rehypePlugins={rehypePlugins} components={renderComponents}>
|
|
@@ -2,11 +2,11 @@ import { useContext, useEffect, useId, useRef, useState } from 'react';
|
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { Code, Workflow } from 'lucide-react';
|
|
4
4
|
import { IconButton } from '@adecore/ui';
|
|
5
|
-
import { useChatHost } from '../../host';
|
|
6
5
|
import { CodeBlock } from './CodeBlock';
|
|
7
6
|
import { CodeStreamingContext } from './code-streaming';
|
|
8
7
|
import { MERMAID_TOKENS, mermaidConfig, sizedSvg, svgDataUrl, type MermaidColors, type SizedSvg } from './mermaid';
|
|
9
8
|
import { watchTheme } from './theme-watch';
|
|
9
|
+
import { useHostCodeMode } from '../../host-hooks';
|
|
10
10
|
|
|
11
11
|
type Drawing = { state: 'drawing' } | { state: 'drawn'; image: SizedSvg } | { state: 'failed' };
|
|
12
12
|
|
|
@@ -63,10 +63,9 @@ async function drawDiagram(id: string, source: string, look: DiagramLook, dark:
|
|
|
63
63
|
* draw, it is the code block it would otherwise be.
|
|
64
64
|
*/
|
|
65
65
|
export function MermaidBlock({ code }: { code: string }) {
|
|
66
|
-
const host = useChatHost();
|
|
67
66
|
const { t } = useTranslation('agent-chat');
|
|
68
67
|
const streaming = useContext(CodeStreamingContext);
|
|
69
|
-
const dark =
|
|
68
|
+
const dark = useHostCodeMode() === 'dark';
|
|
70
69
|
const id = `mermaid-${useId().replace(/[^\w-]/g, '')}`;
|
|
71
70
|
const frame = useRef<HTMLDivElement>(null);
|
|
72
71
|
const [source, setSource] = useState(false);
|
package/src/chat/ui/Pickers.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useContext, useEffect,
|
|
1
|
+
import { useContext, useEffect, useRef, useState, type ReactNode, type RefObject } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import { Bookmark, ChevronDown, ChevronRight, Search, Trash2 } from 'lucide-react';
|
|
3
|
+
import { Bookmark, ChevronDown, ChevronRight, Search, Star, Trash2 } from 'lucide-react';
|
|
4
4
|
import type { AgentKind, ModelSelection, ProviderInfo, UsageProvider } from '@adecore/agent-contracts';
|
|
5
5
|
import { AgentIcon } from '../../agents/AgentIcon';
|
|
6
6
|
import { ProviderLogo } from '../../agents/ProviderLogo';
|
|
@@ -11,8 +11,10 @@ import { ChatScopeContext } from '../../scope';
|
|
|
11
11
|
import { useReportedModels } from '../../state/chats';
|
|
12
12
|
import { ModelMarks } from './ModelMarks';
|
|
13
13
|
import { pickerEntriesOf, type PickerEntry } from '../logic/picker-entries';
|
|
14
|
+
import { favoriteModelsOf, toggleFavoriteModel, useChatPreferences } from '../preferences';
|
|
14
15
|
import { forgetStashed, STASH_SHORTCUT, useStash, type StashedPrompt } from '../stash';
|
|
15
16
|
import { Icon, IconButton, Menu, Popover, Tooltip } from '@adecore/ui';
|
|
17
|
+
import { useHostMachineLabel } from '../../host-hooks';
|
|
16
18
|
|
|
17
19
|
const triggerClass =
|
|
18
20
|
'flex h-7 shrink-0 items-center gap-1 whitespace-nowrap rounded-md px-2 text-xs text-text-muted hover:bg-surface-hover hover:text-text data-[popup-open]:bg-surface-active data-[popup-open]:text-text';
|
|
@@ -74,12 +76,13 @@ export function ModelPicker({
|
|
|
74
76
|
const grouped = providers.length > 1;
|
|
75
77
|
const trimmed = query.trim().toLowerCase();
|
|
76
78
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
const favorites = useChatPreferences((preferences) => preferences.favoriteModels);
|
|
80
|
+
const starredHere = (row: PickerEntry | undefined): boolean => row?.kind === 'model' && favorites[row.provider.kind]?.includes(row.model.slug) === true;
|
|
81
|
+
const entries = pickerEntriesOf(providers, trimmed, legacyOpen, expanded, { provider, model: selection.model }, (kind) =>
|
|
82
|
+
favoriteModelsOf(favorites, kind)
|
|
80
83
|
);
|
|
81
84
|
const scope = useContext(ChatScopeContext);
|
|
82
|
-
const machine =
|
|
85
|
+
const machine = useHostMachineLabel(scope?.id ?? '');
|
|
83
86
|
const reported = useReportedModels(provider);
|
|
84
87
|
const requirementOf = (entry: PickerEntry & { kind: 'model' }) => modelRequirementOf(entry.model, entry.provider, reported);
|
|
85
88
|
// A CLI that is too old blocks all its models; one way to its settings under the group says it once.
|
|
@@ -226,17 +229,21 @@ export function ModelPicker({
|
|
|
226
229
|
const first = grouped && (previous === undefined || previous.kind === 'legacy' || previous.provider.kind !== entry.provider.kind);
|
|
227
230
|
// The makers read as groups only where the whole list is shown, so a short list is not cut by labels.
|
|
228
231
|
const vendorOpen = trimmed !== '' || expanded.has(entry.provider.kind);
|
|
232
|
+
// The starred rows lead their CLI without a maker's label; the makers start after them.
|
|
229
233
|
const vendorFirst =
|
|
230
234
|
vendorOpen &&
|
|
231
235
|
entry.model.vendor !== undefined &&
|
|
236
|
+
!starredHere(entry) &&
|
|
232
237
|
(previous === undefined ||
|
|
233
238
|
previous.kind !== 'model' ||
|
|
234
239
|
previous.provider.kind !== entry.provider.kind ||
|
|
240
|
+
starredHere(previous) ||
|
|
235
241
|
previous.model.vendor !== entry.model.vendor);
|
|
236
242
|
const requirement = requirementOf(entry);
|
|
237
243
|
const next = entries[i + 1];
|
|
238
244
|
const lastOfGroup = next === undefined || next.kind !== 'model' || next.provider.kind !== entry.provider.kind;
|
|
239
245
|
const chosen = entry.provider.kind === provider && entry.model.slug === selection.model;
|
|
246
|
+
const starred = starredHere(entry);
|
|
240
247
|
return (
|
|
241
248
|
<div key={`${entry.provider.kind}/${entry.model.slug}`}>
|
|
242
249
|
{first && (
|
|
@@ -246,30 +253,43 @@ export function ModelPicker({
|
|
|
246
253
|
</Menu.Label>
|
|
247
254
|
)}
|
|
248
255
|
{vendorFirst && <Menu.Label>{entry.model.vendor}</Menu.Label>}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
<span className="
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
{machine === null
|
|
268
|
-
? t('pickers.model.requiresHere', { cli: requirement.cli, version: requirement.minVersion })
|
|
269
|
-
: t('pickers.model.requires', { cli: requirement.cli, version: requirement.minVersion, machine })}
|
|
256
|
+
{/* The star sits beside the option rather than in it, since a button holds no other button. */}
|
|
257
|
+
<div className="flex items-start gap-0.5">
|
|
258
|
+
<button
|
|
259
|
+
role="option"
|
|
260
|
+
aria-selected={chosen}
|
|
261
|
+
aria-disabled={requirement !== null || undefined}
|
|
262
|
+
data-active={i === index}
|
|
263
|
+
className={`cursor-row flex min-w-0 grow flex-col items-stretch gap-0.5 rounded-md px-2.5 py-1.5 text-left text-sm ${requirement === null ? 'text-text-muted' : 'text-text-faint'}`}
|
|
264
|
+
onMouseEnter={() => setIndex(i)}
|
|
265
|
+
onClick={() => choose(entry)}
|
|
266
|
+
>
|
|
267
|
+
<span className="flex w-full items-center gap-2">
|
|
268
|
+
<Menu.Check kind="radio" checked={chosen} />
|
|
269
|
+
{/* Wrapped rather than cut, so a long name a provider gives is read whole. */}
|
|
270
|
+
<span className="min-w-0 break-words">{entry.model.name}</span>
|
|
271
|
+
<ModelMarks model={entry.model} />
|
|
272
|
+
<span className="grow" />
|
|
273
|
+
{entry.model.legacy && <span className="text-xs text-text-faint">{t('pickers.model.legacy')}</span>}
|
|
270
274
|
</span>
|
|
271
|
-
|
|
272
|
-
|
|
275
|
+
{requirement !== null && (
|
|
276
|
+
<span className="pl-6 text-xs text-text-faint">
|
|
277
|
+
{machine === null
|
|
278
|
+
? t('pickers.model.requiresHere', { cli: requirement.cli, version: requirement.minVersion })
|
|
279
|
+
: t('pickers.model.requires', { cli: requirement.cli, version: requirement.minVersion, machine })}
|
|
280
|
+
</span>
|
|
281
|
+
)}
|
|
282
|
+
</button>
|
|
283
|
+
<IconButton
|
|
284
|
+
icon={Star}
|
|
285
|
+
size="sm"
|
|
286
|
+
label={starred ? t('pickers.model.unstar') : t('pickers.model.star')}
|
|
287
|
+
aria-pressed={starred}
|
|
288
|
+
iconClassName={starred ? 'fill-current text-accent' : undefined}
|
|
289
|
+
className="mt-0.5 shrink-0"
|
|
290
|
+
onClick={() => toggleFavoriteModel(entry.provider.kind, entry.model.slug)}
|
|
291
|
+
/>
|
|
292
|
+
</div>
|
|
273
293
|
{lastOfGroup && blockedKinds.has(entry.provider.kind) && (
|
|
274
294
|
<button
|
|
275
295
|
type="button"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect,
|
|
1
|
+
import { useEffect, useRef, type ReactNode } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { ArrowDown, Hand, MessageCircleQuestionMark } from 'lucide-react';
|
|
4
4
|
import type { HoldRelease } from '../prompt-hold';
|
|
@@ -12,6 +12,7 @@ import { useChatHost } from '../../host';
|
|
|
12
12
|
import { useChatScope } from '../../scope';
|
|
13
13
|
import { useChatActions } from '../actions';
|
|
14
14
|
import { Button, Icon } from '@adecore/ui';
|
|
15
|
+
import { useHostExtraPrompts } from '../../host-hooks';
|
|
15
16
|
|
|
16
17
|
/* Keeps a prompt card behind the draft while the person types; `released` says what lifted the hold last. */
|
|
17
18
|
export interface PromptHold {
|
|
@@ -57,14 +58,11 @@ export function PromptComposer({
|
|
|
57
58
|
const actions = useChatActions();
|
|
58
59
|
const elsewhere = onShowElsewhere !== undefined;
|
|
59
60
|
// A card the app raises itself waits in the chat beside the CLI's own requests.
|
|
60
|
-
const extra =
|
|
61
|
-
const subjects =
|
|
62
|
-
(): PromptSubject
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
],
|
|
66
|
-
[pending, extra, chatId]
|
|
67
|
-
);
|
|
61
|
+
const extra = useHostExtraPrompts(id, chatId);
|
|
62
|
+
const subjects: PromptSubject[] = [
|
|
63
|
+
...pending.map((item): PromptSubject => ({ kind: 'chat', nodeId: chatId, item })),
|
|
64
|
+
...extra.map((prompt): PromptSubject => ({ kind: 'host', nodeId: chatId, prompt }))
|
|
65
|
+
];
|
|
68
66
|
const session = usePromptSession({ prompts: subjects, idOf: promptIdOf, pick: pickSubject, disabled });
|
|
69
67
|
const ref = useRef<HTMLDivElement>(null);
|
|
70
68
|
const { active, activeId: activeKey } = session;
|
|
@@ -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);
|