@adecore/agents-react 0.39.0 → 0.40.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.
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 +26 -26
  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
@@ -10,6 +10,7 @@ export const SHELL_LANGUAGE = 'shellscript';
10
10
 
11
11
  // Past this a command is drawn without looking for code inside it; the scan is linear, the highlighting is not.
12
12
  export const EMBEDDED_CODE_LIMIT = 20_000;
13
+
13
14
  const MAX_DEPTH = 3;
14
15
 
15
16
  /* A stretch of a shell command, as offsets into it. */
@@ -113,6 +114,7 @@ const INTERPRETERS: Record<string, Interpreter> = {
113
114
  const WRAPPERS = new Set(['sudo', 'env', 'exec', 'nohup', 'time', 'command', 'nice']);
114
115
  // Tools that run a program of a project's environment as `<tool> run <program>`.
115
116
  const RUNNERS = new Set(['uv', 'poetry', 'pipenv']);
117
+
116
118
  // Where a heredoc written to a file goes, by the file's extension.
117
119
  const EXTENSIONS: Record<string, string> = {
118
120
  py: 'python',
@@ -143,6 +145,7 @@ const BLANKS = ' \t\r';
143
145
  /* The program a command name runs, as the table above knows it: `/usr/bin/python3.12` is `python`. */
144
146
  function programOf(word: string): string {
145
147
  const name = word.slice(word.lastIndexOf('/') + 1);
148
+
146
149
  return /^python\d*(\.\d+)?$/.test(name) ? 'python' : name;
147
150
  }
148
151
 
@@ -153,17 +156,21 @@ function parseCommands(source: string, from: number, to: number): SimpleCommand[
153
156
  const pending: { command: SimpleCommand; delimiter: string; strip: boolean; operator: Span }[] = [];
154
157
  // The start of the `2` of a `2>&1` just read, which the redirect after it begins at.
155
158
  let descriptorStart: number | null = null;
159
+
156
160
  const finish = (): void => {
157
161
  if (current.words.length > 0 || current.redirects.length > 0) {
158
162
  commands.push(current);
159
163
  }
164
+
160
165
  current = { words: [], redirects: [], heredocs: [] };
161
166
  descriptorStart = null;
162
167
  };
168
+
163
169
  const skipBlanks = (index: number): number => {
164
170
  while (index < to && BLANKS.includes(source[index]!)) {
165
171
  index++;
166
172
  }
173
+
167
174
  return index;
168
175
  };
169
176
 
@@ -173,12 +180,16 @@ function parseCommands(source: string, from: number, to: number): SimpleCommand[
173
180
  let quoted: Span | null = null;
174
181
  let parts = 0;
175
182
  let bare = true;
183
+
176
184
  while (index < to) {
177
185
  const char = source[index]!;
186
+
178
187
  if (BLANKS.includes(char) || char === '\n' || SEPARATORS.includes(char) || char === '<' || char === '>') {
179
188
  break;
180
189
  }
190
+
181
191
  parts++;
192
+
182
193
  if (char === "'") {
183
194
  const close = source.indexOf("'", index + 1);
184
195
  const end = close === -1 || close >= to ? to : close;
@@ -188,6 +199,7 @@ function parseCommands(source: string, from: number, to: number): SimpleCommand[
188
199
  index = Math.min(end + 1, to);
189
200
  } else if (char === '"') {
190
201
  let end = index + 1;
202
+
191
203
  while (end < to && source[end] !== '"') {
192
204
  if (source[end] === '\\' && end + 1 < to && '$`"\\\n'.includes(source[end + 1]!)) {
193
205
  text += source[end + 1];
@@ -197,6 +209,7 @@ function parseCommands(source: string, from: number, to: number): SimpleCommand[
197
209
  end++;
198
210
  }
199
211
  }
212
+
200
213
  quoted = { start: index + 1, end };
201
214
  bare = false;
202
215
  index = Math.min(end + 1, to);
@@ -213,38 +226,49 @@ function parseCommands(source: string, from: number, to: number): SimpleCommand[
213
226
  index++;
214
227
  }
215
228
  }
229
+
216
230
  const end = Math.min(index, to);
217
231
  const body = bare ? { start, end } : parts === 1 && quoted !== null ? quoted : null;
232
+
218
233
  return { start, end, text, body };
219
234
  };
220
235
 
221
236
  const readHeredoc = (start: number, delimiter: string, strip: boolean): { body: Span; next: number } => {
222
237
  let index = start;
238
+
223
239
  while (index < to) {
224
240
  const newline = source.indexOf('\n', index);
225
241
  const lineEnd = newline === -1 || newline >= to ? to : newline;
226
242
  let line = source.slice(index, lineEnd).replace(/\r$/, '');
243
+
227
244
  if (strip) {
228
245
  line = line.replace(/^\t+/, '');
229
246
  }
247
+
230
248
  if (line === delimiter) {
231
249
  return { body: { start, end: index }, next: Math.min(lineEnd + 1, to) };
232
250
  }
251
+
233
252
  index = lineEnd + 1;
234
253
  }
254
+
235
255
  return { body: { start, end: to }, next: to };
236
256
  };
237
257
 
238
258
  let index = from;
259
+
239
260
  while (index < to) {
240
261
  const char = source[index]!;
262
+
241
263
  if (char === '\n') {
242
264
  index++;
265
+
243
266
  for (const heredoc of pending) {
244
267
  const { body, next } = readHeredoc(index, heredoc.delimiter, heredoc.strip);
245
268
  heredoc.command.heredocs.push({ ...heredoc.operator, body });
246
269
  index = next;
247
270
  }
271
+
248
272
  pending.length = 0;
249
273
  finish();
250
274
  } else if (BLANKS.includes(char)) {
@@ -261,15 +285,19 @@ function parseCommands(source: string, from: number, to: number): SimpleCommand[
261
285
  index += 2;
262
286
  const strip = source[index] === '-';
263
287
  const word = readWord(skipBlanks(strip ? index + 1 : index));
288
+
264
289
  if (word.text !== '') {
265
290
  pending.push({ command: current, delimiter: word.text, strip, operator: { start, end: word.end } });
266
291
  }
292
+
267
293
  index = word.end;
268
294
  } else if (char === '<' || char === '>' || (char === '&' && source[index + 1] === '>')) {
269
295
  let end = index + 1;
296
+
270
297
  while (end < to && '<>&|'.includes(source[end]!)) {
271
298
  end++;
272
299
  }
300
+
273
301
  const op = source.slice(index, end);
274
302
  const target = readWord(skipBlanks(end));
275
303
  current.redirects.push({ start: descriptorStart ?? index, end: Math.max(target.end, end), op, target });
@@ -282,15 +310,19 @@ function parseCommands(source: string, from: number, to: number): SimpleCommand[
282
310
  const word = readWord(index);
283
311
  // The `2` of `2>&1` is a file descriptor, not an argument.
284
312
  const descriptor = /^\d+$/.test(word.text) && (source[word.end] === '>' || source[word.end] === '<');
313
+
285
314
  if (descriptor) {
286
315
  descriptorStart = word.start;
287
316
  } else {
288
317
  current.words.push(word);
289
318
  }
319
+
290
320
  index = Math.max(word.end, index + 1);
291
321
  }
292
322
  }
323
+
293
324
  finish();
325
+
294
326
  return commands;
295
327
  }
296
328
 
@@ -298,8 +330,10 @@ function parseCommands(source: string, from: number, to: number): SimpleCommand[
298
330
  function closingParen(source: string, open: number, to: number): number {
299
331
  let depth = 0;
300
332
  let quote: string | null = null;
333
+
301
334
  for (let index = open; index < to; index++) {
302
335
  const char = source[index]!;
336
+
303
337
  if (quote !== null) {
304
338
  if (char === quote) {
305
339
  quote = null;
@@ -312,23 +346,28 @@ function closingParen(source: string, open: number, to: number): number {
312
346
  depth++;
313
347
  } else if (char === ')') {
314
348
  depth--;
349
+
315
350
  if (depth === 0) {
316
351
  return index + 1;
317
352
  }
318
353
  }
319
354
  }
355
+
320
356
  return to;
321
357
  }
322
358
 
323
359
  /* The index of the word a command runs, past variable assignments, wrappers and runners; -1 for none. */
324
360
  function programIndex(words: readonly Word[]): number {
325
361
  let index = 0;
362
+
326
363
  while (index < words.length) {
327
364
  const text = words[index]!.text;
365
+
328
366
  if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(text)) {
329
367
  index++;
330
368
  } else if (WRAPPERS.has(programOf(text))) {
331
369
  index++;
370
+
332
371
  while (index < words.length && words[index]!.text.startsWith('-')) {
333
372
  index++;
334
373
  }
@@ -338,6 +377,7 @@ function programIndex(words: readonly Word[]): number {
338
377
  return index;
339
378
  }
340
379
  }
380
+
341
381
  return -1;
342
382
  }
343
383
 
@@ -348,49 +388,64 @@ function codeOf(command: SimpleCommand): EmbeddedCode[] {
348
388
  const program = at === -1 ? '' : programOf(command.words[at]!.text);
349
389
  const interpreter = INTERPRETERS[program];
350
390
  let readsFile = false;
391
+
351
392
  if (interpreter !== undefined) {
352
393
  let positional = 0;
394
+
353
395
  for (let index = at + 1; index < command.words.length; index++) {
354
396
  const word = command.words[index]!;
397
+
355
398
  if (word.text.startsWith('-') && word.text !== '-') {
356
399
  if (interpreter.script(word.text)) {
357
400
  const script = command.words[index + 1];
401
+
358
402
  if (script?.body) {
359
403
  found.push({ ...script.body, language: interpreter.language });
360
404
  }
405
+
361
406
  readsFile = true;
362
407
  index++;
363
408
  } else if (interpreter.takesValue?.has(word.text)) {
364
409
  index++;
365
410
  } else if (interpreter === PYTHON && word.text === '-m') {
366
411
  readsFile = true;
412
+
367
413
  break;
368
414
  }
415
+
369
416
  continue;
370
417
  }
418
+
371
419
  if (interpreter.client !== true) {
372
420
  // A script file; whatever follows is its own arguments.
373
421
  readsFile = word.text !== '-';
422
+
374
423
  break;
375
424
  }
425
+
376
426
  if (positional === interpreter.positional && word.body !== null) {
377
427
  found.push({ ...word.body, language: interpreter.language });
378
428
  }
429
+
379
430
  positional++;
380
431
  }
381
432
  }
433
+
382
434
  const heredocLanguage = interpreter !== undefined && (!readsFile || interpreter.client === true) ? interpreter.language : writtenLanguage(command, program);
435
+
383
436
  if (heredocLanguage !== null) {
384
437
  for (const { body } of command.heredocs) {
385
438
  found.push({ ...body, language: heredocLanguage });
386
439
  }
387
440
  }
441
+
388
442
  return found;
389
443
  }
390
444
 
391
445
  /* The language of the file a `cat > file` or `tee file` writes, by its extension; null when nothing says. */
392
446
  function writtenLanguage(command: SimpleCommand, program: string): string | null {
393
447
  const targets = command.redirects.filter(({ op }) => op === '>' || op === '>>' || op === '>|').map(({ target }) => target.text);
448
+
394
449
  if (program === 'tee') {
395
450
  targets.push(
396
451
  ...command.words
@@ -399,13 +454,16 @@ function writtenLanguage(command: SimpleCommand, program: string): string | null
399
454
  .filter((text) => !text.startsWith('-'))
400
455
  );
401
456
  }
457
+
402
458
  for (const target of targets) {
403
459
  const dot = target.lastIndexOf('.');
404
460
  const language = dot === -1 ? undefined : EXTENSIONS[target.slice(dot + 1).toLowerCase()];
461
+
405
462
  if (language !== undefined) {
406
463
  return language;
407
464
  }
408
465
  }
466
+
409
467
  return null;
410
468
  }
411
469
 
@@ -415,7 +473,9 @@ function collect(source: string, from: number, to: number, depth: number, into:
415
473
  if (code.end <= code.start) {
416
474
  continue;
417
475
  }
476
+
418
477
  into.push(code);
478
+
419
479
  if (code.language === SHELL_LANGUAGE && depth < MAX_DEPTH) {
420
480
  collect(source, code.start, code.end, depth + 1, into);
421
481
  }
@@ -431,6 +491,7 @@ export function shellCommands(source: string): ShellSimpleCommand[] {
431
491
  return parseCommands(source, 0, source.length).map((command) => {
432
492
  const index = programIndex(command.words);
433
493
  const word = command.words[index];
494
+
434
495
  return {
435
496
  ...command,
436
497
  program: word === undefined ? null : { index, name: word.text.slice(word.text.lastIndexOf('/') + 1) }
@@ -449,7 +510,9 @@ export function embeddedCode(command: string): EmbeddedCode[] {
449
510
  if (command.length > EMBEDDED_CODE_LIMIT) {
450
511
  return [];
451
512
  }
513
+
452
514
  const found: EmbeddedCode[] = [];
453
515
  collect(command, 0, command.length, 0, found);
516
+
454
517
  return found.sort((left, right) => left.start - right.start || right.end - left.end);
455
518
  }
@@ -20,6 +20,7 @@ export interface ForkPoint {
20
20
  function turnsOf(items: Record<string, ChatItem>, order: readonly string[]): ChatTurnItem[] {
21
21
  return order.flatMap((id) => {
22
22
  const item = items[id];
23
+
23
24
  return item?.kind === 'turn' ? [item] : [];
24
25
  });
25
26
  }
@@ -32,15 +33,19 @@ export function forkRefusal(info: ChatInfo | null, turn: ChatItem | undefined):
32
33
  if (info === null || turn?.kind !== 'turn') {
33
34
  return i18next.t('agent-chat:fork.refusal.notInConversation');
34
35
  }
36
+
35
37
  if (!FORKABLE_PROVIDERS.has(info.provider)) {
36
38
  return i18next.t('agent-chat:fork.refusal.cliCannotFork');
37
39
  }
40
+
38
41
  if (info.agentSessionId === null) {
39
42
  return i18next.t('agent-chat:fork.refusal.noConversation');
40
43
  }
44
+
41
45
  if (turn.state === 'running' || info.activeTurnId !== null) {
42
46
  return i18next.t('agent-chat:fork.refusal.turnRunning');
43
47
  }
48
+
44
49
  return null;
45
50
  }
46
51
 
@@ -49,31 +54,37 @@ export function summaryRefusal(state: { busy: boolean; originalPresent: boolean
49
54
  if (!state.originalPresent) {
50
55
  return i18next.t('agent-chat:fork.refusal.originalGone');
51
56
  }
57
+
52
58
  if (state.busy) {
53
59
  return i18next.t('agent-chat:fork.refusal.turnRunning');
54
60
  }
61
+
55
62
  return null;
56
63
  }
57
64
 
58
65
  /* The forks this client knows of that went on after this turn of this chat, in the order it holds them. */
59
66
  export function forkIdsAfter(infos: Iterable<ChatInfo>, chatId: string, turnId: string): string[] {
60
67
  const ids: string[] = [];
68
+
61
69
  for (const info of infos) {
62
70
  if (info.forkOf?.chatId === chatId && info.forkOf.turnId === turnId) {
63
71
  ids.push(info.chatId);
64
72
  }
65
73
  }
74
+
66
75
  return ids;
67
76
  }
68
77
 
69
78
  /* The turns of this chat that at least one fork this client knows of went on after. */
70
79
  export function forkedTurnIds(infos: Iterable<ChatInfo>, chatId: string): Set<string> {
71
80
  const turns = new Set<string>();
81
+
72
82
  for (const info of infos) {
73
83
  if (info.forkOf?.chatId === chatId) {
74
84
  turns.add(info.forkOf.turnId);
75
85
  }
76
86
  }
87
+
77
88
  return turns;
78
89
  }
79
90
 
@@ -86,16 +97,20 @@ export function forkPointOf(items: Record<string, ChatItem>, order: readonly str
86
97
  const turns = turnsOf(items, order);
87
98
  const index = turns.findIndex((turn) => turn.id === turnId);
88
99
  const turn = turns[index];
100
+
89
101
  if (turn === undefined) {
90
102
  return null;
91
103
  }
104
+
92
105
  const asked = order.map((id) => items[id]).find((item) => item?.kind === 'user' && item.turnId === turnId);
93
106
  const text = asked?.kind === 'user' ? asked.text : (turn.label ?? '');
107
+
94
108
  const prompt =
95
109
  text
96
110
  .split('\n')
97
111
  .find((line) => line.trim() !== '')
98
112
  ?.trim() ?? null;
113
+
99
114
  return { turnId, number: index + 1, total: turns.length, counted: whole, last: index === turns.length - 1, prompt };
100
115
  }
101
116
 
@@ -106,10 +121,13 @@ export function forkPointLabel(point: ForkPoint, maxPrompt = 60): string {
106
121
  : point.counted
107
122
  ? i18next.t('agent-chat:fork.point.afterTurn', { number: point.number, total: point.total })
108
123
  : i18next.t('agent-chat:fork.point.afterThis');
124
+
109
125
  if (point.prompt === null) {
110
126
  return where;
111
127
  }
128
+
112
129
  const prompt = point.prompt.length > maxPrompt ? `${point.prompt.slice(0, maxPrompt - 1)}…` : point.prompt;
130
+
113
131
  return i18next.t('agent-chat:fork.point.about', { where, prompt });
114
132
  }
115
133
 
@@ -177,6 +195,7 @@ export function forkPayload(input: {
177
195
  const provider = cli !== undefined && cli.chosen.provider !== cli.original.provider ? cli.chosen.provider : null;
178
196
  const selection = cli !== undefined && (provider !== null || cli.chosen.selection.model !== cli.original.selection.model) ? cli.chosen.selection : null;
179
197
  const account = cli?.chosen.account !== undefined && (provider !== null || cli.chosen.account !== cli.original.account) ? cli.chosen.account : null;
198
+
180
199
  return {
181
200
  chatId: input.chatId,
182
201
  turnId: input.turnId,
@@ -192,11 +211,14 @@ export function forkPayload(input: {
192
211
  /* Why a branch cannot be the fork's, or null when it can. */
193
212
  export function branchRefusal(branch: string, taken: readonly string[]): string | null {
194
213
  const name = branch.trim();
214
+
195
215
  if (name === '') {
196
216
  return i18next.t('agent-chat:fork.branch.unnamed');
197
217
  }
218
+
198
219
  if (taken.includes(name)) {
199
220
  return i18next.t('agent-chat:fork.branch.taken');
200
221
  }
222
+
201
223
  return null;
202
224
  }
@@ -23,22 +23,29 @@ export function generatedImageView(tool: ChatToolItem): GeneratedImageView {
23
23
  if (tool.state === 'running') {
24
24
  return { state: 'generating' };
25
25
  }
26
+
26
27
  const parsed = GeneratedImageInputSchema.safeParse(tool.input);
27
28
  const input = parsed.success ? parsed.data : {};
28
29
  const output = tool.output?.trim() || null;
30
+
29
31
  if (tool.state === 'error' || input.attachment === undefined) {
30
32
  return { state: 'failed', failure: output === null && tool.state !== 'error' ? { kind: 'empty' } : { kind: 'provider', message: output } };
31
33
  }
34
+
32
35
  const attachment = input.attachment;
36
+
33
37
  if (attachment.size === 0) {
34
38
  return { state: 'failed', failure: { kind: 'empty' } };
35
39
  }
40
+
36
41
  if (!attachment.mime.startsWith('image/')) {
37
42
  return { state: 'failed', failure: { kind: 'not-image' } };
38
43
  }
44
+
39
45
  if (attachment.size > CHAT_ATTACHMENT_MAX_BYTES) {
40
46
  return { state: 'failed', failure: { kind: 'too-large', limit: CHAT_ATTACHMENT_MAX_BYTES } };
41
47
  }
48
+
42
49
  return { state: 'ready', attachment, prompt: input.revisedPrompt?.trim() || null, transparent: input.transparentBackground === true };
43
50
  }
44
51
 
@@ -13,21 +13,28 @@ export interface GoalView {
13
13
  */
14
14
  export function goalView(info: Pick<ChatInfo, 'goal' | 'activeTurnId'>, goals: ProviderCapabilities['goals'], canAct: boolean): GoalView | null {
15
15
  const goal = info.goal;
16
+
16
17
  if (goal === undefined) {
17
18
  return null;
18
19
  }
20
+
19
21
  if (!canAct || goals === undefined) {
20
22
  return { goal, actions: [] };
21
23
  }
24
+
22
25
  if (goals === 'command') {
23
26
  return { goal, actions: goal.status === 'complete' || info.activeTurnId !== null ? [] : ['clear'] };
24
27
  }
28
+
25
29
  const actions: ChatGoalAction[] = [];
30
+
26
31
  if (goal.status === 'active') {
27
32
  actions.push('pause');
28
33
  } else if (goal.status !== 'complete') {
29
34
  actions.push('resume');
30
35
  }
36
+
31
37
  actions.push('clear');
38
+
32
39
  return { goal, actions };
33
40
  }
@@ -5,6 +5,8 @@ export function handbackReportOf(item: ChatItem): string | null {
5
5
  if (item.kind !== 'tool' || item.name !== 'SubagentHandback' || typeof item.input !== 'object' || item.input === null) {
6
6
  return null;
7
7
  }
8
+
8
9
  const message = (item.input as { message?: unknown }).message;
10
+
9
11
  return typeof message === 'string' && message.trim() !== '' ? message : null;
10
12
  }
@@ -16,10 +16,13 @@ export interface LimitView {
16
16
  */
17
17
  export function limitView(info: Pick<ChatInfo, 'limit' | 'resumeAt' | 'activeTurnId'>, now: number): LimitView | null {
18
18
  const limit = info.limit;
19
+
19
20
  if (limit === undefined || info.activeTurnId !== null) {
20
21
  return null;
21
22
  }
23
+
22
24
  const resumes = info.resumeAt === undefined ? null : formatMoment(info.resumeAt, now);
25
+
23
26
  if (limit.kind === 'overload') {
24
27
  return {
25
28
  pill: resumes === null ? i18next.t('agent-chat:limit.overload.pill') : i18next.t('agent-chat:limit.overload.pillRetry', { time: resumes }),
@@ -27,7 +30,9 @@ export function limitView(info: Pick<ChatInfo, 'limit' | 'resumeAt' | 'activeTur
27
30
  detail: resumes === null ? i18next.t('agent-chat:limit.overload.sendAgain') : i18next.t('agent-chat:limit.overload.retries', { time: resumes })
28
31
  };
29
32
  }
33
+
30
34
  const title = i18next.t('agent-chat:limit.usage.title');
35
+
31
36
  if (resumes !== null) {
32
37
  return {
33
38
  pill: i18next.t('agent-chat:limit.usage.pillResumes', { time: resumes }),
@@ -35,13 +40,16 @@ export function limitView(info: Pick<ChatInfo, 'limit' | 'resumeAt' | 'activeTur
35
40
  detail: i18next.t('agent-chat:limit.usage.resumes', { time: resumes })
36
41
  };
37
42
  }
43
+
38
44
  if (limit.resetsAt !== undefined) {
39
45
  const resets = formatMoment(limit.resetsAt, now);
46
+
40
47
  return {
41
48
  pill: i18next.t('agent-chat:limit.usage.pillUntil', { time: resets }),
42
49
  title,
43
50
  detail: i18next.t('agent-chat:limit.usage.resets', { time: resets })
44
51
  };
45
52
  }
53
+
46
54
  return { pill: i18next.t('agent-chat:limit.usage.pill'), title, detail: null };
47
55
  }
@@ -4,6 +4,7 @@ import { nameCollator } from '@adecore/ui/format';
4
4
  /* The providers in the order a picker lists them: the frontier labs, the rest, then the API providers, each by name. */
5
5
  export function inPickerOrder<T extends { kind: AgentKind; name: string }>(providers: readonly T[]): T[] {
6
6
  const groups = pickerGroupsOf(providers);
7
+
7
8
  return [...groups.frontier, ...groups.others, ...groups.api];
8
9
  }
9
10
 
@@ -33,6 +34,7 @@ export interface PickerCurrent {
33
34
  export type FavoritesOf = (kind: AgentKind) => ReadonlySet<string>;
34
35
 
35
36
  const NO_FAVORITES: FavoritesOf = () => new Set();
37
+
36
38
  const VENDOR_ORDER: Intl.CollatorOptions = { sensitivity: 'base' };
37
39
  const MODEL_ORDER: Intl.CollatorOptions = { sensitivity: 'base', numeric: true };
38
40
 
@@ -48,12 +50,15 @@ export function modelsByVendor(models: ModelInfo[]): ModelInfo[] {
48
50
  if (!models.some((model) => model.vendor !== undefined)) {
49
51
  return models;
50
52
  }
53
+
51
54
  const vendorOrder = nameCollator(VENDOR_ORDER).compare;
52
55
  const modelOrder = nameCollator(MODEL_ORDER).compare;
56
+
53
57
  return [...models].sort((a, b) => {
54
58
  if (a.vendor === undefined || b.vendor === undefined) {
55
59
  return a.vendor === b.vendor ? 0 : a.vendor === undefined ? 1 : -1;
56
60
  }
61
+
57
62
  return vendorOrder(a.vendor, b.vendor) || modelOrder(a.name, b.name);
58
63
  });
59
64
  }
@@ -63,6 +68,7 @@ export function favoritesFirst(models: ModelInfo[], favorites: ReadonlySet<strin
63
68
  if (favorites.size === 0) {
64
69
  return modelsByVendor(models);
65
70
  }
71
+
66
72
  return [...models.filter((model) => favorites.has(model.slug)), ...modelsByVendor(models.filter((model) => !favorites.has(model.slug)))];
67
73
  }
68
74
 
@@ -79,6 +85,7 @@ export function shortlistOf(
79
85
  if (models.length <= COMPACT_MODEL_LIMIT) {
80
86
  return null;
81
87
  }
88
+
82
89
  return favoritesFirst(
83
90
  models.filter((model) => model.isDefault || favorites.has(model.slug) || (current?.provider === kind && current.model === model.slug)),
84
91
  favorites
@@ -97,6 +104,7 @@ export function searchModels(
97
104
  favoritesOf: FavoritesOf = NO_FAVORITES
98
105
  ): Array<{ entry: ProviderInfo; models: ModelInfo[] }> {
99
106
  const needle = query.trim().toLowerCase();
107
+
100
108
  return providers
101
109
  .map((entry) => ({
102
110
  entry,
@@ -126,6 +134,7 @@ export function pickerEntriesOf(
126
134
  ): PickerEntry[] {
127
135
  const rowsOf = (entry: ProviderInfo, models: ModelInfo[]): PickerEntry[] =>
128
136
  favoritesFirst(models, favoritesOf(entry.kind)).map((model) => ({ kind: 'model', provider: entry, model }));
137
+
129
138
  if (query !== '') {
130
139
  return providers.flatMap((entry) =>
131
140
  rowsOf(
@@ -134,22 +143,25 @@ export function pickerEntriesOf(
134
143
  )
135
144
  );
136
145
  }
146
+
137
147
  const legacy = providers.flatMap((entry) =>
138
148
  entry.models.filter((model) => model.legacy).map((model) => ({ kind: 'model' as const, provider: entry, model }))
139
149
  );
150
+
140
151
  const live = providers.flatMap((entry): PickerEntry[] => {
141
152
  const models = entry.models.filter((model) => !model.legacy);
142
153
  const shortlist = shortlistOf(entry.kind, models, current, favoritesOf(entry.kind));
154
+
143
155
  if (shortlist === null) {
144
156
  return rowsOf(entry, models);
145
157
  }
158
+
146
159
  if (expanded.has(entry.kind)) {
147
160
  return [{ kind: 'all', provider: entry, count: models.length, open: true }, ...rowsOf(entry, models)];
148
161
  }
162
+
149
163
  return [...rowsOf(entry, shortlist), { kind: 'all', provider: entry, count: models.length, open: false }];
150
164
  });
151
- if (legacy.length === 0) {
152
- return live;
153
- }
154
- return legacyOpen ? [...live, { kind: 'legacy', count: legacy.length }, ...legacy] : [...live, { kind: 'legacy', count: legacy.length }];
165
+
166
+ return legacy.length === 0 ? live : [...live, { kind: 'legacy', count: legacy.length }, ...(legacyOpen ? legacy : [])];
155
167
  }