@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.
Files changed (317) hide show
  1. package/dist/agents/AgentModelLabel.js +2 -1
  2. package/dist/agents/model-name.js +2 -1
  3. package/dist/agents/provider-paths.d.ts +2 -1
  4. package/dist/agents/provider-paths.js +7 -3
  5. package/dist/chat/account-choice.js +57 -28
  6. package/dist/chat/bookmarks.js +2 -1
  7. package/dist/chat/drafts.d.ts +0 -1
  8. package/dist/chat/drafts.js +9 -28
  9. package/dist/chat/logic/picker-entries.js +2 -8
  10. package/dist/chat/logic/resume-compaction.js +2 -5
  11. package/dist/chat/logic/timeline-copy.js +2 -8
  12. package/dist/chat/logic/timeline.js +9 -10
  13. package/dist/chat/logic/workflow.js +2 -4
  14. package/dist/chat/subagent-conversation.js +2 -1
  15. package/dist/chat/ui/GoalDock.js +2 -1
  16. package/dist/chat/ui/LimitState.js +4 -6
  17. package/dist/chat/ui/composer/use-continuable-turn.js +0 -1
  18. package/dist/chat/ui/composer/use-notice.js +2 -1
  19. package/dist/chat/ui/composer/use-prompt-history.js +1 -1
  20. package/dist/chat/ui/composer/use-prompt-menus.js +216 -253
  21. package/dist/chat/ui/composer/use-take-back.js +2 -2
  22. package/dist/chat/ui/diff/ApprovalReview.js +1 -1
  23. package/dist/chat/ui/highlighter.d.ts +0 -6
  24. package/dist/chat/ui/highlighter.js +0 -12
  25. package/dist/chat/ui/icons.d.ts +0 -1
  26. package/dist/chat/ui/icons.js +0 -3
  27. package/dist/chat/ui/rows/MessageRows.js +2 -5
  28. package/dist/chat/ui/rows/row-rhythm.js +2 -5
  29. package/dist/chat/ui/theme-watch.js +3 -7
  30. package/dist/prompts/logic/usePromptSession.js +2 -1
  31. package/dist/providers/AddAccountForm.js +2 -1
  32. package/dist/providers/CliDetail.js +104 -130
  33. package/dist/providers/account-actions.js +6 -9
  34. package/dist/state/chats.d.ts +1 -0
  35. package/dist/state/chats.js +6 -11
  36. package/dist/state/provider-accounts.d.ts +1 -1
  37. package/dist/state/provider-accounts.js +2 -8
  38. package/dist/transport.d.ts +2 -0
  39. package/dist/transport.js +14 -0
  40. package/dist/usage/ChartLogo.d.ts +5 -0
  41. package/dist/usage/ChartLogo.js +48 -0
  42. package/dist/usage/LimitsList.js +6 -16
  43. package/dist/usage/UsageBreakdown.js +17 -40
  44. package/dist/usage/UsageLimits.js +183 -211
  45. package/dist/usage/UsagePage.js +3 -17
  46. package/dist/usage/UsageSummary.js +4 -9
  47. package/dist/usage/limits.js +16 -31
  48. package/dist/usage/summary.d.ts +1 -1
  49. package/dist/usage/summary.js +3 -5
  50. package/package.json +27 -27
  51. package/src/agents/AccountDot.tsx +1 -0
  52. package/src/agents/AgentIcon.tsx +2 -0
  53. package/src/agents/AgentModelLabel.tsx +4 -6
  54. package/src/agents/ModelOptionControl.tsx +8 -0
  55. package/src/agents/account-limits.ts +3 -0
  56. package/src/agents/accounts.ts +12 -0
  57. package/src/agents/model-name.ts +13 -1
  58. package/src/agents/model-options.ts +2 -0
  59. package/src/agents/model-requirement.ts +4 -0
  60. package/src/agents/option-words.ts +2 -0
  61. package/src/agents/provider-paths.ts +8 -3
  62. package/src/agents/status-look.ts +1 -0
  63. package/src/chat/account-choice.ts +25 -19
  64. package/src/chat/actions.ts +5 -0
  65. package/src/chat/attachments.ts +8 -0
  66. package/src/chat/bookmarks.ts +13 -1
  67. package/src/chat/chat-client.ts +63 -0
  68. package/src/chat/chat-references.ts +3 -0
  69. package/src/chat/composer-effort.ts +5 -0
  70. package/src/chat/composer-send.ts +4 -0
  71. package/src/chat/drafts.ts +21 -17
  72. package/src/chat/forks.ts +4 -0
  73. package/src/chat/guards.ts +2 -0
  74. package/src/chat/index.ts +7 -0
  75. package/src/chat/logic/ansi.ts +47 -0
  76. package/src/chat/logic/bookmarks.ts +7 -0
  77. package/src/chat/logic/command-looks.ts +29 -0
  78. package/src/chat/logic/context-usage.ts +3 -0
  79. package/src/chat/logic/continue-after-stop.ts +5 -0
  80. package/src/chat/logic/effort.ts +4 -0
  81. package/src/chat/logic/embedded-code.ts +63 -0
  82. package/src/chat/logic/fork.ts +22 -0
  83. package/src/chat/logic/generated-image.ts +7 -0
  84. package/src/chat/logic/goal.ts +7 -0
  85. package/src/chat/logic/handback.ts +2 -0
  86. package/src/chat/logic/limit.ts +8 -0
  87. package/src/chat/logic/picker-entries.ts +16 -4
  88. package/src/chat/logic/resume-compaction.ts +22 -5
  89. package/src/chat/logic/scrubber.ts +38 -5
  90. package/src/chat/logic/thought.ts +5 -0
  91. package/src/chat/logic/thread-cards.ts +6 -0
  92. package/src/chat/logic/timeline-copy.ts +4 -9
  93. package/src/chat/logic/timeline-target.ts +15 -0
  94. package/src/chat/logic/timeline.ts +113 -10
  95. package/src/chat/logic/tools.ts +30 -0
  96. package/src/chat/logic/visual-height.ts +5 -0
  97. package/src/chat/logic/visual-viewport.ts +1 -0
  98. package/src/chat/logic/welcome.ts +3 -0
  99. package/src/chat/logic/workflow.ts +26 -7
  100. package/src/chat/mentions.ts +56 -0
  101. package/src/chat/pending-prompts.ts +16 -0
  102. package/src/chat/persisted-json.ts +15 -0
  103. package/src/chat/preferences.ts +16 -0
  104. package/src/chat/prompt-hold.ts +2 -0
  105. package/src/chat/quote.ts +7 -0
  106. package/src/chat/reading-width.ts +1 -0
  107. package/src/chat/recent-messages.ts +5 -0
  108. package/src/chat/resume-compaction-dismissals.ts +1 -0
  109. package/src/chat/stash.ts +12 -0
  110. package/src/chat/subagent-conversation.ts +24 -4
  111. package/src/chat/subagent-list.ts +14 -0
  112. package/src/chat/subagent-support.ts +1 -0
  113. package/src/chat/subagent-view.ts +9 -0
  114. package/src/chat/thumbnails.ts +14 -0
  115. package/src/chat/timeline-flash.ts +2 -0
  116. package/src/chat/timeline-scroll.ts +23 -0
  117. package/src/chat/ui/AccountPill.tsx +4 -0
  118. package/src/chat/ui/AnsiOutput.tsx +1 -0
  119. package/src/chat/ui/BookmarkMarker.tsx +5 -0
  120. package/src/chat/ui/BookmarkSubmenu.tsx +1 -0
  121. package/src/chat/ui/ChatActivity.tsx +13 -0
  122. package/src/chat/ui/CodeBlock.tsx +8 -0
  123. package/src/chat/ui/Composer.tsx +15 -0
  124. package/src/chat/ui/ComposerInput.tsx +22 -0
  125. package/src/chat/ui/DiffPool.tsx +3 -0
  126. package/src/chat/ui/EditDiff.tsx +5 -0
  127. package/src/chat/ui/GoalDock.tsx +13 -1
  128. package/src/chat/ui/ImageView.tsx +11 -0
  129. package/src/chat/ui/LimitState.tsx +23 -6
  130. package/src/chat/ui/Markdown.tsx +41 -0
  131. package/src/chat/ui/MermaidBlock.tsx +16 -0
  132. package/src/chat/ui/MessageActions.tsx +8 -0
  133. package/src/chat/ui/ModelMarks.tsx +1 -0
  134. package/src/chat/ui/Pickers.tsx +26 -0
  135. package/src/chat/ui/PromptComposer.tsx +2 -0
  136. package/src/chat/ui/PromptEditor.tsx +6 -0
  137. package/src/chat/ui/QuoteButton.tsx +12 -0
  138. package/src/chat/ui/ReferenceChip.tsx +3 -0
  139. package/src/chat/ui/ResumeCompactionDock.tsx +1 -0
  140. package/src/chat/ui/RowTime.tsx +1 -0
  141. package/src/chat/ui/RunSettings.tsx +38 -0
  142. package/src/chat/ui/Scrubber.tsx +14 -0
  143. package/src/chat/ui/SessionLine.tsx +1 -0
  144. package/src/chat/ui/ShellCommand.tsx +32 -0
  145. package/src/chat/ui/SubagentControls.tsx +7 -0
  146. package/src/chat/ui/SubagentInfo.tsx +1 -0
  147. package/src/chat/ui/SubagentStopButton.tsx +6 -0
  148. package/src/chat/ui/SubagentTimeline.tsx +10 -0
  149. package/src/chat/ui/Timeline.tsx +119 -0
  150. package/src/chat/ui/TimelineMenu.tsx +5 -0
  151. package/src/chat/ui/UnifiedDiff.tsx +6 -0
  152. package/src/chat/ui/UploadThumb.tsx +2 -0
  153. package/src/chat/ui/VisualDialogs.tsx +3 -0
  154. package/src/chat/ui/VisualFrame.tsx +15 -0
  155. package/src/chat/ui/Welcome.tsx +1 -0
  156. package/src/chat/ui/chip-clipboard.ts +20 -0
  157. package/src/chat/ui/code-lines.ts +14 -0
  158. package/src/chat/ui/code-theme.ts +1 -0
  159. package/src/chat/ui/composer/ComposerControls.tsx +5 -0
  160. package/src/chat/ui/composer/ComposerMenu.tsx +12 -0
  161. package/src/chat/ui/composer/DraftParts.tsx +2 -0
  162. package/src/chat/ui/composer/QueuedMessages.tsx +4 -0
  163. package/src/chat/ui/composer/chip-paste.ts +16 -0
  164. package/src/chat/ui/composer/chips.ts +6 -0
  165. package/src/chat/ui/composer/editor.ts +9 -0
  166. package/src/chat/ui/composer/insert.ts +2 -0
  167. package/src/chat/ui/composer/key-handler.ts +39 -0
  168. package/src/chat/ui/composer/keys.ts +23 -0
  169. package/src/chat/ui/composer/placeholder.ts +6 -0
  170. package/src/chat/ui/composer/prompt-picks.ts +1 -0
  171. package/src/chat/ui/composer/surface.ts +5 -0
  172. package/src/chat/ui/composer/use-chat-controls.ts +13 -0
  173. package/src/chat/ui/composer/use-compaction-offer.ts +3 -0
  174. package/src/chat/ui/composer/use-composer-menus.ts +2 -0
  175. package/src/chat/ui/composer/use-composer-submit.ts +27 -0
  176. package/src/chat/ui/composer/use-continuable-turn.ts +3 -5
  177. package/src/chat/ui/composer/use-draft-files.ts +24 -0
  178. package/src/chat/ui/composer/use-effort-stepper.ts +8 -0
  179. package/src/chat/ui/composer/use-notice.ts +4 -1
  180. package/src/chat/ui/composer/use-pending-requests.ts +3 -0
  181. package/src/chat/ui/composer/use-prompt-history.ts +20 -11
  182. package/src/chat/ui/composer/use-prompt-hold.ts +3 -0
  183. package/src/chat/ui/composer/use-prompt-menus.ts +34 -14
  184. package/src/chat/ui/composer/use-take-back.ts +20 -2
  185. package/src/chat/ui/diff/ApprovalReview.tsx +18 -15
  186. package/src/chat/ui/diff/DiffFallbackBoundary.tsx +1 -0
  187. package/src/chat/ui/diff/DiffOmitted.tsx +1 -0
  188. package/src/chat/ui/diff/DiffStack.tsx +2 -0
  189. package/src/chat/ui/diff/DiffUnavailable.tsx +1 -0
  190. package/src/chat/ui/diff/EditorEditDiff.tsx +13 -0
  191. package/src/chat/ui/diff/EditorPatchDiff.tsx +4 -0
  192. package/src/chat/ui/diff/LegacyDiffPool.tsx +2 -0
  193. package/src/chat/ui/diff/LegacyEditDiff.tsx +2 -0
  194. package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +8 -0
  195. package/src/chat/ui/diff/RawDiff.tsx +1 -0
  196. package/src/chat/ui/diff/ReviewDiff.tsx +1 -0
  197. package/src/chat/ui/diff/diff-source-budget.ts +8 -0
  198. package/src/chat/ui/diff/editor-edit-model.ts +4 -0
  199. package/src/chat/ui/diff/review-plan.ts +9 -0
  200. package/src/chat/ui/diff/review-settle.ts +1 -0
  201. package/src/chat/ui/diff/syntax-runtime.ts +52 -0
  202. package/src/chat/ui/diff/use-review-stack.ts +9 -0
  203. package/src/chat/ui/diff-syntax.ts +12 -0
  204. package/src/chat/ui/diff-theme.ts +2 -0
  205. package/src/chat/ui/file-links.ts +2 -0
  206. package/src/chat/ui/find-reveal.ts +1 -0
  207. package/src/chat/ui/full-diff.ts +1 -0
  208. package/src/chat/ui/highlighter.ts +29 -15
  209. package/src/chat/ui/icons.tsx +2 -4
  210. package/src/chat/ui/index.ts +4 -0
  211. package/src/chat/ui/intelligent-ui/UiBlockFrame.tsx +25 -0
  212. package/src/chat/ui/intelligent-ui/UiFallbackPart.tsx +1 -0
  213. package/src/chat/ui/intelligent-ui/UiReply.tsx +63 -0
  214. package/src/chat/ui/intelligent-ui/block-local.ts +5 -0
  215. package/src/chat/ui/intelligent-ui/chart-data.ts +15 -0
  216. package/src/chat/ui/intelligent-ui/node-text.ts +17 -0
  217. package/src/chat/ui/intelligent-ui/renderers/binding.ts +1 -0
  218. package/src/chat/ui/intelligent-ui/renderers/chart.tsx +31 -0
  219. package/src/chat/ui/intelligent-ui/renderers/choices.tsx +4 -0
  220. package/src/chat/ui/intelligent-ui/renderers/content.tsx +18 -0
  221. package/src/chat/ui/intelligent-ui/renderers/data.tsx +11 -0
  222. package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +13 -0
  223. package/src/chat/ui/intelligent-ui/renderers/links.tsx +9 -0
  224. package/src/chat/ui/intelligent-ui/renderers/map-slots.ts +15 -0
  225. package/src/chat/ui/intelligent-ui/renderers/map-style.ts +15 -0
  226. package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +48 -0
  227. package/src/chat/ui/intelligent-ui/renderers/map.tsx +20 -0
  228. package/src/chat/ui/intelligent-ui/renderers/place-point.ts +3 -0
  229. package/src/chat/ui/intelligent-ui/renderers/structure.tsx +3 -0
  230. package/src/chat/ui/intelligent-ui/renderers/text.tsx +8 -0
  231. package/src/chat/ui/intelligent-ui/reply-parts.ts +5 -0
  232. package/src/chat/ui/intelligent-ui/table-data.ts +17 -0
  233. package/src/chat/ui/intelligent-ui/use-in-view.ts +4 -0
  234. package/src/chat/ui/intelligent-ui/use-later.ts +4 -0
  235. package/src/chat/ui/intelligent-ui/use-truncated.ts +5 -0
  236. package/src/chat/ui/intelligent-ui/use-ui-links.ts +29 -0
  237. package/src/chat/ui/intelligent-ui/use-ui-queries.ts +60 -0
  238. package/src/chat/ui/markdown-blocks.ts +64 -0
  239. package/src/chat/ui/mermaid.ts +4 -0
  240. package/src/chat/ui/quote-selection.ts +10 -0
  241. package/src/chat/ui/rehype-chips.ts +5 -0
  242. package/src/chat/ui/rehype-fade.ts +2 -0
  243. package/src/chat/ui/remark-html-as-text.ts +5 -0
  244. package/src/chat/ui/reveal.ts +18 -0
  245. package/src/chat/ui/rows/ForksRow.tsx +5 -0
  246. package/src/chat/ui/rows/GeneratedImageRow.tsx +10 -0
  247. package/src/chat/ui/rows/MessageRows.tsx +29 -5
  248. package/src/chat/ui/rows/RowContainer.tsx +1 -0
  249. package/src/chat/ui/rows/Rows.tsx +2 -0
  250. package/src/chat/ui/rows/SubagentRow.tsx +13 -0
  251. package/src/chat/ui/rows/TaskRow.tsx +7 -0
  252. package/src/chat/ui/rows/VisualRow.tsx +4 -0
  253. package/src/chat/ui/rows/WorkRows.tsx +46 -0
  254. package/src/chat/ui/rows/WorkflowRow.tsx +10 -0
  255. package/src/chat/ui/rows/row-rhythm.ts +7 -5
  256. package/src/chat/ui/theme-watch.ts +8 -7
  257. package/src/chat/ui/thread-references.ts +7 -0
  258. package/src/chat/ui/use-diff-source-tokens.ts +8 -0
  259. package/src/chat/ui/useToggleSet.ts +3 -0
  260. package/src/chat/ui/visual-theme.ts +30 -0
  261. package/src/chat/visual-bridge.ts +23 -0
  262. package/src/chat/visual-viewport.ts +12 -0
  263. package/src/host-hooks.ts +6 -4
  264. package/src/host.ts +2 -0
  265. package/src/index.ts +3 -0
  266. package/src/lazy.tsx +9 -0
  267. package/src/mounted-registry.ts +4 -0
  268. package/src/port-transport.ts +32 -0
  269. package/src/prompts/index.ts +2 -0
  270. package/src/prompts/logic/focus.ts +5 -0
  271. package/src/prompts/logic/keys.ts +12 -0
  272. package/src/prompts/logic/prompts.ts +8 -0
  273. package/src/prompts/logic/subjects.ts +8 -0
  274. package/src/prompts/logic/useFocusAfterAnswer.ts +2 -0
  275. package/src/prompts/logic/usePromptSession.ts +13 -1
  276. package/src/prompts/ui/ApprovalBody.tsx +2 -0
  277. package/src/prompts/ui/PromptActions.tsx +2 -0
  278. package/src/prompts/ui/PromptCard.tsx +6 -0
  279. package/src/prompts/ui/PromptView.tsx +6 -0
  280. package/src/prompts/ui/QuestionBody.tsx +11 -0
  281. package/src/providers/AccountColors.tsx +1 -0
  282. package/src/providers/AccountDetail.tsx +7 -0
  283. package/src/providers/AccountVariables.tsx +2 -0
  284. package/src/providers/AddAccountForm.tsx +5 -1
  285. package/src/providers/CliDetail.tsx +6 -5
  286. package/src/providers/InstallPage.tsx +11 -0
  287. package/src/providers/ProviderList.tsx +3 -0
  288. package/src/providers/ProvidersPane.tsx +18 -0
  289. package/src/providers/account-actions.ts +10 -8
  290. package/src/providers/install-steps.ts +3 -0
  291. package/src/providers/pane-target.ts +5 -0
  292. package/src/providers/provider-abilities.ts +1 -0
  293. package/src/providers/provider-groups.ts +6 -0
  294. package/src/providers/variables.ts +12 -0
  295. package/src/scope.ts +2 -0
  296. package/src/state/chats.ts +61 -14
  297. package/src/state/index.ts +2 -0
  298. package/src/state/provider-accounts.ts +7 -9
  299. package/src/state/providers.ts +1 -0
  300. package/src/state/usage.ts +6 -0
  301. package/src/storage.ts +18 -0
  302. package/src/testing/prompts.ts +5 -0
  303. package/src/transport.ts +18 -0
  304. package/src/usage/ChartLogo.tsx +12 -0
  305. package/src/usage/LimitsList.tsx +7 -8
  306. package/src/usage/UsageBreakdown.tsx +11 -4
  307. package/src/usage/UsageChart.tsx +5 -0
  308. package/src/usage/UsageLimits.tsx +10 -7
  309. package/src/usage/UsageLimitsCard.tsx +2 -0
  310. package/src/usage/UsagePage.tsx +16 -19
  311. package/src/usage/UsageSummary.tsx +4 -5
  312. package/src/usage/UsageTiles.tsx +2 -0
  313. package/src/usage/format.ts +7 -0
  314. package/src/usage/limit-groups.ts +14 -0
  315. package/src/usage/limits.ts +11 -13
  316. package/src/usage/money.ts +1 -0
  317. package/src/usage/summary.ts +15 -7
@@ -1,8 +1,9 @@
1
1
  import { c as _c } from "react/compiler-runtime";
2
2
  import { useEffect } from 'react';
3
3
  import { useChatScope } from '../scope.js';
4
- import { useProviderAccountsStore } from '../state/provider-accounts.js';
4
+ import { useProviderAccounts } from '../state/provider-accounts.js';
5
5
  import { useUsage, useUsageStore } from '../state/usage.js';
6
+ import { whenOpen } from '../transport.js';
6
7
  import { limitGroups } from './limit-groups.js';
7
8
  /* Asking the CLIs is expensive, so everything that shows the windows shares one read per host. */
8
9
  const holds = new Map();
@@ -10,19 +11,11 @@ const holds = new Map();
10
11
  function hold(scope) {
11
12
  const link = scope.transport;
12
13
  const scopeId = scope.id;
13
- const ask = () => {
14
+ const offStatus = whenOpen(link, () => {
14
15
  link.request('usage.limits', {}).then(snapshot => useUsageStore.getState().setLimits(scopeId, snapshot)).catch(() => undefined);
15
- };
16
- if (link.status === 'open') {
17
- ask();
18
- }
16
+ });
19
17
  // A running turn reports its own numbers, which is what keeps a bar moving between reads.
20
18
  const offChanged = link.on('usage.limitsChanged', snapshot => useUsageStore.getState().setLimits(scopeId, snapshot));
21
- const offStatus = link.subscribeStatus(status => {
22
- if (status === 'open') {
23
- ask();
24
- }
25
- });
26
19
  return {
27
20
  readers: 0,
28
21
  release: () => {
@@ -67,27 +60,19 @@ function _temp(s) {
67
60
  return s.limits;
68
61
  }
69
62
  export function useLimitGroups(limits) {
70
- const $ = _c(5);
71
- const {
72
- id
73
- } = useChatScope();
63
+ const $ = _c(3);
64
+ const accounts = useProviderAccounts(_temp2);
74
65
  let t0;
75
- if ($[0] !== id) {
76
- t0 = s => s.byScope[id]?.accounts ?? null;
77
- $[0] = id;
78
- $[1] = t0;
79
- } else {
80
- t0 = $[1];
81
- }
82
- const accounts = useProviderAccountsStore(t0);
83
- let t1;
84
- if ($[2] !== accounts || $[3] !== limits) {
85
- t1 = limits === null ? [] : limitGroups(limits, accounts);
86
- $[2] = accounts;
87
- $[3] = limits;
88
- $[4] = t1;
66
+ if ($[0] !== accounts || $[1] !== limits) {
67
+ t0 = limits === null ? [] : limitGroups(limits, accounts);
68
+ $[0] = accounts;
69
+ $[1] = limits;
70
+ $[2] = t0;
89
71
  } else {
90
- t1 = $[4];
72
+ t0 = $[2];
91
73
  }
92
- return t1;
74
+ return t0;
75
+ }
76
+ function _temp2(row) {
77
+ return row.accounts;
93
78
  }
@@ -1,5 +1,5 @@
1
1
  import type { UsageProvider, UsageSummaryResult, UsageTotals } from '@adecore/agent-contracts';
2
- import type { UsageMetric } from '../state/usage.js';
2
+ import { type UsageMetric } from '../state/usage.js';
3
3
  export interface ChartSlot {
4
4
  slot: string;
5
5
  byProvider: Partial<Record<UsageProvider, number>>;
@@ -1,3 +1,4 @@
1
+ import { dayOf } from '../state/usage.js';
1
2
  import { EMPTY_TOTALS, USAGE_PROVIDERS, addTotals, totalTokensOf } from '@adecore/agent-contracts';
2
3
  /* Three or four round numbers up the side. The one piece of arithmetic a chart library would bring. */
3
4
  export function niceScale(max, ticks = 4) {
@@ -16,9 +17,6 @@ export function niceScale(max, ticks = 4) {
16
17
  step
17
18
  };
18
19
  }
19
- function pad(value) {
20
- return String(value).padStart(2, '0');
21
- }
22
20
  /*
23
21
  * Every slot the period has, whether anything happened in it or not: an empty day keeps its place on
24
22
  * the axis, so a gap in the work reads as a gap and not as a shorter week.
@@ -27,14 +25,14 @@ export function enumerateSlots(summary) {
27
25
  if (summary.resolution === 'hour') {
28
26
  return Array.from({
29
27
  length: 24
30
- }, (_, hour) => `${summary.to}T${pad(hour)}`);
28
+ }, (_, hour) => `${summary.to}T${String(hour).padStart(2, '0')}`);
31
29
  }
32
30
  const slots = [];
33
31
  const at = new Date(`${summary.from}T00:00:00`);
34
32
  const end = new Date(`${summary.to}T00:00:00`);
35
33
  // A calendar loop rather than adding milliseconds, since a day is 23 or 25 hours long twice a year.
36
34
  while (at <= end && slots.length < 400) {
37
- slots.push(`${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())}`);
35
+ slots.push(dayOf(at));
38
36
  at.setDate(at.getDate() + 1);
39
37
  }
40
38
  return slots;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/agents-react",
3
- "version": "0.39.0",
3
+ "version": "0.40.1",
4
4
  "description": "React agent chat threads, composers, approvals, provider accounts and usage views.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -67,49 +67,49 @@
67
67
  "test:visuals": "bun --config=integration.bunfig.toml --conditions=source test ./src/chat/visual-viewport.integration.test.ts"
68
68
  },
69
69
  "dependencies": {
70
- "@adecore/agent-contracts": "0.39.0",
71
- "@adecore/editor": "0.39.0",
72
- "@adecore/editor-react": "0.39.0",
73
- "@adecore/intelligent-ui": "0.39.0",
74
- "@adecore/merge": "0.39.0",
70
+ "@adecore/agent-contracts": "0.40.1",
71
+ "@adecore/editor": "0.40.1",
72
+ "@adecore/editor-react": "0.40.1",
73
+ "@adecore/intelligent-ui": "0.40.1",
74
+ "@adecore/merge": "0.40.1",
75
75
  "@codemirror/commands": "^6.11.1",
76
- "@codemirror/language": "^6.12.4",
76
+ "@codemirror/language": "^6.13.1",
77
77
  "@codemirror/state": "^6.7.6",
78
- "@codemirror/view": "^6.43.13",
79
- "@lezer/common": "^1.5.2",
80
- "@lezer/highlight": "^1.2.3",
81
- "@lezer/markdown": "^1.7.2",
82
- "@pierre/diffs": "^1.4.3",
83
- "@tanstack/react-virtual": "^3.14.13",
78
+ "@codemirror/view": "^6.43.15",
79
+ "@lezer/common": "^1.5.4",
80
+ "@lezer/highlight": "^1.2.5",
81
+ "@lezer/markdown": "^1.8.1",
82
+ "@pierre/diffs": "^1.6.1",
83
+ "@tanstack/react-virtual": "^3.14.23",
84
84
  "clsx": "^2.1.1",
85
- "lucide-react": "^1.47.0",
86
- "maplibre-gl": "^6.14.0",
85
+ "lucide-react": "^1.61.0",
86
+ "maplibre-gl": "^6.16.0",
87
87
  "pele": "1.8.0",
88
88
  "react-markdown": "^10.1.0",
89
89
  "remark-breaks": "^4.0.0",
90
90
  "remark-gfm": "^4.0.1",
91
- "shiki": "^4.4.3",
92
- "simple-icons": "^16.32.0",
93
- "zod": "^4.6.5",
94
- "zustand": "^5.0.15"
91
+ "shiki": "^4.5.0",
92
+ "simple-icons": "^16.36.0",
93
+ "zod": "^4.6.7",
94
+ "zustand": "^5.0.16"
95
95
  },
96
96
  "peerDependencies": {
97
- "@adecore/ui": "^0.39.0",
97
+ "@adecore/ui": "^0.40.1",
98
98
  "i18next": "^26.4.2",
99
99
  "react": "^19.3.0",
100
100
  "react-dom": "^19.3.0",
101
101
  "react-i18next": "^17.0.15"
102
102
  },
103
103
  "devDependencies": {
104
- "@adecore/ui": "0.39.0",
105
- "@types/bun": "^1.4.2",
104
+ "@adecore/ui": "0.40.1",
105
+ "@types/bun": "^1.4.4",
106
106
  "@types/react": "^19.3.0",
107
107
  "@types/react-dom": "^19.3.0",
108
- "i18next": "^26.4.2",
109
- "linkedom": "^0.18.13",
110
- "react": "^19.3.0",
111
- "react-dom": "^19.3.0",
112
- "react-i18next": "^17.0.15",
108
+ "i18next": "^26.5.11",
109
+ "linkedom": "^0.18.14",
110
+ "react": "^19.3.1",
111
+ "react-dom": "^19.3.1",
112
+ "react-i18next": "^17.1.9",
113
113
  "typescript": "^7.0.2"
114
114
  },
115
115
  "license": "FSL-1.1-MIT",
@@ -6,6 +6,7 @@ import { accountColor } from './accounts';
6
6
  export function AccountDot({ color, className }: { color: string | undefined; className?: string }) {
7
7
  const { accents } = useChatHost();
8
8
  const paint = accountColor(color, accents.all);
9
+
9
10
  return (
10
11
  <span
11
12
  aria-hidden
@@ -17,9 +17,11 @@ const BUILT_IN_MARKS: Partial<Record<AgentKind, AgentMark>> = {
17
17
 
18
18
  export function AgentIcon({ kind, size = 14, className }: { kind: AgentKind; size?: number; className?: string }) {
19
19
  const mark = hostAgentMark(kind) ?? BUILT_IN_MARKS[kind];
20
+
20
21
  if (!mark) {
21
22
  return <Icon icon={Bot} size={size} className={className} />;
22
23
  }
24
+
23
25
  return (
24
26
  <svg
25
27
  role="img"
@@ -1,10 +1,11 @@
1
1
  import { useContext } from 'react';
2
2
  import clsx from 'clsx';
3
- import type { AgentKind, UsageProvider } from '@adecore/agent-contracts';
3
+ import type { AgentKind } from '@adecore/agent-contracts';
4
4
  import { ChatScopeContext } from '../scope';
5
5
  import { useProvidersStore } from '../state/providers';
6
6
  import { AgentIcon } from './AgentIcon';
7
7
  import { modelName } from './model-name';
8
+ import { hasProviderLogo } from './provider-paths';
8
9
  import { ProviderLogo } from './ProviderLogo';
9
10
 
10
11
  interface AgentModelLabelProps {
@@ -27,13 +28,10 @@ export function AgentModelLabel({ provider, model, scopeId, size = 14, className
27
28
  const id = scopeId ?? scope?.id;
28
29
  const catalog = useProvidersStore((s) => (id === undefined ? undefined : s.byScope[id]?.providers.find((entry) => entry.kind === provider)));
29
30
  const name = model === undefined ? (catalog?.name ?? provider) : modelName(model, catalog?.models);
31
+
30
32
  return (
31
33
  <span className={clsx('flex min-w-0 items-center gap-1.5', className)}>
32
- {provider === 'claude' || provider === 'codex' ? (
33
- <ProviderLogo provider={provider as UsageProvider} size={size} />
34
- ) : (
35
- <AgentIcon kind={provider} size={size} className="shrink-0" />
36
- )}
34
+ {hasProviderLogo(provider) ? <ProviderLogo provider={provider} size={size} /> : <AgentIcon kind={provider} size={size} className="shrink-0" />}
37
35
  <span className="truncate">{name}</span>
38
36
  </span>
39
37
  );
@@ -29,21 +29,28 @@ type ModelOptionControlProps = {
29
29
  export function ModelOptionControl(props: ModelOptionControlProps) {
30
30
  const { options, layout = 'compact', disabled } = props;
31
31
  const option = localizedOption(props.option);
32
+
32
33
  if (option.type === 'boolean') {
33
34
  return <Switch checked={optionValue(option, options) === true} label={option.label} disabled={disabled} onCheckedChange={props.onChange} />;
34
35
  }
36
+
35
37
  const picked = props.defaultLabel !== undefined && options[option.id] === undefined ? MODEL_DEFAULT : String(optionValue(option, options));
38
+
36
39
  const choices = [
37
40
  ...(props.defaultLabel !== undefined ? [{ id: MODEL_DEFAULT, label: props.defaultLabel, description: undefined }] : []),
38
41
  ...option.choices
39
42
  ];
43
+
40
44
  const choose = (id: string): void => {
41
45
  if (props.defaultLabel !== undefined) {
42
46
  props.onChange(id === MODEL_DEFAULT ? undefined : id);
47
+
43
48
  return;
44
49
  }
50
+
45
51
  props.onChange(id);
46
52
  };
53
+
47
54
  if (layout === 'row' && choices.length <= MAX_SEGMENTS) {
48
55
  return (
49
56
  <Segmented
@@ -55,6 +62,7 @@ export function ModelOptionControl(props: ModelOptionControlProps) {
55
62
  />
56
63
  );
57
64
  }
65
+
58
66
  return (
59
67
  <Select
60
68
  value={picked}
@@ -44,9 +44,12 @@ export function continueTargets(
44
44
  ): AccountEntry[] {
45
45
  const roomy = others(accounts, kind, current).flatMap((entry) => {
46
46
  const limits = limitsOfAccount(snapshot, entry.id);
47
+
47
48
  return hasRoom(limits) ? [{ entry, pressure: pressure(limits) }] : [];
48
49
  });
50
+
49
51
  roomy.sort((a, b) => a.pressure - b.pressure);
52
+
50
53
  return roomy.map((target) => target.entry);
51
54
  }
52
55
 
@@ -29,7 +29,9 @@ export function accountsOfKind(accounts: ProviderAccounts | null, kind: AgentKin
29
29
  if (accounts === null) {
30
30
  return [];
31
31
  }
32
+
32
33
  const statuses = new Map(accounts.statuses.map((status) => [status.id, status]));
34
+
33
35
  return Object.entries(accounts.accounts)
34
36
  .filter(([, account]) => account.kind === kind)
35
37
  .map(([id, account]) => ({ id, account, status: statuses.get(id) ?? null, isDefault: id === kind }))
@@ -51,6 +53,7 @@ export function accountLoginOf(accounts: ProviderAccounts | null | undefined, ki
51
53
  if (accounts?.loginCommands?.[kind] !== undefined) {
52
54
  return 'terminal';
53
55
  }
56
+
54
57
  return accounts?.chatLogins?.includes(kind) === true ? 'chat' : null;
55
58
  }
56
59
 
@@ -68,6 +71,7 @@ export const ACCOUNT_TONE_CLASSES: Record<AccountTone, string> = {
68
71
  */
69
72
  export function accountStatusLine(status: ProviderAccountStatus | null, canLogIn: boolean): { text: string; tone: AccountTone } {
70
73
  const words = (key: string, values?: Record<string, string>): string => i18next.t(`agent-providers:status.${key}`, values);
74
+
71
75
  switch (status?.state ?? 'checking') {
72
76
  case 'checking':
73
77
  return { text: words('checking'), tone: 'muted' };
@@ -81,6 +85,7 @@ export function accountStatusLine(status: ProviderAccountStatus | null, canLogIn
81
85
  if (!canLogIn) {
82
86
  return { text: words('available'), tone: 'muted' };
83
87
  }
88
+
84
89
  return { text: status?.plan ? words('readyPlan', { plan: status.plan }) : words('ready'), tone: 'muted' };
85
90
  case 'folder-missing':
86
91
  return { text: words('folderMissing'), tone: 'error' };
@@ -105,9 +110,11 @@ function slugOf(label: string): string {
105
110
  export function mintAccountId(kind: AgentKind, label: string, taken: ReadonlySet<string>): string {
106
111
  const base = `${kind}_${slugOf(label) || 'account'}`.slice(0, 60).replace(/-+$/, '');
107
112
  let id = base;
113
+
108
114
  for (let suffix = 2; taken.has(id); suffix += 1) {
109
115
  id = `${base}-${suffix}`;
110
116
  }
117
+
111
118
  return id;
112
119
  }
113
120
 
@@ -119,6 +126,7 @@ export function freeAccountColor(entries: readonly AccountEntry[], accents: Chat
119
126
  const { all, featured } = accents;
120
127
  const worn = new Set(entries.map((entry) => (accountColor(entry.account.color, all) === undefined ? fallback : entry.account.color)));
121
128
  const choices = [...featured, ...all.map((entry) => entry.id).filter((id) => !featured.includes(id))];
129
+
122
130
  return choices.find((id) => !worn.has(id)) ?? featured[entries.length % featured.length] ?? fallback;
123
131
  }
124
132
 
@@ -140,11 +148,15 @@ export function hasAccountChoice(entries: readonly AccountEntry[]): boolean {
140
148
  export function canContinueOn(accounts: ProviderAccounts | null, kind: AgentKind, from: string | undefined, to: string | undefined): boolean {
141
149
  const fromId = from ?? kind;
142
150
  const toId = to ?? kind;
151
+
143
152
  if (fromId === toId) {
144
153
  return true;
145
154
  }
155
+
146
156
  const folderOf = (id: string): string | undefined =>
147
157
  accounts?.accounts[id]?.kind === kind ? accounts.statuses.find((status) => status.id === id)?.transcripts : undefined;
158
+
148
159
  const folder = folderOf(fromId);
160
+
149
161
  return folder !== undefined && folder !== '' && folder === folderOf(toId);
150
162
  }
@@ -1,5 +1,6 @@
1
1
  import type { AgentKind, ModelInfo, ProviderInfo } from '@adecore/agent-contracts';
2
2
  import { useProviders } from '../state/providers';
3
+ import { hasProviderLogo } from './provider-paths';
3
4
 
4
5
  /*
5
6
  * A model id as a person reads it, for a model no catalog names: without its vendor prefix and its
@@ -9,14 +10,19 @@ import { useProviders } from '../state/providers';
9
10
  export function modelNameFromSlug(slug: string): string {
10
11
  const bare = slug.slice(slug.lastIndexOf('/') + 1).replace(/-\d{6,8}$/, '');
11
12
  const words: string[] = [];
13
+
12
14
  for (const word of bare.split('-')) {
13
15
  const previous = words.at(-1);
16
+
14
17
  if (previous !== undefined && /^\d+$/.test(word) && /\d$/.test(previous)) {
15
18
  words[words.length - 1] = `${previous}.${word}`;
19
+
16
20
  continue;
17
21
  }
22
+
18
23
  words.push(word);
19
24
  }
25
+
20
26
  return words.map((word) => (word === 'gpt' ? 'GPT' : word.charAt(0).toUpperCase() + word.slice(1))).join(' ');
21
27
  }
22
28
 
@@ -34,8 +40,9 @@ export interface AgentChip {
34
40
 
35
41
  export function agentChipOf(providers: readonly ProviderInfo[], provider: AgentKind, model: string): AgentChip {
36
42
  const owner = providers.find((entry) => entry.kind === provider);
43
+
37
44
  return {
38
- mark: provider === 'claude' || provider === 'codex' ? 'logo' : 'icon',
45
+ mark: hasProviderLogo(provider) ? 'logo' : 'icon',
39
46
  name: owner?.name ?? provider,
40
47
  model: modelName(model, owner?.models)
41
48
  };
@@ -53,18 +60,23 @@ export function useModelName(provider: AgentKind | undefined, slug: string): str
53
60
  export function sharedModelPrefix(models: readonly ModelInfo[]): string {
54
61
  const names = models.map((entry) => entry.name.split(' '));
55
62
  const first = names[0];
63
+
56
64
  if (first === undefined || names.length < 2) {
57
65
  return '';
58
66
  }
67
+
59
68
  let count = 0;
69
+
60
70
  while (names.every((words) => words.length > count + 1 && words[count] === first[count])) {
61
71
  count++;
62
72
  }
73
+
63
74
  return count === 0 ? '' : `${first.slice(0, count).join(' ')} `;
64
75
  }
65
76
 
66
77
  /* A model's name without the words its whole catalog shares. */
67
78
  export function shortModelName(name: string, models: readonly ModelInfo[] | undefined): string {
68
79
  const prefix = sharedModelPrefix(models ?? []);
80
+
69
81
  return prefix !== '' && name.startsWith(prefix) ? name.slice(prefix.length) : name;
70
82
  }
@@ -9,9 +9,11 @@ function fits(option: ModelOptionDescriptor, value: string | boolean | undefined
9
9
  /* What an option runs at: the value picked, else the model's own default. A value the model does not offer counts as unpicked. */
10
10
  export function optionValue(option: ModelOptionDescriptor, options: OptionValues): string | boolean {
11
11
  const picked = options[option.id];
12
+
12
13
  if (fits(option, picked)) {
13
14
  return picked;
14
15
  }
16
+
15
17
  return option.type === 'boolean' ? option.defaultValue : option.defaultChoice;
16
18
  }
17
19
 
@@ -8,6 +8,7 @@ export interface ModelRequirement {
8
8
 
9
9
  function partsOf(version: string, strict: boolean): number[] | null {
10
10
  const found = (strict ? /^(\d+)\.(\d+)(?:\.(\d+))?$/ : /(\d+)\.(\d+)\.(\d+)/).exec(version);
11
+
11
12
  return found === null ? null : [Number(found[1]), Number(found[2]), Number(found[3] ?? 0)];
12
13
  }
13
14
 
@@ -24,13 +25,16 @@ function isReported(slug: string, reported: ReadonlySet<string>): boolean {
24
25
  export function modelRequirementOf(model: ModelInfo, provider: ProviderInfo, reported: ReadonlySet<string>): ModelRequirement | null {
25
26
  const minimum = model.minCliVersion === undefined ? null : partsOf(model.minCliVersion, true);
26
27
  const installed = provider.version === null ? null : partsOf(provider.version, false);
28
+
27
29
  if (minimum === null || installed === null || isReported(model.slug, reported)) {
28
30
  return null;
29
31
  }
32
+
30
33
  for (let i = 0; i < 3; i++) {
31
34
  if (installed[i] !== minimum[i]) {
32
35
  return (installed[i] ?? 0) > (minimum[i] ?? 0) ? null : { cli: provider.name, minVersion: model.minCliVersion ?? '' };
33
36
  }
34
37
  }
38
+
35
39
  return null;
36
40
  }
@@ -20,8 +20,10 @@ export function choiceWord(optionId: string, choice: { id: string; label: string
20
20
  /* The option with the words a person reads, for a surface that draws its labels and choices. */
21
21
  export function localizedOption(option: ModelOptionDescriptor): ModelOptionDescriptor {
22
22
  const label = optionWord(option);
23
+
23
24
  if (option.type !== 'select') {
24
25
  return { ...option, label };
25
26
  }
27
+
26
28
  return { ...option, label, choices: option.choices.map((choice) => ({ ...choice, label: choiceWord(option.id, choice) })) };
27
29
  }
@@ -1,9 +1,9 @@
1
- import type { UsageProvider } from '@adecore/agent-contracts';
1
+ import type { AgentKind, UsageProvider } from '@adecore/agent-contracts';
2
2
  import { siGithubcopilot } from 'simple-icons';
3
3
 
4
4
  /*
5
- * The marks of Anthropic, OpenAI, xAI and GitHub Copilot, and a monogram for a provider with no mark of its own. Lucide has no brand marks, so these are the icon rule's
6
- * exception for provider marks. One path each, in `currentColor`, so a row decides how loud the logo is.
5
+ * Lucide has no brand marks, so these are the icon rule's exception for provider marks. One path each,
6
+ * in `currentColor`, so a row decides how loud the logo is.
7
7
  */
8
8
  const CLAUDE_PATH =
9
9
  'M4.709 15.955l4.72-2.647.079-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.377-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.048.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.607.212-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.914-1.316-.012.01z';
@@ -16,3 +16,8 @@ export const PROVIDER_PATHS: Record<UsageProvider, string> = {
16
16
  grok: 'M6.469 8.776 16.512 23h-4.464L2.005 8.776H6.47Zm-.004 7.9 2.233 3.164L6.467 23H2l4.465-6.324ZM22 2.582V23h-3.659V7.764L22 2.582ZM22 1l-9.952 14.095-2.233-3.163L17.533 1H22Z',
17
17
  copilot: siGithubcopilot.path
18
18
  };
19
+
20
+ /* The CLIs drawn with the logo of the usage page rather than their agent mark. */
21
+ export function hasProviderLogo(kind: AgentKind): kind is 'claude' | 'codex' {
22
+ return kind === 'claude' || kind === 'codex';
23
+ }
@@ -38,5 +38,6 @@ export function taskStatusWord(task: TaskState): StatusWord {
38
38
  if (task.status !== 'open') {
39
39
  return task.status;
40
40
  }
41
+
41
42
  return task.paused === undefined ? 'running' : 'paused';
42
43
  }
@@ -1,4 +1,3 @@
1
- import { useMemo } from 'react';
2
1
  import type { AgentKind, ProviderAccounts } from '@adecore/agent-contracts';
3
2
  import { accountName, accountsOfKind, hasAccountChoice, offeredAccounts, type AccountEntry } from '../agents/accounts';
4
3
  import { useProviderAccounts, type ProviderAccountsRow } from '../state/provider-accounts';
@@ -23,24 +22,27 @@ export interface AccountChoice {
23
22
  export function useAccountChoice(kind: AgentKind | null, account: string | undefined): AccountChoice | null {
24
23
  const accounts = useProviderAccounts((row) => row.accounts);
25
24
  const providerName = useProviders((row) => row.providers.find((entry) => entry.kind === kind)?.name) ?? kind ?? '';
26
- return useMemo(() => {
27
- if (kind === null || accounts === null) {
28
- return null;
29
- }
30
- const entries = accountsOfKind(accounts, kind);
31
- if (!hasAccountChoice(entries)) {
32
- return null;
33
- }
34
- const currentId = account ?? kind;
35
- return {
36
- accounts,
37
- offered: offeredAccounts(entries, currentId),
38
- currentId,
39
- current: entries.find((entry) => entry.id === currentId) ?? null,
40
- providerName,
41
- nameOf: (entry: AccountEntry) => accountName(entry, providerName)
42
- };
43
- }, [accounts, kind, account, providerName]);
25
+
26
+ if (kind === null || accounts === null) {
27
+ return null;
28
+ }
29
+
30
+ const entries = accountsOfKind(accounts, kind);
31
+
32
+ if (!hasAccountChoice(entries)) {
33
+ return null;
34
+ }
35
+
36
+ const currentId = account ?? kind;
37
+
38
+ return {
39
+ accounts,
40
+ offered: offeredAccounts(entries, currentId),
41
+ currentId,
42
+ current: entries.find((entry) => entry.id === currentId) ?? null,
43
+ providerName,
44
+ nameOf: (entry: AccountEntry) => accountName(entry, providerName)
45
+ };
44
46
  }
45
47
 
46
48
  /*
@@ -63,11 +65,14 @@ export function newAccountChoiceOf(row: ProviderAccountsRow, kind: AgentKind | n
63
65
  if (kind === null || !row.loaded) {
64
66
  return LOADING;
65
67
  }
68
+
66
69
  const entries = accountsOfKind(row.accounts, kind);
70
+
67
71
  // An older machine lists no accounts at all, or none of a CLI it does not know.
68
72
  if (entries.length === 0) {
69
73
  return UNSUPPORTED;
70
74
  }
75
+
71
76
  return {
72
77
  status: 'ready',
73
78
  offered: offeredAccounts(entries, undefined),
@@ -80,5 +85,6 @@ export function newAccountChoiceOf(row: ProviderAccountsRow, kind: AgentKind | n
80
85
  export function useNewAccountChoice(kind: AgentKind | null): NewAccountChoice {
81
86
  const row = useProviderAccounts((entry) => entry);
82
87
  const providerName = useProviders((entry) => entry.providers.find((provider) => provider.kind === kind)?.name) ?? kind ?? '';
88
+
83
89
  return newAccountChoiceOf(row, kind, providerName);
84
90
  }
@@ -14,6 +14,7 @@ export function transportActions(transport: ChatTransport): ChatActions {
14
14
  },
15
15
  unqueue: async (chatId, messageId) => {
16
16
  const { message } = await transport.request('chat.unqueue', { chatId, messageId });
17
+
17
18
  // A host answers without the message once it went out, which is what the composer tells a person.
18
19
  if (message === undefined) {
19
20
  throw new ChatTransportError('request-not-found', i18next.t('agent-chat:composer.queue.alreadySent'));
@@ -58,15 +59,19 @@ const byTransport = new WeakMap<ChatTransport, ChatActions>();
58
59
  /* The app's own actions when its host has them, and the requests of this scope's host of chats otherwise. */
59
60
  export function actionsOf(scope: ChatScope, host: Pick<ChatHost, 'actions'>): ChatActions {
60
61
  const own = host.actions;
62
+
61
63
  if (own !== null) {
62
64
  return own;
63
65
  }
66
+
64
67
  const { transport } = scope;
65
68
  let actions = byTransport.get(transport);
69
+
66
70
  if (actions === undefined) {
67
71
  actions = transportActions(transport);
68
72
  byTransport.set(transport, actions);
69
73
  }
74
+
70
75
  return actions;
71
76
  }
72
77