@adecore/agents-react 0.39.0 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/AgentModelLabel.js +2 -1
- package/dist/agents/model-name.js +2 -1
- package/dist/agents/provider-paths.d.ts +2 -1
- package/dist/agents/provider-paths.js +7 -3
- package/dist/chat/account-choice.js +57 -28
- package/dist/chat/bookmarks.js +2 -1
- package/dist/chat/drafts.d.ts +0 -1
- package/dist/chat/drafts.js +9 -28
- package/dist/chat/logic/picker-entries.js +2 -8
- package/dist/chat/logic/resume-compaction.js +2 -5
- package/dist/chat/logic/timeline-copy.js +2 -8
- package/dist/chat/logic/timeline.js +9 -10
- package/dist/chat/logic/workflow.js +2 -4
- package/dist/chat/subagent-conversation.js +2 -1
- package/dist/chat/ui/GoalDock.js +2 -1
- package/dist/chat/ui/LimitState.js +4 -6
- package/dist/chat/ui/composer/use-continuable-turn.js +0 -1
- package/dist/chat/ui/composer/use-notice.js +2 -1
- package/dist/chat/ui/composer/use-prompt-history.js +1 -1
- package/dist/chat/ui/composer/use-prompt-menus.js +216 -253
- package/dist/chat/ui/composer/use-take-back.js +2 -2
- package/dist/chat/ui/diff/ApprovalReview.js +1 -1
- package/dist/chat/ui/highlighter.d.ts +0 -6
- package/dist/chat/ui/highlighter.js +0 -12
- package/dist/chat/ui/icons.d.ts +0 -1
- package/dist/chat/ui/icons.js +0 -3
- package/dist/chat/ui/rows/MessageRows.js +2 -5
- package/dist/chat/ui/rows/row-rhythm.js +2 -5
- package/dist/chat/ui/theme-watch.js +3 -7
- package/dist/prompts/logic/usePromptSession.js +2 -1
- package/dist/providers/AddAccountForm.js +2 -1
- package/dist/providers/CliDetail.js +104 -130
- package/dist/providers/account-actions.js +6 -9
- package/dist/state/chats.d.ts +1 -0
- package/dist/state/chats.js +6 -11
- package/dist/state/provider-accounts.d.ts +1 -1
- package/dist/state/provider-accounts.js +2 -8
- package/dist/transport.d.ts +2 -0
- package/dist/transport.js +14 -0
- package/dist/usage/ChartLogo.d.ts +5 -0
- package/dist/usage/ChartLogo.js +48 -0
- package/dist/usage/LimitsList.js +6 -16
- package/dist/usage/UsageBreakdown.js +17 -40
- package/dist/usage/UsageLimits.js +183 -211
- package/dist/usage/UsagePage.js +3 -17
- package/dist/usage/UsageSummary.js +4 -9
- package/dist/usage/limits.js +16 -31
- package/dist/usage/summary.d.ts +1 -1
- package/dist/usage/summary.js +3 -5
- package/package.json +27 -27
- package/src/agents/AccountDot.tsx +1 -0
- package/src/agents/AgentIcon.tsx +2 -0
- package/src/agents/AgentModelLabel.tsx +4 -6
- package/src/agents/ModelOptionControl.tsx +8 -0
- package/src/agents/account-limits.ts +3 -0
- package/src/agents/accounts.ts +12 -0
- package/src/agents/model-name.ts +13 -1
- package/src/agents/model-options.ts +2 -0
- package/src/agents/model-requirement.ts +4 -0
- package/src/agents/option-words.ts +2 -0
- package/src/agents/provider-paths.ts +8 -3
- package/src/agents/status-look.ts +1 -0
- package/src/chat/account-choice.ts +25 -19
- package/src/chat/actions.ts +5 -0
- package/src/chat/attachments.ts +8 -0
- package/src/chat/bookmarks.ts +13 -1
- package/src/chat/chat-client.ts +63 -0
- package/src/chat/chat-references.ts +3 -0
- package/src/chat/composer-effort.ts +5 -0
- package/src/chat/composer-send.ts +4 -0
- package/src/chat/drafts.ts +21 -17
- package/src/chat/forks.ts +4 -0
- package/src/chat/guards.ts +2 -0
- package/src/chat/index.ts +7 -0
- package/src/chat/logic/ansi.ts +47 -0
- package/src/chat/logic/bookmarks.ts +7 -0
- package/src/chat/logic/command-looks.ts +29 -0
- package/src/chat/logic/context-usage.ts +3 -0
- package/src/chat/logic/continue-after-stop.ts +5 -0
- package/src/chat/logic/effort.ts +4 -0
- package/src/chat/logic/embedded-code.ts +63 -0
- package/src/chat/logic/fork.ts +22 -0
- package/src/chat/logic/generated-image.ts +7 -0
- package/src/chat/logic/goal.ts +7 -0
- package/src/chat/logic/handback.ts +2 -0
- package/src/chat/logic/limit.ts +8 -0
- package/src/chat/logic/picker-entries.ts +16 -4
- package/src/chat/logic/resume-compaction.ts +22 -5
- package/src/chat/logic/scrubber.ts +38 -5
- package/src/chat/logic/thought.ts +5 -0
- package/src/chat/logic/thread-cards.ts +6 -0
- package/src/chat/logic/timeline-copy.ts +4 -9
- package/src/chat/logic/timeline-target.ts +15 -0
- package/src/chat/logic/timeline.ts +113 -10
- package/src/chat/logic/tools.ts +30 -0
- package/src/chat/logic/visual-height.ts +5 -0
- package/src/chat/logic/visual-viewport.ts +1 -0
- package/src/chat/logic/welcome.ts +3 -0
- package/src/chat/logic/workflow.ts +26 -7
- package/src/chat/mentions.ts +56 -0
- package/src/chat/pending-prompts.ts +16 -0
- package/src/chat/persisted-json.ts +15 -0
- package/src/chat/preferences.ts +16 -0
- package/src/chat/prompt-hold.ts +2 -0
- package/src/chat/quote.ts +7 -0
- package/src/chat/reading-width.ts +1 -0
- package/src/chat/recent-messages.ts +5 -0
- package/src/chat/resume-compaction-dismissals.ts +1 -0
- package/src/chat/stash.ts +12 -0
- package/src/chat/subagent-conversation.ts +24 -4
- package/src/chat/subagent-list.ts +14 -0
- package/src/chat/subagent-support.ts +1 -0
- package/src/chat/subagent-view.ts +9 -0
- package/src/chat/thumbnails.ts +14 -0
- package/src/chat/timeline-flash.ts +2 -0
- package/src/chat/timeline-scroll.ts +23 -0
- package/src/chat/ui/AccountPill.tsx +4 -0
- package/src/chat/ui/AnsiOutput.tsx +1 -0
- package/src/chat/ui/BookmarkMarker.tsx +5 -0
- package/src/chat/ui/BookmarkSubmenu.tsx +1 -0
- package/src/chat/ui/ChatActivity.tsx +13 -0
- package/src/chat/ui/CodeBlock.tsx +8 -0
- package/src/chat/ui/Composer.tsx +15 -0
- package/src/chat/ui/ComposerInput.tsx +22 -0
- package/src/chat/ui/DiffPool.tsx +3 -0
- package/src/chat/ui/EditDiff.tsx +5 -0
- package/src/chat/ui/GoalDock.tsx +13 -1
- package/src/chat/ui/ImageView.tsx +11 -0
- package/src/chat/ui/LimitState.tsx +23 -6
- package/src/chat/ui/Markdown.tsx +41 -0
- package/src/chat/ui/MermaidBlock.tsx +16 -0
- package/src/chat/ui/MessageActions.tsx +8 -0
- package/src/chat/ui/ModelMarks.tsx +1 -0
- package/src/chat/ui/Pickers.tsx +26 -0
- package/src/chat/ui/PromptComposer.tsx +2 -0
- package/src/chat/ui/PromptEditor.tsx +6 -0
- package/src/chat/ui/QuoteButton.tsx +12 -0
- package/src/chat/ui/ReferenceChip.tsx +3 -0
- package/src/chat/ui/ResumeCompactionDock.tsx +1 -0
- package/src/chat/ui/RowTime.tsx +1 -0
- package/src/chat/ui/RunSettings.tsx +38 -0
- package/src/chat/ui/Scrubber.tsx +14 -0
- package/src/chat/ui/SessionLine.tsx +1 -0
- package/src/chat/ui/ShellCommand.tsx +32 -0
- package/src/chat/ui/SubagentControls.tsx +7 -0
- package/src/chat/ui/SubagentInfo.tsx +1 -0
- package/src/chat/ui/SubagentStopButton.tsx +6 -0
- package/src/chat/ui/SubagentTimeline.tsx +10 -0
- package/src/chat/ui/Timeline.tsx +119 -0
- package/src/chat/ui/TimelineMenu.tsx +5 -0
- package/src/chat/ui/UnifiedDiff.tsx +6 -0
- package/src/chat/ui/UploadThumb.tsx +2 -0
- package/src/chat/ui/VisualDialogs.tsx +3 -0
- package/src/chat/ui/VisualFrame.tsx +15 -0
- package/src/chat/ui/Welcome.tsx +1 -0
- package/src/chat/ui/chip-clipboard.ts +20 -0
- package/src/chat/ui/code-lines.ts +14 -0
- package/src/chat/ui/code-theme.ts +1 -0
- package/src/chat/ui/composer/ComposerControls.tsx +5 -0
- package/src/chat/ui/composer/ComposerMenu.tsx +12 -0
- package/src/chat/ui/composer/DraftParts.tsx +2 -0
- package/src/chat/ui/composer/QueuedMessages.tsx +4 -0
- package/src/chat/ui/composer/chip-paste.ts +16 -0
- package/src/chat/ui/composer/chips.ts +6 -0
- package/src/chat/ui/composer/editor.ts +9 -0
- package/src/chat/ui/composer/insert.ts +2 -0
- package/src/chat/ui/composer/key-handler.ts +39 -0
- package/src/chat/ui/composer/keys.ts +23 -0
- package/src/chat/ui/composer/placeholder.ts +6 -0
- package/src/chat/ui/composer/prompt-picks.ts +1 -0
- package/src/chat/ui/composer/surface.ts +5 -0
- package/src/chat/ui/composer/use-chat-controls.ts +13 -0
- package/src/chat/ui/composer/use-compaction-offer.ts +3 -0
- package/src/chat/ui/composer/use-composer-menus.ts +2 -0
- package/src/chat/ui/composer/use-composer-submit.ts +27 -0
- package/src/chat/ui/composer/use-continuable-turn.ts +3 -5
- package/src/chat/ui/composer/use-draft-files.ts +24 -0
- package/src/chat/ui/composer/use-effort-stepper.ts +8 -0
- package/src/chat/ui/composer/use-notice.ts +4 -1
- package/src/chat/ui/composer/use-pending-requests.ts +3 -0
- package/src/chat/ui/composer/use-prompt-history.ts +20 -11
- package/src/chat/ui/composer/use-prompt-hold.ts +3 -0
- package/src/chat/ui/composer/use-prompt-menus.ts +34 -14
- package/src/chat/ui/composer/use-take-back.ts +20 -2
- package/src/chat/ui/diff/ApprovalReview.tsx +18 -15
- package/src/chat/ui/diff/DiffFallbackBoundary.tsx +1 -0
- package/src/chat/ui/diff/DiffOmitted.tsx +1 -0
- package/src/chat/ui/diff/DiffStack.tsx +2 -0
- package/src/chat/ui/diff/DiffUnavailable.tsx +1 -0
- package/src/chat/ui/diff/EditorEditDiff.tsx +13 -0
- package/src/chat/ui/diff/EditorPatchDiff.tsx +4 -0
- package/src/chat/ui/diff/LegacyDiffPool.tsx +2 -0
- package/src/chat/ui/diff/LegacyEditDiff.tsx +2 -0
- package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +8 -0
- package/src/chat/ui/diff/RawDiff.tsx +1 -0
- package/src/chat/ui/diff/ReviewDiff.tsx +1 -0
- package/src/chat/ui/diff/diff-source-budget.ts +8 -0
- package/src/chat/ui/diff/editor-edit-model.ts +4 -0
- package/src/chat/ui/diff/review-plan.ts +9 -0
- package/src/chat/ui/diff/review-settle.ts +1 -0
- package/src/chat/ui/diff/syntax-runtime.ts +52 -0
- package/src/chat/ui/diff/use-review-stack.ts +9 -0
- package/src/chat/ui/diff-syntax.ts +12 -0
- package/src/chat/ui/diff-theme.ts +2 -0
- package/src/chat/ui/file-links.ts +2 -0
- package/src/chat/ui/find-reveal.ts +1 -0
- package/src/chat/ui/full-diff.ts +1 -0
- package/src/chat/ui/highlighter.ts +29 -15
- package/src/chat/ui/icons.tsx +2 -4
- package/src/chat/ui/index.ts +4 -0
- package/src/chat/ui/intelligent-ui/UiBlockFrame.tsx +25 -0
- package/src/chat/ui/intelligent-ui/UiFallbackPart.tsx +1 -0
- package/src/chat/ui/intelligent-ui/UiReply.tsx +63 -0
- package/src/chat/ui/intelligent-ui/block-local.ts +5 -0
- package/src/chat/ui/intelligent-ui/chart-data.ts +15 -0
- package/src/chat/ui/intelligent-ui/node-text.ts +17 -0
- package/src/chat/ui/intelligent-ui/renderers/binding.ts +1 -0
- package/src/chat/ui/intelligent-ui/renderers/chart.tsx +31 -0
- package/src/chat/ui/intelligent-ui/renderers/choices.tsx +4 -0
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +18 -0
- package/src/chat/ui/intelligent-ui/renderers/data.tsx +11 -0
- package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +13 -0
- package/src/chat/ui/intelligent-ui/renderers/links.tsx +9 -0
- package/src/chat/ui/intelligent-ui/renderers/map-slots.ts +15 -0
- package/src/chat/ui/intelligent-ui/renderers/map-style.ts +15 -0
- package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +48 -0
- package/src/chat/ui/intelligent-ui/renderers/map.tsx +20 -0
- package/src/chat/ui/intelligent-ui/renderers/place-point.ts +3 -0
- package/src/chat/ui/intelligent-ui/renderers/structure.tsx +3 -0
- package/src/chat/ui/intelligent-ui/renderers/text.tsx +8 -0
- package/src/chat/ui/intelligent-ui/reply-parts.ts +5 -0
- package/src/chat/ui/intelligent-ui/table-data.ts +17 -0
- package/src/chat/ui/intelligent-ui/use-in-view.ts +4 -0
- package/src/chat/ui/intelligent-ui/use-later.ts +4 -0
- package/src/chat/ui/intelligent-ui/use-truncated.ts +5 -0
- package/src/chat/ui/intelligent-ui/use-ui-links.ts +29 -0
- package/src/chat/ui/intelligent-ui/use-ui-queries.ts +60 -0
- package/src/chat/ui/markdown-blocks.ts +64 -0
- package/src/chat/ui/mermaid.ts +4 -0
- package/src/chat/ui/quote-selection.ts +10 -0
- package/src/chat/ui/rehype-chips.ts +5 -0
- package/src/chat/ui/rehype-fade.ts +2 -0
- package/src/chat/ui/remark-html-as-text.ts +5 -0
- package/src/chat/ui/reveal.ts +18 -0
- package/src/chat/ui/rows/ForksRow.tsx +5 -0
- package/src/chat/ui/rows/GeneratedImageRow.tsx +10 -0
- package/src/chat/ui/rows/MessageRows.tsx +29 -5
- package/src/chat/ui/rows/RowContainer.tsx +1 -0
- package/src/chat/ui/rows/Rows.tsx +2 -0
- package/src/chat/ui/rows/SubagentRow.tsx +13 -0
- package/src/chat/ui/rows/TaskRow.tsx +7 -0
- package/src/chat/ui/rows/VisualRow.tsx +4 -0
- package/src/chat/ui/rows/WorkRows.tsx +46 -0
- package/src/chat/ui/rows/WorkflowRow.tsx +10 -0
- package/src/chat/ui/rows/row-rhythm.ts +7 -5
- package/src/chat/ui/theme-watch.ts +8 -7
- package/src/chat/ui/thread-references.ts +7 -0
- package/src/chat/ui/use-diff-source-tokens.ts +8 -0
- package/src/chat/ui/useToggleSet.ts +3 -0
- package/src/chat/ui/visual-theme.ts +30 -0
- package/src/chat/visual-bridge.ts +23 -0
- package/src/chat/visual-viewport.ts +12 -0
- package/src/host-hooks.ts +6 -4
- package/src/host.ts +2 -0
- package/src/index.ts +3 -0
- package/src/lazy.tsx +9 -0
- package/src/mounted-registry.ts +4 -0
- package/src/port-transport.ts +32 -0
- package/src/prompts/index.ts +2 -0
- package/src/prompts/logic/focus.ts +5 -0
- package/src/prompts/logic/keys.ts +12 -0
- package/src/prompts/logic/prompts.ts +8 -0
- package/src/prompts/logic/subjects.ts +8 -0
- package/src/prompts/logic/useFocusAfterAnswer.ts +2 -0
- package/src/prompts/logic/usePromptSession.ts +13 -1
- package/src/prompts/ui/ApprovalBody.tsx +2 -0
- package/src/prompts/ui/PromptActions.tsx +2 -0
- package/src/prompts/ui/PromptCard.tsx +6 -0
- package/src/prompts/ui/PromptView.tsx +6 -0
- package/src/prompts/ui/QuestionBody.tsx +11 -0
- package/src/providers/AccountColors.tsx +1 -0
- package/src/providers/AccountDetail.tsx +7 -0
- package/src/providers/AccountVariables.tsx +2 -0
- package/src/providers/AddAccountForm.tsx +5 -1
- package/src/providers/CliDetail.tsx +6 -5
- package/src/providers/InstallPage.tsx +11 -0
- package/src/providers/ProviderList.tsx +3 -0
- package/src/providers/ProvidersPane.tsx +18 -0
- package/src/providers/account-actions.ts +10 -8
- package/src/providers/install-steps.ts +3 -0
- package/src/providers/pane-target.ts +5 -0
- package/src/providers/provider-abilities.ts +1 -0
- package/src/providers/provider-groups.ts +6 -0
- package/src/providers/variables.ts +12 -0
- package/src/scope.ts +2 -0
- package/src/state/chats.ts +61 -14
- package/src/state/index.ts +2 -0
- package/src/state/provider-accounts.ts +7 -9
- package/src/state/providers.ts +1 -0
- package/src/state/usage.ts +6 -0
- package/src/storage.ts +18 -0
- package/src/testing/prompts.ts +5 -0
- package/src/transport.ts +18 -0
- package/src/usage/ChartLogo.tsx +12 -0
- package/src/usage/LimitsList.tsx +7 -8
- package/src/usage/UsageBreakdown.tsx +11 -4
- package/src/usage/UsageChart.tsx +5 -0
- package/src/usage/UsageLimits.tsx +10 -7
- package/src/usage/UsageLimitsCard.tsx +2 -0
- package/src/usage/UsagePage.tsx +16 -19
- package/src/usage/UsageSummary.tsx +4 -5
- package/src/usage/UsageTiles.tsx +2 -0
- package/src/usage/format.ts +7 -0
- package/src/usage/limit-groups.ts +14 -0
- package/src/usage/limits.ts +11 -13
- package/src/usage/money.ts +1 -0
- package/src/usage/summary.ts +15 -7
package/src/state/chats.ts
CHANGED
|
@@ -21,10 +21,8 @@ export interface ChatState {
|
|
|
21
21
|
historyPending?: boolean;
|
|
22
22
|
items: Record<string, ChatItem>;
|
|
23
23
|
/*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* The rows are derived from this, so a word arriving touches the row that draws it and not the
|
|
27
|
-
* whole thread; that row reads its text from `items` with `useCurrentItem`.
|
|
24
|
+
* `items`, except that a delta growing a reply or thought that has text, or a running call's output,
|
|
25
|
+
* leaves it as it was. The rows derive from this, so a word redraws only its row (`useCurrentItem`).
|
|
28
26
|
*/
|
|
29
27
|
structure: Record<string, ChatItem>;
|
|
30
28
|
order: string[];
|
|
@@ -100,21 +98,25 @@ interface ChatsStore {
|
|
|
100
98
|
forgetWhere(matches: (key: string) => boolean): void;
|
|
101
99
|
}
|
|
102
100
|
|
|
103
|
-
function isWaiting(item: ChatItem | undefined): item is ChatApprovalItem | ChatQuestionItem {
|
|
101
|
+
export function isWaiting(item: ChatItem | undefined): item is ChatApprovalItem | ChatQuestionItem {
|
|
104
102
|
return (item?.kind === 'approval' && item.decision === 'pending') || (item?.kind === 'question' && item.state === 'pending');
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
function stateOf(info: ChatInfo, items: ChatItem[], page?: ChatPage): ChatState {
|
|
108
106
|
const byId = Object.fromEntries(items.map((item) => [item.id, item]));
|
|
109
107
|
const order = items.map((item) => item.id);
|
|
108
|
+
|
|
110
109
|
if (page === undefined || page.history.cursor === null) {
|
|
111
110
|
return { info, items: byId, structure: byId, order };
|
|
112
111
|
}
|
|
112
|
+
|
|
113
113
|
const before = page.pending.filter((item) => !(item.id in byId));
|
|
114
114
|
const subagents = (page.subagents ?? []).filter((item) => !(item.id in byId));
|
|
115
|
+
|
|
115
116
|
for (const item of [...before, ...subagents]) {
|
|
116
117
|
byId[item.id] = item;
|
|
117
118
|
}
|
|
119
|
+
|
|
118
120
|
return {
|
|
119
121
|
info,
|
|
120
122
|
items: byId,
|
|
@@ -137,13 +139,17 @@ type ItemWriter = (state: ChatState, item: ChatItem, structural?: boolean) => Ch
|
|
|
137
139
|
function withEarlierItem(state: ChatState, item: ChatItem, write: ItemWriter): ChatState {
|
|
138
140
|
if (item.kind === 'subagent') {
|
|
139
141
|
const subagents = state.subagentsBefore ?? [];
|
|
142
|
+
|
|
140
143
|
return { ...write(state, item), subagentsBefore: subagents.includes(item.id) ? subagents : [...subagents, item.id] };
|
|
141
144
|
}
|
|
145
|
+
|
|
142
146
|
const waiting = state.waitingBefore ?? [];
|
|
143
147
|
const held = waiting.includes(item.id);
|
|
148
|
+
|
|
144
149
|
if (isWaiting(item)) {
|
|
145
150
|
return { ...write(state, item), waitingBefore: held ? waiting : [...waiting, item.id] };
|
|
146
151
|
}
|
|
152
|
+
|
|
147
153
|
return held ? { ...write(state, item), waitingBefore: waiting.filter((id) => id !== item.id) } : state;
|
|
148
154
|
}
|
|
149
155
|
|
|
@@ -152,22 +158,29 @@ export function prependPage(state: ChatState, cursor: string, page: ChatHistoryR
|
|
|
152
158
|
if (state.history?.cursor !== cursor) {
|
|
153
159
|
return state;
|
|
154
160
|
}
|
|
161
|
+
|
|
155
162
|
const { historyPending: _historyPending, ...loaded } = state;
|
|
156
163
|
const held = new Set(state.order);
|
|
157
164
|
const older = page.items.filter((item) => !held.has(item.id));
|
|
158
165
|
const olderIds = new Set(older.map((item) => item.id));
|
|
159
166
|
const items = { ...state.items };
|
|
167
|
+
|
|
160
168
|
for (const item of older) {
|
|
161
169
|
// A request from before the page that this client already holds came in live, which is never older than the page.
|
|
162
170
|
items[item.id] = state.items[item.id] ?? item;
|
|
163
171
|
}
|
|
172
|
+
|
|
164
173
|
const order = [...olderIds, ...state.order];
|
|
174
|
+
|
|
165
175
|
if (page.history.cursor === null) {
|
|
166
176
|
const { history: _history, waitingBefore: _waitingBefore, subagentsBefore: _subagentsBefore, ...rest } = loaded;
|
|
177
|
+
|
|
167
178
|
return { ...rest, items, structure: items, order };
|
|
168
179
|
}
|
|
180
|
+
|
|
169
181
|
const waitingBefore = (state.waitingBefore ?? []).filter((id) => !olderIds.has(id));
|
|
170
182
|
const subagentsBefore = (state.subagentsBefore ?? []).filter((id) => !olderIds.has(id));
|
|
183
|
+
|
|
171
184
|
return { ...loaded, items, structure: items, order, history: page.history, waitingBefore, subagentsBefore };
|
|
172
185
|
}
|
|
173
186
|
|
|
@@ -178,6 +191,7 @@ export function waitingRequestsOf(chat: Pick<ChatState, 'structure' | 'order' |
|
|
|
178
191
|
|
|
179
192
|
function withItem(state: ChatState, item: ChatItem, structural = true): ChatState {
|
|
180
193
|
const items = { ...state.items, [item.id]: item };
|
|
194
|
+
|
|
181
195
|
return structural ? { ...state, items, structure: items } : { ...state, items };
|
|
182
196
|
}
|
|
183
197
|
|
|
@@ -201,18 +215,23 @@ export function applyEvent(state: ChatState, event: ChatEvent): ChatState {
|
|
|
201
215
|
|
|
202
216
|
function replayEvents(state: ChatState, events: readonly ChatEvent[]): ChatState {
|
|
203
217
|
let draft: Record<string, ChatItem> | undefined;
|
|
218
|
+
|
|
204
219
|
const write: ItemWriter = (current, item, structural = true) => {
|
|
205
220
|
// A text delta must leave the last structural snapshot intact, including within this replay.
|
|
206
221
|
if (draft !== current.items || (!structural && draft === current.structure)) {
|
|
207
222
|
draft = { ...current.items };
|
|
208
223
|
}
|
|
224
|
+
|
|
209
225
|
// Item ids may be __proto__, so this must define an own data property.
|
|
210
226
|
Object.defineProperty(draft, item.id, { value: item, enumerable: true, configurable: true, writable: true });
|
|
227
|
+
|
|
211
228
|
return structural ? { ...current, items: draft, structure: draft } : { ...current, items: draft };
|
|
212
229
|
};
|
|
230
|
+
|
|
213
231
|
for (const event of events) {
|
|
214
232
|
state = applyUpdate(state, event, write);
|
|
215
233
|
}
|
|
234
|
+
|
|
216
235
|
return state;
|
|
217
236
|
}
|
|
218
237
|
|
|
@@ -222,37 +241,44 @@ function applyUpdate(state: ChatState, event: ChatEvent, write: ItemWriter): Cha
|
|
|
222
241
|
if (isBeforePage(state, event.historyIndex)) {
|
|
223
242
|
return withEarlierItem(state, event.item, write);
|
|
224
243
|
}
|
|
244
|
+
|
|
225
245
|
const known = event.item.id in state.items;
|
|
246
|
+
|
|
226
247
|
return { ...write(state, event.item), order: known ? state.order : [...state.order, event.item.id] };
|
|
227
248
|
}
|
|
228
249
|
case 'delta': {
|
|
229
250
|
const item = state.items[event.itemId];
|
|
251
|
+
|
|
230
252
|
if (event.uiQueries && item?.kind === 'assistant') {
|
|
231
253
|
// The text first, as the daemon's thread applies it, then the metadata on what it grew into.
|
|
232
254
|
const { uiQueries, ...text } = event;
|
|
233
255
|
const grown = event.text === '' ? state : applyUpdate(state, text, write);
|
|
234
256
|
const reply = grown.items[event.itemId] ?? item;
|
|
257
|
+
|
|
235
258
|
return write(grown, { ...reply, uiQueries } as ChatItem, false);
|
|
236
259
|
}
|
|
260
|
+
|
|
237
261
|
if (event.ui !== undefined && event.text === '') {
|
|
238
262
|
if (item?.kind === 'assistant' && item.streaming && event.textLength === item.text.length) {
|
|
239
263
|
return write(state, { ...item, ui: event.ui }, false);
|
|
240
264
|
}
|
|
265
|
+
|
|
241
266
|
return state;
|
|
242
267
|
}
|
|
268
|
+
|
|
243
269
|
if (item?.kind === 'assistant' || item?.kind === 'thinking') {
|
|
244
270
|
const grown = { ...item, text: item.text + event.text };
|
|
271
|
+
|
|
245
272
|
// The first text is structure after all, since an empty reply that is not streaming has no row.
|
|
246
|
-
|
|
247
|
-
return write(state, grown);
|
|
248
|
-
}
|
|
249
|
-
return write(state, grown, false);
|
|
273
|
+
return write(state, grown, item.text === '');
|
|
250
274
|
}
|
|
275
|
+
|
|
251
276
|
if (item?.kind === 'tool' && item.state === 'running') {
|
|
252
277
|
const progress = item.progress ?? { startedAt: null, description: null, output: null };
|
|
253
|
-
|
|
254
|
-
return write(state,
|
|
278
|
+
|
|
279
|
+
return write(state, { ...item, progress: { ...progress, output: appendToolOutput(progress.output, event.text) } }, false);
|
|
255
280
|
}
|
|
281
|
+
|
|
256
282
|
return state;
|
|
257
283
|
}
|
|
258
284
|
case 'info':
|
|
@@ -265,9 +291,11 @@ function applyUpdate(state: ChatState, event: ChatEvent, write: ItemWriter): Cha
|
|
|
265
291
|
/* Fields of a held chat that are not part of its thread; a chat this client does not hold stays as it is. */
|
|
266
292
|
function withFields(store: ChatsStore, key: string, fields: Pick<ChatState, 'bookmarks' | 'visuals'>): Partial<ChatsStore> {
|
|
267
293
|
const current = store.byKey[key];
|
|
294
|
+
|
|
268
295
|
if (!current) {
|
|
269
296
|
return {};
|
|
270
297
|
}
|
|
298
|
+
|
|
271
299
|
return { byKey: { ...store.byKey, [key]: { ...current, ...fields } } };
|
|
272
300
|
}
|
|
273
301
|
|
|
@@ -279,6 +307,7 @@ export const useChats = create<ChatsStore>((set) => ({
|
|
|
279
307
|
// The bookmarks and the visuals are not part of the thread; they come in on their own and outlive a reset of it.
|
|
280
308
|
const { bookmarks, visuals } = s.byKey[key] ?? {};
|
|
281
309
|
const next = { ...stateOf(info, items, page), ...(bookmarks === undefined ? {} : { bookmarks }), ...(visuals === undefined ? {} : { visuals }) };
|
|
310
|
+
|
|
282
311
|
return {
|
|
283
312
|
byKey: { ...s.byKey, [key]: next },
|
|
284
313
|
statusByKey: withStatus(s.statusByKey, key, info)
|
|
@@ -288,48 +317,59 @@ export const useChats = create<ChatsStore>((set) => ({
|
|
|
288
317
|
prepend(key, cursor, page) {
|
|
289
318
|
set((s) => {
|
|
290
319
|
const current = s.byKey[key];
|
|
320
|
+
|
|
291
321
|
if (!current) {
|
|
292
322
|
return {};
|
|
293
323
|
}
|
|
324
|
+
|
|
294
325
|
const next = prependPage(current, cursor, page);
|
|
326
|
+
|
|
295
327
|
return next === current ? {} : { byKey: { ...s.byKey, [key]: next } };
|
|
296
328
|
});
|
|
297
329
|
},
|
|
298
330
|
apply(key, event) {
|
|
299
331
|
set((s) => {
|
|
300
332
|
const current = s.byKey[key];
|
|
333
|
+
|
|
301
334
|
if (!current) {
|
|
302
335
|
return {};
|
|
303
336
|
}
|
|
337
|
+
|
|
304
338
|
const next = applyEvent(current, event);
|
|
339
|
+
|
|
305
340
|
return { byKey: { ...s.byKey, [key]: next }, statusByKey: withStatus(s.statusByKey, key, next.info) };
|
|
306
341
|
});
|
|
307
342
|
},
|
|
308
343
|
replay(key, events) {
|
|
309
344
|
set((store) => {
|
|
310
345
|
const current = store.byKey[key];
|
|
346
|
+
|
|
311
347
|
if (!current || events.length === 0) {
|
|
312
348
|
return store;
|
|
313
349
|
}
|
|
350
|
+
|
|
314
351
|
const next = replayEvents(current, events);
|
|
352
|
+
|
|
315
353
|
return next === current ? store : { byKey: { ...store.byKey, [key]: next }, statusByKey: withStatus(store.statusByKey, key, next.info) };
|
|
316
354
|
});
|
|
317
355
|
},
|
|
318
356
|
/*
|
|
319
|
-
* The
|
|
320
|
-
*
|
|
321
|
-
* the counters need, and they ask the info and never the items.
|
|
357
|
+
* The host sends this for every chat it has loaded. The thread stays empty until somebody attaches,
|
|
358
|
+
* since a header, a list and the counters read the info and never the items.
|
|
322
359
|
*/
|
|
323
360
|
status(key, info) {
|
|
324
361
|
set((s) => {
|
|
325
362
|
const current = s.byKey[key];
|
|
363
|
+
|
|
326
364
|
if (!current) {
|
|
327
365
|
return { byKey: { ...s.byKey, [key]: { ...stateOf(info, []), historyPending: true } }, statusByKey: withStatus(s.statusByKey, key, info) };
|
|
328
366
|
}
|
|
367
|
+
|
|
329
368
|
// An attached client already had this on `chat.event`; writing it again would redraw the thread.
|
|
330
369
|
if (sameInfo(current.info, info)) {
|
|
331
370
|
return {};
|
|
332
371
|
}
|
|
372
|
+
|
|
333
373
|
return { byKey: { ...s.byKey, [key]: { ...current, info } }, statusByKey: withStatus(s.statusByKey, key, info) };
|
|
334
374
|
});
|
|
335
375
|
},
|
|
@@ -345,6 +385,7 @@ export const useChats = create<ChatsStore>((set) => ({
|
|
|
345
385
|
delete next[key];
|
|
346
386
|
const statuses = { ...s.statusByKey };
|
|
347
387
|
delete statuses[key];
|
|
388
|
+
|
|
348
389
|
return { byKey: next, statusByKey: statuses };
|
|
349
390
|
});
|
|
350
391
|
},
|
|
@@ -371,6 +412,7 @@ export function chatSink(keyOf: (chatId: string) => string): ChatSink {
|
|
|
371
412
|
/* One chat's thread in the scope this is rendered in. The selector keeps a render tied to the field it reads. */
|
|
372
413
|
export function useChatRow<T>(chatId: string, select: (row: ChatState | undefined) => T): T {
|
|
373
414
|
const { keyOf } = useChatScope();
|
|
415
|
+
|
|
374
416
|
return useChats((s) => select(s.byKey[keyOf(chatId)]));
|
|
375
417
|
}
|
|
376
418
|
|
|
@@ -380,15 +422,19 @@ export function useChatRow<T>(chatId: string, select: (row: ChatState | undefine
|
|
|
380
422
|
*/
|
|
381
423
|
export function useReportedModels(provider: AgentKind): ReadonlySet<string> {
|
|
382
424
|
const scope = useContext(ChatScopeContext);
|
|
425
|
+
|
|
383
426
|
const joined = useChats((s) => {
|
|
384
427
|
const names = new Set<string>();
|
|
428
|
+
|
|
385
429
|
for (const [key, state] of Object.entries(s.statusByKey)) {
|
|
386
430
|
if (scope?.owns(key) === true && state.info.provider === provider && state.info.model !== null) {
|
|
387
431
|
names.add(state.info.model);
|
|
388
432
|
}
|
|
389
433
|
}
|
|
434
|
+
|
|
390
435
|
return [...names].sort().join('\n');
|
|
391
436
|
});
|
|
437
|
+
|
|
392
438
|
return new Set(joined === '' ? [] : joined.split('\n'));
|
|
393
439
|
}
|
|
394
440
|
|
|
@@ -401,6 +447,7 @@ export function useAwaitsApproval(chatId: string, toolUseId: string): boolean {
|
|
|
401
447
|
export function useCurrentItem<T extends ChatItem>(chatId: string, derived: T): T {
|
|
402
448
|
return useChatRow(chatId, (row) => {
|
|
403
449
|
const item = row?.items[derived.id];
|
|
450
|
+
|
|
404
451
|
return item?.kind === derived.kind ? (item as T) : derived;
|
|
405
452
|
});
|
|
406
453
|
}
|
package/src/state/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export {
|
|
|
12
12
|
type ChatHistoryPage,
|
|
13
13
|
type ChatPage
|
|
14
14
|
} from './chats';
|
|
15
|
+
|
|
15
16
|
export {
|
|
16
17
|
useProviderAccountsStore,
|
|
17
18
|
useProviderAccounts,
|
|
@@ -20,5 +21,6 @@ export {
|
|
|
20
21
|
watchProviderAccounts,
|
|
21
22
|
type ProviderAccountsRow
|
|
22
23
|
} from './provider-accounts';
|
|
24
|
+
|
|
23
25
|
export { useProvidersStore, useProviders, providersOf, providerSinkFor, type ProvidersRow } from './providers';
|
|
24
26
|
export { type UsageMetric, USAGE_PREFERENCES_KEY, dayOf, windowFor, reloadUsagePreferences, useUsageStore, type UsagePeriod, type UsageView } from './usage';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { create } from 'zustand';
|
|
2
2
|
import type { ProviderAccounts } from '@adecore/agent-contracts';
|
|
3
3
|
import { useChatScope } from '../scope';
|
|
4
|
-
import type
|
|
4
|
+
import { whenOpen, type ChatTransport } from '../transport';
|
|
5
5
|
|
|
6
6
|
export interface ProviderAccountsRow {
|
|
7
7
|
/* Null until the host answered, and for a host too old to keep accounts. */
|
|
@@ -33,6 +33,7 @@ export const useProviderAccountsStore = create<ProviderAccountsStore>((set, get)
|
|
|
33
33
|
/* The accounts of the host in scope, like `useProviders`. */
|
|
34
34
|
export function useProviderAccounts<T>(select: (row: ProviderAccountsRow) => T): T {
|
|
35
35
|
const { id } = useChatScope();
|
|
36
|
+
|
|
36
37
|
return useProviderAccountsStore((s) => select(s.byScope[id] ?? NONE));
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -53,8 +54,10 @@ export function watchProviderAccounts(scopeId: string, transport: ChatTransport)
|
|
|
53
54
|
const store = useProviderAccountsStore.getState();
|
|
54
55
|
// A list asked for before a change arrived is older than that change, so it is dropped.
|
|
55
56
|
let changes = 0;
|
|
57
|
+
|
|
56
58
|
const load = (): void => {
|
|
57
59
|
const asked = changes;
|
|
60
|
+
|
|
58
61
|
transport
|
|
59
62
|
.request('accounts.list', {})
|
|
60
63
|
.then((accounts) => {
|
|
@@ -69,19 +72,14 @@ export function watchProviderAccounts(scopeId: string, transport: ChatTransport)
|
|
|
69
72
|
}
|
|
70
73
|
});
|
|
71
74
|
};
|
|
75
|
+
|
|
72
76
|
const off = [
|
|
73
77
|
transport.on('accounts.changed', (accounts) => {
|
|
74
78
|
changes += 1;
|
|
75
79
|
store.set(scopeId, accounts);
|
|
76
80
|
}),
|
|
77
|
-
transport
|
|
78
|
-
if (status === 'open') {
|
|
79
|
-
load();
|
|
80
|
-
}
|
|
81
|
-
})
|
|
81
|
+
whenOpen(transport, load)
|
|
82
82
|
];
|
|
83
|
-
|
|
84
|
-
load();
|
|
85
|
-
}
|
|
83
|
+
|
|
86
84
|
return () => off.forEach((stop) => stop());
|
|
87
85
|
}
|
package/src/state/providers.ts
CHANGED
|
@@ -34,6 +34,7 @@ export const useProvidersStore = create<ProvidersStore>((set, get) => ({
|
|
|
34
34
|
*/
|
|
35
35
|
export function useProviders<T>(select: (row: ProvidersRow) => T): T {
|
|
36
36
|
const { id } = useChatScope();
|
|
37
|
+
|
|
37
38
|
return useProvidersStore((s) => select(s.byScope[id] ?? NONE));
|
|
38
39
|
}
|
|
39
40
|
|
package/src/state/usage.ts
CHANGED
|
@@ -19,6 +19,7 @@ export const USAGE_PERIODS: readonly { id: UsagePeriod; days: number }[] = [
|
|
|
19
19
|
const storageKey = (): string => chatStorageKey('usage');
|
|
20
20
|
|
|
21
21
|
export let USAGE_PREFERENCES_KEY = storageKey();
|
|
22
|
+
|
|
22
23
|
onChatStorageConfiguration(() => {
|
|
23
24
|
USAGE_PREFERENCES_KEY = storageKey();
|
|
24
25
|
});
|
|
@@ -35,6 +36,7 @@ const DEFAULTS: Preferences = { period: '7d', metric: 'cost', currency: 'USD' };
|
|
|
35
36
|
function parsePreferences(raw: string | null): Preferences {
|
|
36
37
|
try {
|
|
37
38
|
const stored = JSON.parse(raw ?? '{}') as Partial<Preferences>;
|
|
39
|
+
|
|
38
40
|
return {
|
|
39
41
|
period: USAGE_PERIODS.some((period) => period.id === stored.period) ? stored.period! : DEFAULTS.period,
|
|
40
42
|
metric: stored.metric === 'tokens' || stored.metric === 'cost' ? stored.metric : DEFAULTS.metric,
|
|
@@ -65,6 +67,7 @@ export function dayOf(date: Date): string {
|
|
|
65
67
|
export function windowFor(period: UsagePeriod, now = new Date()): UsageSummaryPayload {
|
|
66
68
|
const days = USAGE_PERIODS.find((entry) => entry.id === period)?.days ?? 7;
|
|
67
69
|
const from = new Date(now.getFullYear(), now.getMonth(), now.getDate() - (days - 1));
|
|
70
|
+
|
|
68
71
|
return {
|
|
69
72
|
from: dayOf(from),
|
|
70
73
|
to: dayOf(now),
|
|
@@ -129,9 +132,11 @@ export const useUsageStore = createChatStore<UsageStore>(
|
|
|
129
132
|
storage.write({ ...chosen(get()), ...change });
|
|
130
133
|
set(change);
|
|
131
134
|
};
|
|
135
|
+
|
|
132
136
|
const patchRow = (scopeId: string, change: Partial<UsageRow>): void => {
|
|
133
137
|
set({ byScope: { ...get().byScope, [scopeId]: { ...(get().byScope[scopeId] ?? EMPTY), ...change } } });
|
|
134
138
|
};
|
|
139
|
+
|
|
135
140
|
return {
|
|
136
141
|
...DEFAULTS,
|
|
137
142
|
byScope: {},
|
|
@@ -177,5 +182,6 @@ export type UsageView = Preferences & UsageRow;
|
|
|
177
182
|
|
|
178
183
|
export function useUsage<T>(select: (view: UsageView) => T): T {
|
|
179
184
|
const { id } = useChatScope();
|
|
185
|
+
|
|
180
186
|
return useUsageStore((s) => select({ period: s.period, metric: s.metric, currency: s.currency, ...(s.byScope[id] ?? EMPTY) }));
|
|
181
187
|
}
|
package/src/storage.ts
CHANGED
|
@@ -37,12 +37,14 @@ export function configureChatStorage(configuration: ChatStorageOptions): void {
|
|
|
37
37
|
if (hydrated) {
|
|
38
38
|
throw new Error('Configure chat storage before using chat stores');
|
|
39
39
|
}
|
|
40
|
+
|
|
40
41
|
options = {
|
|
41
42
|
...configuration,
|
|
42
43
|
keys: { ...configuration.keys },
|
|
43
44
|
legacyNamespaces: [...(configuration.legacyNamespaces ?? [])],
|
|
44
45
|
legacyKeys: Object.fromEntries(Object.entries(configuration.legacyKeys ?? {}).map(([key, values]) => [key, [...values]]))
|
|
45
46
|
};
|
|
47
|
+
|
|
46
48
|
for (const listener of configurationListeners) {
|
|
47
49
|
listener();
|
|
48
50
|
}
|
|
@@ -60,6 +62,7 @@ export function chatStorageAdapter(): ChatStorageAdapter | null {
|
|
|
60
62
|
if (options.storage !== undefined) {
|
|
61
63
|
return options.storage;
|
|
62
64
|
}
|
|
65
|
+
|
|
63
66
|
try {
|
|
64
67
|
return globalThis.localStorage;
|
|
65
68
|
} catch {
|
|
@@ -71,7 +74,9 @@ export function hydrateChatStorage(): void {
|
|
|
71
74
|
if (hydrated) {
|
|
72
75
|
return;
|
|
73
76
|
}
|
|
77
|
+
|
|
74
78
|
hydrated = true;
|
|
79
|
+
|
|
75
80
|
for (const hydrate of hydrators) {
|
|
76
81
|
hydrate();
|
|
77
82
|
}
|
|
@@ -80,34 +85,47 @@ export function hydrateChatStorage(): void {
|
|
|
80
85
|
export function createChatStore<T>(initial: StateCreator<T>, hydrate: (store: StoreApi<T>) => void): UseBoundStore<StoreApi<T>> {
|
|
81
86
|
const store = create<T>(initial);
|
|
82
87
|
let initialized = false;
|
|
88
|
+
|
|
83
89
|
const hydrateOnce = (): void => {
|
|
84
90
|
if (initialized) {
|
|
85
91
|
return;
|
|
86
92
|
}
|
|
93
|
+
|
|
87
94
|
initialized = true;
|
|
88
95
|
hydrate(store);
|
|
89
96
|
};
|
|
97
|
+
|
|
90
98
|
hydrators.push(hydrateOnce);
|
|
99
|
+
|
|
91
100
|
const ready = (): void => {
|
|
92
101
|
hydrateChatStorage();
|
|
93
102
|
hydrateOnce();
|
|
94
103
|
};
|
|
104
|
+
|
|
95
105
|
const useStore = ((...args: Parameters<typeof store>) => {
|
|
96
106
|
ready();
|
|
107
|
+
|
|
97
108
|
return store(...args);
|
|
98
109
|
}) as typeof store;
|
|
110
|
+
|
|
99
111
|
Object.assign(useStore, store);
|
|
112
|
+
|
|
100
113
|
useStore.getState = () => {
|
|
101
114
|
ready();
|
|
115
|
+
|
|
102
116
|
return store.getState();
|
|
103
117
|
};
|
|
118
|
+
|
|
104
119
|
useStore.subscribe = (...args) => {
|
|
105
120
|
ready();
|
|
121
|
+
|
|
106
122
|
return store.subscribe(...args);
|
|
107
123
|
};
|
|
124
|
+
|
|
108
125
|
useStore.setState = ((...args: Parameters<typeof store.setState>) => {
|
|
109
126
|
ready();
|
|
110
127
|
store.setState(...args);
|
|
111
128
|
}) as typeof store.setState;
|
|
129
|
+
|
|
112
130
|
return useStore;
|
|
113
131
|
}
|
package/src/testing/prompts.ts
CHANGED
|
@@ -11,6 +11,7 @@ const endpoints: ChatQuestion = {
|
|
|
11
11
|
{ label: 'Only chat and files', description: 'Keep terminal retries separate' }
|
|
12
12
|
]
|
|
13
13
|
};
|
|
14
|
+
|
|
14
15
|
const checks: ChatQuestion = {
|
|
15
16
|
id: 'checks',
|
|
16
17
|
header: 'Validation',
|
|
@@ -22,10 +23,13 @@ const checks: ChatQuestion = {
|
|
|
22
23
|
{ label: 'Build', description: 'Verify the production bundle' }
|
|
23
24
|
]
|
|
24
25
|
};
|
|
26
|
+
|
|
25
27
|
const notes: ChatQuestion = { id: 'notes', header: 'Instructions', question: 'What else should the agent keep in mind?', multiSelect: false, choices: [] };
|
|
28
|
+
|
|
26
29
|
function base(id: string) {
|
|
27
30
|
return { id: `preview-${id}`, requestId: `preview-${id}`, createdAt: 0, turnId: null };
|
|
28
31
|
}
|
|
32
|
+
|
|
29
33
|
function question(id: string, questions: ChatQuestion[], async = false): ChatQuestionItem {
|
|
30
34
|
return {
|
|
31
35
|
...base(id),
|
|
@@ -36,6 +40,7 @@ function question(id: string, questions: ChatQuestion[], async = false): ChatQue
|
|
|
36
40
|
async
|
|
37
41
|
};
|
|
38
42
|
}
|
|
43
|
+
|
|
39
44
|
function approval(long = false): ChatApprovalItem {
|
|
40
45
|
return {
|
|
41
46
|
...base(long ? 'long' : 'edit'),
|
package/src/transport.ts
CHANGED
|
@@ -43,8 +43,26 @@ export function errorCode(error: unknown): string | null {
|
|
|
43
43
|
return typeof error === 'object' && error !== null && 'code' in error && typeof error.code === 'string' ? error.code : null;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/* Runs `run` now while the link is up and again each time it comes back; answers the function that stops it. */
|
|
47
|
+
export function whenOpen(transport: ChatTransport, run: () => void): () => void {
|
|
48
|
+
if (transport.status === 'open') {
|
|
49
|
+
run();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return transport.subscribeStatus((status) => {
|
|
53
|
+
if (status === 'open') {
|
|
54
|
+
run();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function errorMessage(error: unknown): string {
|
|
60
|
+
return error instanceof Error ? error.message : String(error);
|
|
61
|
+
}
|
|
62
|
+
|
|
46
63
|
/* Whether a request failed because the link was down rather than because the host refused it; the reconnect asks again. */
|
|
47
64
|
export function isConnectionError(error: unknown): boolean {
|
|
48
65
|
const code = errorCode(error);
|
|
66
|
+
|
|
49
67
|
return code === 'not-connected' || code === 'disconnected';
|
|
50
68
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { UsageProvider } from '@adecore/agent-contracts';
|
|
2
|
+
import { ProviderLogo } from '../agents/ProviderLogo';
|
|
3
|
+
import { PROVIDER_COLORS } from './format';
|
|
4
|
+
|
|
5
|
+
/* A provider's logo in its color of the chart, so it reads as the series it names. */
|
|
6
|
+
export function ChartLogo({ provider, className }: { provider: UsageProvider; className?: string }) {
|
|
7
|
+
return (
|
|
8
|
+
<span className={className} style={{ color: PROVIDER_COLORS[provider] }}>
|
|
9
|
+
<ProviderLogo provider={provider} />
|
|
10
|
+
</span>
|
|
11
|
+
);
|
|
12
|
+
}
|
package/src/usage/LimitsList.tsx
CHANGED
|
@@ -6,8 +6,8 @@ import { limitsAccountId } from '../agents/account-limits';
|
|
|
6
6
|
import { AccountDot } from '../agents/AccountDot';
|
|
7
7
|
import { Tooltip } from '@adecore/ui';
|
|
8
8
|
import { formatClock, formatWeekdayClock, isSameDay, formatCountdown, formatPercent } from '@adecore/ui/format';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { ChartLogo } from './ChartLogo';
|
|
10
|
+
import { PROVIDER_LABELS } from './format';
|
|
11
11
|
import { accountNote, evenPace, explain, isSignedOut, nextReset, paceText, type LimitAccount, type LimitGroup, type PaceStanding } from './limit-groups';
|
|
12
12
|
|
|
13
13
|
/* Red where a window is nearly spent, amber where it is worth knowing; room to spare is not news and takes the text color. */
|
|
@@ -115,12 +115,11 @@ export function LimitsList({ limits, now, compact = false }: LimitsListProps) {
|
|
|
115
115
|
<div className={clsx('flex flex-col', compact ? 'gap-4' : 'gap-6')}>
|
|
116
116
|
{limits.providers.map((provider) => {
|
|
117
117
|
const note = explain(provider);
|
|
118
|
+
|
|
118
119
|
return (
|
|
119
120
|
<div key={limitsAccountId(provider)} className="flex flex-col gap-2">
|
|
120
121
|
<p className="flex items-center gap-2 text-xs font-medium">
|
|
121
|
-
<
|
|
122
|
-
<ProviderLogo provider={provider.kind} />
|
|
123
|
-
</span>
|
|
122
|
+
<ChartLogo provider={provider.kind} />
|
|
124
123
|
{PROVIDER_LABELS[provider.kind]}
|
|
125
124
|
{provider.plan !== null && <span className="text-text-muted">· {provider.plan}</span>}
|
|
126
125
|
</p>
|
|
@@ -145,6 +144,7 @@ function WindowLine({ window, now }: { window: UsageWindow; now: number }) {
|
|
|
145
144
|
const { t } = useTranslation('agent-usage');
|
|
146
145
|
const percent = Math.round(window.used * 100);
|
|
147
146
|
const mark = evenPace(window, now)?.percent ?? null;
|
|
147
|
+
|
|
148
148
|
return (
|
|
149
149
|
<>
|
|
150
150
|
<span className="text-text-muted">{window.label}</span>
|
|
@@ -179,6 +179,7 @@ function AccountLines({ account, now }: { account: LimitAccount; now: number })
|
|
|
179
179
|
const windows = signedOut || note !== null ? [] : (account.entry?.windows ?? []);
|
|
180
180
|
const next = nextReset(windows, now);
|
|
181
181
|
const resetsIn = next === null ? null : resetInLabel(next.resetsAt, now);
|
|
182
|
+
|
|
182
183
|
return (
|
|
183
184
|
<>
|
|
184
185
|
<p className="col-span-full mt-2 flex min-w-0 items-center gap-2">
|
|
@@ -214,9 +215,7 @@ export function AccountLimitsList({ groups, now }: { groups: readonly LimitGroup
|
|
|
214
215
|
{groups.map((group) => (
|
|
215
216
|
<Fragment key={group.kind}>
|
|
216
217
|
<p className="col-span-full mt-3 flex items-center gap-2 font-medium first:mt-0">
|
|
217
|
-
<
|
|
218
|
-
<ProviderLogo provider={group.kind} />
|
|
219
|
-
</span>
|
|
218
|
+
<ChartLogo provider={group.kind} />
|
|
220
219
|
{PROVIDER_LABELS[group.kind]}
|
|
221
220
|
</p>
|
|
222
221
|
{group.accounts.map((account) => (
|