@adecore/agents-react 0.31.11 → 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 +1946 -163
- 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 +35 -0
- package/dist/locales/en/agent-chat.js +747 -712
- 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 +35 -0
- package/dist/locales/nl/agent-chat.js +747 -712
- 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 -25
- 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.d.ts +1 -0
- package/dist/state/chats.js +408 -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 +20 -7
- 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 +135 -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 +127 -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/prompts/ui/ApprovalBody.tsx +3 -1
- 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 +7 -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
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { EffortStep } from './logic/effort';
|
|
2
|
+
|
|
3
|
+
type Stepper = (step: EffortStep) => void;
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* A command outside the composer, such as a menu row or a shortcut of the app, steps the reasoning effort
|
|
7
|
+
* of a chat whose composer is on screen. Keyed with the scope's `keyOf`, like `sendFromComposer`. One chat can
|
|
8
|
+
* have two composers on screen; the one that came last steps, and the one before takes over when it goes.
|
|
9
|
+
*/
|
|
10
|
+
const steppers = new Map<string, Stepper[]>();
|
|
11
|
+
|
|
12
|
+
export function registerEffortStepper(key: string, stepper: Stepper): () => void {
|
|
13
|
+
steppers.set(key, [...(steppers.get(key) ?? []), stepper]);
|
|
14
|
+
return () => {
|
|
15
|
+
const left = (steppers.get(key) ?? []).filter((entry) => entry !== stepper);
|
|
16
|
+
if (left.length > 0) {
|
|
17
|
+
steppers.set(key, left);
|
|
18
|
+
} else {
|
|
19
|
+
steppers.delete(key);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* False when that chat has no composer on screen. At either end of the model's efforts the composer says so and nothing changes. */
|
|
25
|
+
export function stepEffortFromComposer(key: string, step: EffortStep): boolean {
|
|
26
|
+
const stepper = steppers.get(key)?.at(-1);
|
|
27
|
+
if (stepper === undefined) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
stepper(step);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
package/src/chat/forks.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
2
1
|
import type { ChatInfo } from '@adecore/agent-contracts';
|
|
3
2
|
import { forkIdsAfter, forkedTurnIds } from './logic/fork';
|
|
4
3
|
import { useChatScope } from '../scope';
|
|
@@ -40,15 +39,15 @@ function splitIds(joined: string): string[] {
|
|
|
40
39
|
/* The turns of this chat a fork went on after, for the thread to put a line under. */
|
|
41
40
|
export function useForkedTurns(chatId: string): ReadonlySet<string> {
|
|
42
41
|
const { owns } = useChatScope();
|
|
43
|
-
const select =
|
|
42
|
+
const select = joinedForks(owns, (infos) => forkedTurnIds(infos, chatId));
|
|
44
43
|
const joined = useChats(select);
|
|
45
|
-
return
|
|
44
|
+
return new Set(splitIds(joined));
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
/* The forks that went on after this turn of this chat. */
|
|
49
48
|
export function useForkIdsAfter(chatId: string, turnId: string): string[] {
|
|
50
49
|
const { owns } = useChatScope();
|
|
51
|
-
const select =
|
|
50
|
+
const select = joinedForks(owns, (infos) => forkIdsAfter(infos, chatId, turnId));
|
|
52
51
|
const joined = useChats(select);
|
|
53
|
-
return
|
|
52
|
+
return splitIds(joined);
|
|
54
53
|
}
|
package/src/chat/index.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// Public API: exports.test.ts snapshots this list, so a name added or dropped here is an API change.
|
|
2
|
-
export { type AccountChoice, useAccountChoice } from './account-choice';
|
|
2
|
+
export { type AccountChoice, useAccountChoice, type NewAccountChoice, useNewAccountChoice } from './account-choice';
|
|
3
3
|
export { transportActions, actionsOf, useChatActions } from './actions';
|
|
4
4
|
export { type ChatSendExtras, ChatClient } from './chat-client';
|
|
5
5
|
export type { ProjectChat } from './chat-references';
|
|
6
6
|
export { sendFromComposer, type ComposerSend } from './composer-send';
|
|
7
|
+
export { stepEffortFromComposer } from './composer-effort';
|
|
8
|
+
export type { EffortStep } from './logic/effort';
|
|
7
9
|
export { EMPTY_DRAFT, useDrafts, readDraft, writeDraft, offerDraft, type ChatDraft } from './drafts';
|
|
8
10
|
export { stripAnsi } from './logic/ansi';
|
|
9
11
|
export {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ModelInfo, ModelOptionChoice, ModelSelection } from '@adecore/agent-contracts';
|
|
2
|
+
|
|
3
|
+
/* The option a catalog names the reasoning effort by; its choices run from the least to the most. */
|
|
4
|
+
export const EFFORT_OPTION = 'effort';
|
|
5
|
+
|
|
6
|
+
/* `up` is one choice more effort, `down` one less. */
|
|
7
|
+
export type EffortStep = 'up' | 'down';
|
|
8
|
+
|
|
9
|
+
export type EffortStepResult =
|
|
10
|
+
| { kind: 'stepped'; selection: ModelSelection; choice: ModelOptionChoice }
|
|
11
|
+
/* Already at the end of the model's choices that way, which `choice` names. */
|
|
12
|
+
| { kind: 'edge'; choice: ModelOptionChoice }
|
|
13
|
+
/* The model has no effort to step, or the catalog does not know the model. */
|
|
14
|
+
| { kind: 'none' };
|
|
15
|
+
|
|
16
|
+
/* One step of effort within what the model offers. A value the model no longer offers counts as its default. */
|
|
17
|
+
export function stepEffort(model: ModelInfo | undefined, selection: ModelSelection, step: EffortStep): EffortStepResult {
|
|
18
|
+
const option = model?.options.find((candidate) => candidate.id === EFFORT_OPTION);
|
|
19
|
+
if (option === undefined || option.type !== 'select') {
|
|
20
|
+
return { kind: 'none' };
|
|
21
|
+
}
|
|
22
|
+
const chosen = selection.options[EFFORT_OPTION];
|
|
23
|
+
const index = option.choices.findIndex((choice) => choice.id === chosen);
|
|
24
|
+
const from = index === -1 ? option.choices.findIndex((choice) => choice.id === option.defaultChoice) : index;
|
|
25
|
+
const current = Math.max(0, from);
|
|
26
|
+
const next = current + (step === 'up' ? 1 : -1);
|
|
27
|
+
const choice = option.choices[next];
|
|
28
|
+
if (choice === undefined) {
|
|
29
|
+
return { kind: 'edge', choice: option.choices[current]! };
|
|
30
|
+
}
|
|
31
|
+
return { kind: 'stepped', selection: { ...selection, options: { ...selection.options, [EFFORT_OPTION]: choice.id } }, choice };
|
|
32
|
+
}
|
|
@@ -17,6 +17,11 @@ export interface PickerCurrent {
|
|
|
17
17
|
model: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/* The models a person starred, per CLI. */
|
|
21
|
+
export type FavoritesOf = (kind: AgentKind) => ReadonlySet<string>;
|
|
22
|
+
|
|
23
|
+
const NO_FAVORITES: FavoritesOf = () => new Set();
|
|
24
|
+
|
|
20
25
|
function matches(model: ModelInfo, query: string): boolean {
|
|
21
26
|
return model.name.toLowerCase().includes(query) || model.slug.toLowerCase().includes(query) || (model.vendor?.toLowerCase().includes(query) ?? false);
|
|
22
27
|
}
|
|
@@ -40,12 +45,31 @@ export function modelsByVendor(models: ModelInfo[]): ModelInfo[] {
|
|
|
40
45
|
});
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
/* The models
|
|
44
|
-
export function
|
|
48
|
+
/* The starred models first, in the order they came, then the rest by maker. */
|
|
49
|
+
export function favoritesFirst(models: ModelInfo[], favorites: ReadonlySet<string>): ModelInfo[] {
|
|
50
|
+
if (favorites.size === 0) {
|
|
51
|
+
return modelsByVendor(models);
|
|
52
|
+
}
|
|
53
|
+
return [...models.filter((model) => favorites.has(model.slug)), ...modelsByVendor(models.filter((model) => !favorites.has(model.slug)))];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* The models a long list shows before it is opened: the starred ones, the default and the one that is picked.
|
|
58
|
+
* Null when the list is short enough to show whole.
|
|
59
|
+
*/
|
|
60
|
+
export function shortlistOf(
|
|
61
|
+
kind: AgentKind,
|
|
62
|
+
models: ModelInfo[],
|
|
63
|
+
current: PickerCurrent | null,
|
|
64
|
+
favorites: ReadonlySet<string> = new Set()
|
|
65
|
+
): ModelInfo[] | null {
|
|
45
66
|
if (models.length <= COMPACT_MODEL_LIMIT) {
|
|
46
67
|
return null;
|
|
47
68
|
}
|
|
48
|
-
return
|
|
69
|
+
return favoritesFirst(
|
|
70
|
+
models.filter((model) => model.isDefault || favorites.has(model.slug) || (current?.provider === kind && current.model === model.slug)),
|
|
71
|
+
favorites
|
|
72
|
+
);
|
|
49
73
|
}
|
|
50
74
|
|
|
51
75
|
/* Whether the providers offer enough models together that a search is quicker than reading the list. */
|
|
@@ -53,11 +77,21 @@ export function wantsModelSearch(providers: readonly ProviderInfo[]): boolean {
|
|
|
53
77
|
return providers.reduce((count, entry) => count + entry.models.length, 0) > COMPACT_MODEL_LIMIT;
|
|
54
78
|
}
|
|
55
79
|
|
|
56
|
-
/* Every model of every provider that a search finds, legacy ones included, by maker; a provider with none is left out. */
|
|
57
|
-
export function searchModels(
|
|
80
|
+
/* Every model of every provider that a search finds, legacy ones included, starred first and then by maker; a provider with none is left out. */
|
|
81
|
+
export function searchModels(
|
|
82
|
+
providers: readonly ProviderInfo[],
|
|
83
|
+
query: string,
|
|
84
|
+
favoritesOf: FavoritesOf = NO_FAVORITES
|
|
85
|
+
): Array<{ entry: ProviderInfo; models: ModelInfo[] }> {
|
|
58
86
|
const needle = query.trim().toLowerCase();
|
|
59
87
|
return providers
|
|
60
|
-
.map((entry) => ({
|
|
88
|
+
.map((entry) => ({
|
|
89
|
+
entry,
|
|
90
|
+
models: favoritesFirst(
|
|
91
|
+
entry.models.filter((model) => matches(model, needle)),
|
|
92
|
+
favoritesOf(entry.kind)
|
|
93
|
+
)
|
|
94
|
+
}))
|
|
61
95
|
.filter((group) => group.models.length > 0);
|
|
62
96
|
}
|
|
63
97
|
|
|
@@ -67,17 +101,18 @@ const NONE: ReadonlySet<AgentKind> = new Set();
|
|
|
67
101
|
* The rows of the model list. A model an old CLI cannot run is a row like any other, so a search for
|
|
68
102
|
* only that model still finds the row that explains why it cannot be picked. A search looks through
|
|
69
103
|
* every model of every provider; without one, a provider with many models shows a short list and a row
|
|
70
|
-
* that opens the rest, grouped by maker.
|
|
104
|
+
* that opens the rest, grouped by maker. A provider's starred models come first wherever it lists them.
|
|
71
105
|
*/
|
|
72
106
|
export function pickerEntriesOf(
|
|
73
107
|
providers: readonly ProviderInfo[],
|
|
74
108
|
query: string,
|
|
75
109
|
legacyOpen: boolean,
|
|
76
110
|
expanded: ReadonlySet<AgentKind> = NONE,
|
|
77
|
-
current: PickerCurrent | null = null
|
|
111
|
+
current: PickerCurrent | null = null,
|
|
112
|
+
favoritesOf: FavoritesOf = NO_FAVORITES
|
|
78
113
|
): PickerEntry[] {
|
|
79
114
|
const rowsOf = (entry: ProviderInfo, models: ModelInfo[]): PickerEntry[] =>
|
|
80
|
-
|
|
115
|
+
favoritesFirst(models, favoritesOf(entry.kind)).map((model) => ({ kind: 'model', provider: entry, model }));
|
|
81
116
|
if (query !== '') {
|
|
82
117
|
return providers.flatMap((entry) =>
|
|
83
118
|
rowsOf(
|
|
@@ -91,7 +126,7 @@ export function pickerEntriesOf(
|
|
|
91
126
|
);
|
|
92
127
|
const live = providers.flatMap((entry): PickerEntry[] => {
|
|
93
128
|
const models = entry.models.filter((model) => !model.legacy);
|
|
94
|
-
const shortlist = shortlistOf(entry.kind, models, current);
|
|
129
|
+
const shortlist = shortlistOf(entry.kind, models, current, favoritesOf(entry.kind));
|
|
95
130
|
if (shortlist === null) {
|
|
96
131
|
return rowsOf(entry, models);
|
|
97
132
|
}
|
|
@@ -9,31 +9,39 @@ export const RESUME_COMPACTION_TOKENS = 100_000;
|
|
|
9
9
|
/* The longest prompt cache Claude keeps is an hour, so past this the whole thread is read again anyway. */
|
|
10
10
|
export const RESUME_COMPACTION_IDLE_MS = 70 * 60_000;
|
|
11
11
|
|
|
12
|
+
/* `cache-expired` when the CLI named when its prompt cache goes cold, `idle` when only the silence says so. */
|
|
13
|
+
export type ResumeCompactionReason = 'cache-expired' | 'idle';
|
|
14
|
+
|
|
12
15
|
export interface ResumeCompactionOffer {
|
|
13
16
|
/* The turn the offer is about, and the key "Keep full history" is remembered under. */
|
|
14
17
|
turnId: string;
|
|
15
18
|
tokens: number;
|
|
19
|
+
reason: ResumeCompactionReason;
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
return items.findLast((item): item is Extract<ChatItem, { kind: 'turn' }> => item.kind === 'turn') ?? null;
|
|
20
|
-
}
|
|
22
|
+
type Turn = Extract<ChatItem, { kind: 'turn' }>;
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
export function resumeCompactionOffer({
|
|
24
|
-
info,
|
|
25
|
-
compaction,
|
|
26
|
-
items,
|
|
27
|
-
dismissedTurnId,
|
|
28
|
-
now
|
|
29
|
-
}: {
|
|
24
|
+
interface OfferInput {
|
|
30
25
|
info: ChatInfo;
|
|
31
26
|
/* Absent until the host answered `provider.list`. */
|
|
32
27
|
compaction: ProviderCapabilities['compaction'] | undefined;
|
|
33
28
|
items: readonly ChatItem[];
|
|
34
29
|
dismissedTurnId: string | null;
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* The newest turn by when it started, so a thread that arrives out of order still names the turn that ran last. */
|
|
33
|
+
function lastTurn(items: readonly ChatItem[]): Turn | null {
|
|
34
|
+
let last: Turn | null = null;
|
|
35
|
+
for (const item of items) {
|
|
36
|
+
if (item.kind === 'turn' && (last === null || item.createdAt >= last.createdAt)) {
|
|
37
|
+
last = item;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return last;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* The turn an offer would be about, or null while nothing could be offered whatever the time. */
|
|
44
|
+
function offerableTurn({ info, compaction, items, dismissedTurnId }: OfferInput): Turn | null {
|
|
37
45
|
if (compaction === undefined || compaction === 'none') {
|
|
38
46
|
return null;
|
|
39
47
|
}
|
|
@@ -47,9 +55,6 @@ export function resumeCompactionOffer({
|
|
|
47
55
|
if (turn === null || turn.endedAt === null || turn.id === dismissedTurnId) {
|
|
48
56
|
return null;
|
|
49
57
|
}
|
|
50
|
-
if (now - turn.endedAt < RESUME_COMPACTION_IDLE_MS) {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
58
|
/*
|
|
54
59
|
* `contextTokens` keeps the old number until the next request, so without this the offer would
|
|
55
60
|
* come straight back after a compaction. A native compaction is a turn without a message of the
|
|
@@ -58,5 +63,78 @@ export function resumeCompactionOffer({
|
|
|
58
63
|
if (items.some((item) => item.kind === 'compaction' && item.turnId === turn.id)) {
|
|
59
64
|
return null;
|
|
60
65
|
}
|
|
61
|
-
return
|
|
66
|
+
return turn;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* A silence longer than any cache lives stands in for an expiry from a CLI that names none. */
|
|
70
|
+
function dueAtOf(info: ChatInfo, endedAt: number): number {
|
|
71
|
+
return info.usage.cacheExpiresAt ?? endedAt + RESUME_COMPACTION_IDLE_MS;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/*
|
|
75
|
+
* When the next message would pay for the whole thread again, or null while nothing could be offered,
|
|
76
|
+
* so a surface knows whether to keep a timer.
|
|
77
|
+
*/
|
|
78
|
+
export function resumeCompactionDueAt(input: OfferInput): number | null {
|
|
79
|
+
const endedAt = offerableTurn(input)?.endedAt ?? null;
|
|
80
|
+
return endedAt === null ? null : dueAtOf(input.info, endedAt);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Whether to offer compacting before the next message. */
|
|
84
|
+
export function resumeCompactionOffer(input: OfferInput & { now: number }): ResumeCompactionOffer | null {
|
|
85
|
+
const turn = offerableTurn(input);
|
|
86
|
+
if (turn === null || turn.endedAt === null || input.now < dueAtOf(input.info, turn.endedAt)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
turnId: turn.id,
|
|
91
|
+
tokens: input.info.usage.contextTokens,
|
|
92
|
+
reason: input.info.usage.cacheExpiresAt === undefined ? 'idle' : 'cache-expired'
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* What `watchDueAt` keeps time with; a test hands in a fake. */
|
|
97
|
+
export interface DueClock {
|
|
98
|
+
now(): number;
|
|
99
|
+
setTimer(run: () => void, ms: number): unknown;
|
|
100
|
+
clearTimer(timer: unknown): void;
|
|
101
|
+
/* Calls `wake` when the page may have slept or sat in the background, and answers how to stop. */
|
|
102
|
+
onWake(wake: () => void): () => void;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
* Calls `due` with the time once `dueAt` passed. A timer can fire a few milliseconds early, and one that runs on a
|
|
107
|
+
* monotonic clock fires late after the system slept, so an early timer is armed again and a wake looks at once.
|
|
108
|
+
*/
|
|
109
|
+
export function watchDueAt(dueAt: number, due: (now: number) => void, clock: DueClock): () => void {
|
|
110
|
+
let timer: unknown = null;
|
|
111
|
+
let done = false;
|
|
112
|
+
const check = (): void => {
|
|
113
|
+
if (timer !== null) {
|
|
114
|
+
clock.clearTimer(timer);
|
|
115
|
+
timer = null;
|
|
116
|
+
}
|
|
117
|
+
if (done) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const now = clock.now();
|
|
121
|
+
if (now >= dueAt) {
|
|
122
|
+
done = true;
|
|
123
|
+
due(now);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
timer = clock.setTimer(() => {
|
|
127
|
+
timer = null;
|
|
128
|
+
check();
|
|
129
|
+
}, dueAt - now);
|
|
130
|
+
};
|
|
131
|
+
const stopWake = clock.onWake(check);
|
|
132
|
+
check();
|
|
133
|
+
return () => {
|
|
134
|
+
done = true;
|
|
135
|
+
stopWake();
|
|
136
|
+
if (timer !== null) {
|
|
137
|
+
clock.clearTimer(timer);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
62
140
|
}
|
|
@@ -125,12 +125,13 @@ export function summarizeGroup(tools: ChatToolItem[], looks?: readonly CommandLo
|
|
|
125
125
|
}
|
|
126
126
|
const names = new Set(tools.map((tool) => tool.name));
|
|
127
127
|
const only = names.size === 1 ? tools[0]!.name : null;
|
|
128
|
-
|
|
128
|
+
const failed = tools.filter(failedFileChange);
|
|
129
|
+
if (only !== null && toolEntry(only)?.grouped === true && failed.length === 0) {
|
|
129
130
|
return i18next.t(`agent-chat:group.tools.${only}`, { count: tools.length });
|
|
130
131
|
}
|
|
131
132
|
// Counts the files rather than the calls: three edits to one file are one file edited.
|
|
132
|
-
if (tools.every((tool) => isFileChange(tool.name))) {
|
|
133
|
-
const paths = new Set(tools.map(editedPath));
|
|
133
|
+
if (tools.every((tool) => isFileChange(tool.name)) && failed.length < tools.length) {
|
|
134
|
+
const paths = new Set(tools.filter((tool) => !failedFileChange(tool)).map(editedPath));
|
|
134
135
|
return i18next.t('agent-chat:group.edited', { count: paths.size });
|
|
135
136
|
}
|
|
136
137
|
if (only !== null) {
|
|
@@ -139,6 +140,11 @@ export function summarizeGroup(tools: ChatToolItem[], looks?: readonly CommandLo
|
|
|
139
140
|
return i18next.t('agent-chat:group.toolCalls', { count: tools.length });
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
/* A file change that failed or was refused changed nothing: it counts as a call, never as an edit. */
|
|
144
|
+
function failedFileChange(tool: ChatToolItem): boolean {
|
|
145
|
+
return isFileChange(tool.name) && tool.state === 'error';
|
|
146
|
+
}
|
|
147
|
+
|
|
142
148
|
function editedPath(tool: ChatToolItem): string {
|
|
143
149
|
return (tool.input as { file_path?: string })?.file_path ?? tool.id;
|
|
144
150
|
}
|
|
@@ -168,6 +174,8 @@ export function summarizeTurn(tools: readonly ChatToolItem[], looks?: readonly C
|
|
|
168
174
|
if (run !== null && group !== undefined) {
|
|
169
175
|
// Keyed by the program too, so two looks with a key in common keep their own sentences.
|
|
170
176
|
add(`look:${run.look.program}:${group.key}`, group.sentence);
|
|
177
|
+
} else if (failedFileChange(tool)) {
|
|
178
|
+
other += 1;
|
|
171
179
|
} else if (isFileChange(tool.name)) {
|
|
172
180
|
edited.add(editedPath(tool));
|
|
173
181
|
add('agent-chat:group.edited', (count) => i18next.t('agent-chat:group.edited', { count }), edited.size);
|
package/src/chat/preferences.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface ChatPreferences {
|
|
|
20
20
|
readingWidth: ChatReadingWidth;
|
|
21
21
|
/* Which key sends from the composer. It never reaches a machine either. */
|
|
22
22
|
sendKey: ChatSendKey;
|
|
23
|
+
/* The models a person starred, per CLI, which the pickers list first. Neither does this. */
|
|
24
|
+
favoriteModels: Partial<Record<AgentKind, string[]>>;
|
|
23
25
|
/* When a person last changed any of it; what settles whose pick a machine uses when several clients told it one. */
|
|
24
26
|
changedAt: number;
|
|
25
27
|
}
|
|
@@ -32,9 +34,23 @@ export const DEFAULT_CHAT_PREFERENCES: ChatPreferences = {
|
|
|
32
34
|
accountByMachine: {},
|
|
33
35
|
readingWidth: DEFAULT_READING_WIDTH,
|
|
34
36
|
sendKey: DEFAULT_SEND_KEY,
|
|
37
|
+
favoriteModels: {},
|
|
35
38
|
changedAt: 0
|
|
36
39
|
};
|
|
37
40
|
|
|
41
|
+
/* Only lists of slugs survive, so a hand-edited or older record cannot put something else in the pickers. */
|
|
42
|
+
function parseFavoriteModels(stored: unknown): Partial<Record<AgentKind, string[]>> {
|
|
43
|
+
if (typeof stored !== 'object' || stored === null || Array.isArray(stored)) {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
return Object.fromEntries(
|
|
47
|
+
Object.entries(stored).flatMap(([provider, slugs]) => {
|
|
48
|
+
const kept = Array.isArray(slugs) ? slugs.filter((slug): slug is string => typeof slug === 'string' && slug !== '') : [];
|
|
49
|
+
return kept.length === 0 ? [] : [[provider, kept]];
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
38
54
|
/*
|
|
39
55
|
* Only the fields we know today, so what an older build wrote (one global `selection`, the plan
|
|
40
56
|
* toggle's `interactionMode`) falls away here instead of riding along in every later write. A
|
|
@@ -54,6 +70,7 @@ export function parseChatPreferences(raw: string | null): ChatPreferences {
|
|
|
54
70
|
accountByMachine: { ...stored.accountByMachine },
|
|
55
71
|
readingWidth: isChatReadingWidth(stored.readingWidth) ? stored.readingWidth : DEFAULT_READING_WIDTH,
|
|
56
72
|
sendKey: isChatSendKey(stored.sendKey) ? stored.sendKey : DEFAULT_SEND_KEY,
|
|
73
|
+
favoriteModels: parseFavoriteModels(stored.favoriteModels),
|
|
57
74
|
changedAt: typeof stored.changedAt === 'number' ? stored.changedAt : 0
|
|
58
75
|
};
|
|
59
76
|
} catch {
|
|
@@ -190,6 +207,24 @@ export function rememberChatAccount(scopeId: string, provider: AgentKind, accoun
|
|
|
190
207
|
write(withAccount(useChatPreferences.getState(), scopeId, provider, account));
|
|
191
208
|
}
|
|
192
209
|
|
|
210
|
+
/* The starred models of one CLI; a slug only means something inside its own catalog. */
|
|
211
|
+
export function favoriteModelsOf(favorites: ChatPreferences['favoriteModels'], provider: AgentKind): ReadonlySet<string> {
|
|
212
|
+
return new Set(favorites[provider] ?? []);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* Stars a model that was not, and takes the star off one that was. A provider left without any drops out. */
|
|
216
|
+
export function withFavoriteToggled(preferences: ChatPreferences, provider: AgentKind, slug: string): ChatPreferences {
|
|
217
|
+
const current = preferences.favoriteModels[provider] ?? [];
|
|
218
|
+
const next = current.includes(slug) ? current.filter((each) => each !== slug) : [...current, slug];
|
|
219
|
+
const { [provider]: _previous, ...others } = preferences.favoriteModels;
|
|
220
|
+
return { ...preferences, favoriteModels: next.length === 0 ? others : { ...others, [provider]: next } };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* Leaves `changedAt` alone: a star orders a person's own pickers and is no pick a machine weighs. */
|
|
224
|
+
export function toggleFavoriteModel(provider: AgentKind, slug: string): void {
|
|
225
|
+
store(withFavoriteToggled(useChatPreferences.getState(), provider, slug));
|
|
226
|
+
}
|
|
227
|
+
|
|
193
228
|
/* Back to the CLI's own default for this provider; the other providers keep what they had. */
|
|
194
229
|
export function forgetChatSelection(provider: AgentKind): void {
|
|
195
230
|
const current = useChatPreferences.getState();
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import i18next from 'i18next';
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
2
|
import { create } from 'zustand';
|
|
4
3
|
import type { ChatItem, ChatSubagentItem } from '@adecore/agent-contracts';
|
|
5
4
|
import { useChatScope } from '../scope';
|
|
@@ -128,10 +127,10 @@ export function useOpenableSubagents(chatId: string): ChatSubagentItem[] {
|
|
|
128
127
|
// The structure and not the items, so a delta growing a reply does not walk the thread again.
|
|
129
128
|
const order = useChatRow(chatId, (row) => row?.order);
|
|
130
129
|
const structure = useChatRow(chatId, (row) => row?.structure);
|
|
131
|
-
return
|
|
130
|
+
return structure === undefined ? [] : openableSubagents(order ?? NO_ITEMS, structure, refused);
|
|
132
131
|
}
|
|
133
132
|
|
|
134
133
|
export function useSubagentItem(chatId: string, toolUseId: string): ChatSubagentItem | null {
|
|
135
134
|
const structure = useChatRow(chatId, (row) => row?.structure);
|
|
136
|
-
return
|
|
135
|
+
return structure === undefined ? null : subagentOf(structure, toolUseId);
|
|
137
136
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
2
1
|
import { useTranslation } from 'react-i18next';
|
|
3
2
|
import { parseAnsi } from '../logic/ansi';
|
|
4
3
|
|
|
5
4
|
export function AnsiOutput({ text, limit, tailLines }: { text: string; limit?: number; tailLines?: number }) {
|
|
6
5
|
const { t } = useTranslation('agent-chat');
|
|
7
|
-
const { tokens, omitted } =
|
|
6
|
+
const { tokens, omitted } = parseAnsi(text, { limit, tailLines });
|
|
8
7
|
return (
|
|
9
8
|
<>
|
|
10
9
|
{tokens.map((token, index) => (
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
2
1
|
import clsx from 'clsx';
|
|
3
2
|
import { useTranslation } from 'react-i18next';
|
|
4
3
|
import { Bookmark, Pencil, X } from 'lucide-react';
|
|
@@ -8,6 +7,7 @@ import { useChatHost } from '../../host';
|
|
|
8
7
|
import { useChatScope } from '../../scope';
|
|
9
8
|
import { useChatRow } from '../../state/chats';
|
|
10
9
|
import { Icon, Menu } from '@adecore/ui';
|
|
10
|
+
import { useHostChatPlace } from '../../host-hooks';
|
|
11
11
|
|
|
12
12
|
/* A chat's bookmarks in thread order, each a jump to its message with rename and remove; nothing without bookmarks. */
|
|
13
13
|
export function BookmarkSubmenu({ chatId }: { chatId: string }) {
|
|
@@ -16,8 +16,8 @@ export function BookmarkSubmenu({ chatId }: { chatId: string }) {
|
|
|
16
16
|
const bookmarks = useChatRow(chatId, (row) => row?.bookmarks);
|
|
17
17
|
const order = useChatRow(chatId, (row) => row?.order);
|
|
18
18
|
const host = useChatHost();
|
|
19
|
-
const place =
|
|
20
|
-
const sorted =
|
|
19
|
+
const place = useHostChatPlace(chatId);
|
|
20
|
+
const sorted = bookmarksInThreadOrder(bookmarks ?? [], order ?? []);
|
|
21
21
|
if (sorted.length === 0) {
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
3
|
import { useShallow } from 'zustand/shallow';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
@@ -16,6 +16,7 @@ import { useChatHost } from '../../host';
|
|
|
16
16
|
import { useChatScope } from '../../scope';
|
|
17
17
|
import { useChatActions } from '../actions';
|
|
18
18
|
import { statusLookOf, type StatusWord } from '../../agents/status-look';
|
|
19
|
+
import { useHostTask, useHostTasks } from '../../host-hooks';
|
|
19
20
|
|
|
20
21
|
const NO_TASKS: readonly ChatBackgroundTask[] = [];
|
|
21
22
|
|
|
@@ -30,29 +31,21 @@ export function ChatActivity({ chatId }: { chatId: string }) {
|
|
|
30
31
|
const order = useChatRow(chatId, (row) => row?.order) ?? NO_ITEMS;
|
|
31
32
|
const before = useChatRow(chatId, (row) => row?.subagentsBefore) ?? NO_ITEMS;
|
|
32
33
|
const structure = useChatRow(chatId, (row) => row?.structure);
|
|
33
|
-
const subagents =
|
|
34
|
+
const subagents = structure === undefined ? [] : flyoutSubagents([...before, ...order], structure);
|
|
34
35
|
const { id, keyOf } = useChatScope();
|
|
35
|
-
const tasks =
|
|
36
|
+
const tasks = useHostTasks(id);
|
|
36
37
|
// The infos of these sub-agents' own chats, so the info of any other chat changing does not draw this again.
|
|
37
38
|
const children = useChats(useShallow((s) => subagents.map((item) => (item.childId === undefined ? undefined : s.statusByKey[keyOf(item.childId)]?.info))));
|
|
38
|
-
const progress =
|
|
39
|
-
() =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return taskRowStateOf(item, taskId === null ? null : (tasks?.[taskId] ?? null), children[index]);
|
|
44
|
-
})
|
|
45
|
-
),
|
|
46
|
-
[subagents, tasks, children]
|
|
47
|
-
);
|
|
48
|
-
const words = useMemo(
|
|
49
|
-
() =>
|
|
50
|
-
subagents.map((item) => {
|
|
51
|
-
const taskId = taskIdOf(item);
|
|
52
|
-
return statusWordOf(item, taskId === null ? null : (tasks?.[taskId] ?? null));
|
|
53
|
-
}),
|
|
54
|
-
[subagents, tasks]
|
|
39
|
+
const progress = progressOf(
|
|
40
|
+
subagents.map((item, index) => {
|
|
41
|
+
const taskId = taskIdOf(item);
|
|
42
|
+
return taskRowStateOf(item, taskId === null ? null : (tasks?.[taskId] ?? null), children[index]);
|
|
43
|
+
})
|
|
55
44
|
);
|
|
45
|
+
const words = subagents.map((item) => {
|
|
46
|
+
const taskId = taskIdOf(item);
|
|
47
|
+
return statusWordOf(item, taskId === null ? null : (tasks?.[taskId] ?? null));
|
|
48
|
+
});
|
|
56
49
|
const background = useChatRow(chatId, (row) => row?.info.background) ?? NO_TASKS;
|
|
57
50
|
if (subagents.length === 0 && background.length === 0) {
|
|
58
51
|
return null;
|
|
@@ -174,11 +167,10 @@ function SubagentRows({ chatId, items }: { chatId: string; items: readonly ChatS
|
|
|
174
167
|
}
|
|
175
168
|
|
|
176
169
|
function SubagentEntry({ chatId, item, now }: { chatId: string; item: ChatSubagentItem; now: number }) {
|
|
177
|
-
const host = useChatHost();
|
|
178
170
|
const { t } = useTranslation('agent-chat');
|
|
179
171
|
const { id, keyOf } = useChatScope();
|
|
180
172
|
const refused = useSubagentSupport((s) => s.unsupported[id] === true);
|
|
181
|
-
const task =
|
|
173
|
+
const task = useHostTask(id, taskIdOf(item));
|
|
182
174
|
const { show } = useSubagentTrail(chatId);
|
|
183
175
|
const child = useChats((s) => (item.childId === undefined ? undefined : s.statusByKey[keyOf(item.childId)]?.info));
|
|
184
176
|
const word = statusWordOf(item, task);
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { MessageSquare, X } from 'lucide-react';
|
|
4
|
-
import { useChatHost } from '../../host';
|
|
5
4
|
import { CHIP_IN_MESSAGE, MENTION_TONE } from './chips';
|
|
6
5
|
import { Icon, Tooltip } from '@adecore/ui';
|
|
6
|
+
import { useHostReferableChats } from '../../host-hooks';
|
|
7
7
|
|
|
8
8
|
/* A chat attached to a message, under the title it has now rather than the one it had when it was attached. */
|
|
9
9
|
export function ChatReferenceChip({ chatId, onRemove }: { chatId: string; onRemove?: () => void }) {
|
|
10
|
-
const host = useChatHost();
|
|
11
10
|
const { t } = useTranslation('agent-chat');
|
|
12
|
-
const chat =
|
|
11
|
+
const chat = useHostReferableChats().find((candidate) => candidate.id === chatId);
|
|
13
12
|
const title = chat === undefined ? t('chatReference.gone') : chat.title || t('chatReference.untitled');
|
|
14
13
|
return (
|
|
15
14
|
<span className={clsx(MENTION_TONE, CHIP_IN_MESSAGE)}>
|