@ruimte/agents-react 0.0.0 → 0.7.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 (312) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +142 -1
  3. package/dist/agents/AccountDot.d.ts +4 -0
  4. package/dist/agents/AccountDot.js +8 -0
  5. package/dist/agents/AgentIcon.d.ts +6 -0
  6. package/dist/agents/AgentIcon.js +20 -0
  7. package/dist/agents/ProviderLogo.d.ts +8 -0
  8. package/dist/agents/ProviderLogo.js +8 -0
  9. package/dist/agents/account-limits.d.ts +7 -0
  10. package/dist/agents/account-limits.js +26 -0
  11. package/dist/agents/accounts.d.ts +22 -0
  12. package/dist/agents/accounts.js +103 -0
  13. package/dist/agents/model-name.d.ts +6 -0
  14. package/dist/agents/model-name.js +44 -0
  15. package/dist/agents/provider-paths.d.ts +2 -0
  16. package/dist/agents/provider-paths.js +8 -0
  17. package/dist/agents/status-look.d.ts +13 -0
  18. package/dist/agents/status-look.js +22 -0
  19. package/dist/chat/account-choice.d.ts +11 -0
  20. package/dist/chat/account-choice.js +30 -0
  21. package/dist/chat/actions.d.ts +6 -0
  22. package/dist/chat/actions.js +64 -0
  23. package/dist/chat/activity.d.ts +5 -0
  24. package/dist/chat/activity.js +4 -0
  25. package/dist/chat/attachments.d.ts +23 -0
  26. package/dist/chat/attachments.js +70 -0
  27. package/dist/chat/bookmarks.d.ts +14 -0
  28. package/dist/chat/bookmarks.js +73 -0
  29. package/dist/chat/chat-client.d.ts +53 -0
  30. package/dist/chat/chat-client.js +232 -0
  31. package/dist/chat/chat-references.d.ts +5 -0
  32. package/dist/chat/chat-references.js +5 -0
  33. package/dist/chat/drafts.d.ts +29 -0
  34. package/dist/chat/drafts.js +104 -0
  35. package/dist/chat/forks.d.ts +2 -0
  36. package/dist/chat/forks.js +26 -0
  37. package/dist/chat/guards.d.ts +12 -0
  38. package/dist/chat/guards.js +63 -0
  39. package/dist/chat/logic/answers.d.ts +3 -0
  40. package/dist/chat/logic/answers.js +14 -0
  41. package/dist/chat/logic/bookmarks.d.ts +5 -0
  42. package/dist/chat/logic/bookmarks.js +31 -0
  43. package/dist/chat/logic/context-usage.d.ts +12 -0
  44. package/dist/chat/logic/context-usage.js +20 -0
  45. package/dist/chat/logic/fork.d.ts +44 -0
  46. package/dist/chat/logic/fork.js +138 -0
  47. package/dist/chat/logic/handback.d.ts +2 -0
  48. package/dist/chat/logic/handback.js +8 -0
  49. package/dist/chat/logic/json.d.ts +1 -0
  50. package/dist/chat/logic/json.js +2 -0
  51. package/dist/chat/logic/limit.d.ts +7 -0
  52. package/dist/chat/logic/limit.js +34 -0
  53. package/dist/chat/logic/resume-compaction.d.ts +14 -0
  54. package/dist/chat/logic/resume-compaction.js +47 -0
  55. package/dist/chat/logic/scrubber.d.ts +50 -0
  56. package/dist/chat/logic/scrubber.js +216 -0
  57. package/dist/chat/logic/timeline-copy.d.ts +4 -0
  58. package/dist/chat/logic/timeline-copy.js +51 -0
  59. package/dist/chat/logic/timeline-target.d.ts +12 -0
  60. package/dist/chat/logic/timeline-target.js +34 -0
  61. package/dist/chat/logic/timeline.d.ts +110 -0
  62. package/dist/chat/logic/timeline.js +339 -0
  63. package/dist/chat/logic/tool-catalog.d.ts +10 -0
  64. package/dist/chat/logic/tool-catalog.js +40 -0
  65. package/dist/chat/logic/tools.d.ts +15 -0
  66. package/dist/chat/logic/tools.js +79 -0
  67. package/dist/chat/logic/workflow.d.ts +14 -0
  68. package/dist/chat/logic/workflow.js +47 -0
  69. package/dist/chat/mentions.d.ts +46 -0
  70. package/dist/chat/mentions.js +128 -0
  71. package/dist/chat/persisted-json.d.ts +5 -0
  72. package/dist/chat/persisted-json.js +24 -0
  73. package/dist/chat/preferences.d.ts +23 -0
  74. package/dist/chat/preferences.js +110 -0
  75. package/dist/chat/quote.d.ts +2 -0
  76. package/dist/chat/quote.js +25 -0
  77. package/dist/chat/recent-messages.d.ts +17 -0
  78. package/dist/chat/recent-messages.js +28 -0
  79. package/dist/chat/resume-compaction-dismissals.d.ts +3 -0
  80. package/dist/chat/resume-compaction-dismissals.js +14 -0
  81. package/dist/chat/runtime-modes.d.ts +4 -0
  82. package/dist/chat/runtime-modes.js +11 -0
  83. package/dist/chat/stash.d.ts +24 -0
  84. package/dist/chat/stash.js +88 -0
  85. package/dist/chat/subagent-conversation.d.ts +38 -0
  86. package/dist/chat/subagent-conversation.js +150 -0
  87. package/dist/chat/subagent-list.d.ts +16 -0
  88. package/dist/chat/subagent-list.js +76 -0
  89. package/dist/chat/subagent-support.d.ts +6 -0
  90. package/dist/chat/subagent-support.js +14 -0
  91. package/dist/chat/subagent-view.d.ts +33 -0
  92. package/dist/chat/subagent-view.js +65 -0
  93. package/dist/chat/thumbnails.d.ts +10 -0
  94. package/dist/chat/thumbnails.js +87 -0
  95. package/dist/chat/timeline-scroll.d.ts +16 -0
  96. package/dist/chat/timeline-scroll.js +100 -0
  97. package/dist/chat/ui/AccountPill.d.ts +3 -0
  98. package/dist/chat/ui/AccountPill.js +24 -0
  99. package/dist/chat/ui/BookmarkMarker.d.ts +8 -0
  100. package/dist/chat/ui/BookmarkMarker.js +59 -0
  101. package/dist/chat/ui/BookmarkSubmenu.d.ts +3 -0
  102. package/dist/chat/ui/BookmarkSubmenu.js +44 -0
  103. package/dist/chat/ui/ChatActivity.d.ts +3 -0
  104. package/dist/chat/ui/ChatActivity.js +87 -0
  105. package/dist/chat/ui/ChatReferenceChip.d.ts +4 -0
  106. package/dist/chat/ui/ChatReferenceChip.js +17 -0
  107. package/dist/chat/ui/CodeBlock.d.ts +4 -0
  108. package/dist/chat/ui/CodeBlock.js +130 -0
  109. package/dist/chat/ui/Composer.d.ts +14 -0
  110. package/dist/chat/ui/Composer.js +821 -0
  111. package/dist/chat/ui/ComposerInput.d.ts +28 -0
  112. package/dist/chat/ui/ComposerInput.js +118 -0
  113. package/dist/chat/ui/DiffPool.d.ts +5 -0
  114. package/dist/chat/ui/DiffPool.js +26 -0
  115. package/dist/chat/ui/EditDiff.d.ts +4 -0
  116. package/dist/chat/ui/EditDiff.js +21 -0
  117. package/dist/chat/ui/FadingWords.d.ts +3 -0
  118. package/dist/chat/ui/FadingWords.js +5 -0
  119. package/dist/chat/ui/ImageView.d.ts +6 -0
  120. package/dist/chat/ui/ImageView.js +85 -0
  121. package/dist/chat/ui/LimitState.d.ts +8 -0
  122. package/dist/chat/ui/LimitState.js +117 -0
  123. package/dist/chat/ui/Markdown.d.ts +18 -0
  124. package/dist/chat/ui/Markdown.js +158 -0
  125. package/dist/chat/ui/MessageActions.d.ts +9 -0
  126. package/dist/chat/ui/MessageActions.js +56 -0
  127. package/dist/chat/ui/Pickers.d.ts +15 -0
  128. package/dist/chat/ui/Pickers.js +110 -0
  129. package/dist/chat/ui/PromptComposer.d.ts +13 -0
  130. package/dist/chat/ui/PromptComposer.js +48 -0
  131. package/dist/chat/ui/QuoteButton.d.ts +7 -0
  132. package/dist/chat/ui/QuoteButton.js +76 -0
  133. package/dist/chat/ui/ResumeCompactionDock.d.ts +5 -0
  134. package/dist/chat/ui/ResumeCompactionDock.js +15 -0
  135. package/dist/chat/ui/RunSettings.d.ts +25 -0
  136. package/dist/chat/ui/RunSettings.js +148 -0
  137. package/dist/chat/ui/Scrubber.d.ts +17 -0
  138. package/dist/chat/ui/Scrubber.js +121 -0
  139. package/dist/chat/ui/SubagentControls.d.ts +11 -0
  140. package/dist/chat/ui/SubagentControls.js +37 -0
  141. package/dist/chat/ui/SubagentStopButton.d.ts +7 -0
  142. package/dist/chat/ui/SubagentStopButton.js +36 -0
  143. package/dist/chat/ui/SubagentTimeline.d.ts +4 -0
  144. package/dist/chat/ui/SubagentTimeline.js +92 -0
  145. package/dist/chat/ui/Timeline.d.ts +5 -0
  146. package/dist/chat/ui/Timeline.js +318 -0
  147. package/dist/chat/ui/TimelineMenu.d.ts +12 -0
  148. package/dist/chat/ui/TimelineMenu.js +61 -0
  149. package/dist/chat/ui/UnifiedDiff.d.ts +8 -0
  150. package/dist/chat/ui/UnifiedDiff.js +51 -0
  151. package/dist/chat/ui/UploadThumb.d.ts +4 -0
  152. package/dist/chat/ui/UploadThumb.js +11 -0
  153. package/dist/chat/ui/chips.d.ts +4 -0
  154. package/dist/chat/ui/chips.js +11 -0
  155. package/dist/chat/ui/code-lines.d.ts +18 -0
  156. package/dist/chat/ui/code-lines.js +46 -0
  157. package/dist/chat/ui/code-streaming.d.ts +1 -0
  158. package/dist/chat/ui/code-streaming.js +3 -0
  159. package/dist/chat/ui/code-theme.d.ts +3 -0
  160. package/dist/chat/ui/code-theme.js +10 -0
  161. package/dist/chat/ui/composer/chips.d.ts +6 -0
  162. package/dist/chat/ui/composer/chips.js +29 -0
  163. package/dist/chat/ui/composer/editor.d.ts +11 -0
  164. package/dist/chat/ui/composer/editor.js +94 -0
  165. package/dist/chat/ui/composer/keys.d.ts +30 -0
  166. package/dist/chat/ui/composer/keys.js +98 -0
  167. package/dist/chat/ui/diff-theme.d.ts +2 -0
  168. package/dist/chat/ui/diff-theme.js +20 -0
  169. package/dist/chat/ui/file-links.d.ts +5 -0
  170. package/dist/chat/ui/file-links.js +18 -0
  171. package/dist/chat/ui/find-reveal.d.ts +7 -0
  172. package/dist/chat/ui/find-reveal.js +13 -0
  173. package/dist/chat/ui/icons.d.ts +4 -0
  174. package/dist/chat/ui/icons.js +20 -0
  175. package/dist/chat/ui/markdown-blocks.d.ts +6 -0
  176. package/dist/chat/ui/markdown-blocks.js +101 -0
  177. package/dist/chat/ui/quote-selection.d.ts +3 -0
  178. package/dist/chat/ui/quote-selection.js +44 -0
  179. package/dist/chat/ui/rehype-chips.d.ts +23 -0
  180. package/dist/chat/ui/rehype-chips.js +28 -0
  181. package/dist/chat/ui/rehype-fade.d.ts +23 -0
  182. package/dist/chat/ui/rehype-fade.js +30 -0
  183. package/dist/chat/ui/remark-html-as-text.d.ts +8 -0
  184. package/dist/chat/ui/remark-html-as-text.js +24 -0
  185. package/dist/chat/ui/reveal.d.ts +13 -0
  186. package/dist/chat/ui/reveal.js +112 -0
  187. package/dist/chat/ui/rows/ForksRow.d.ts +4 -0
  188. package/dist/chat/ui/rows/ForksRow.js +40 -0
  189. package/dist/chat/ui/rows/MessageRows.d.ts +36 -0
  190. package/dist/chat/ui/rows/MessageRows.js +190 -0
  191. package/dist/chat/ui/rows/Rows.d.ts +12 -0
  192. package/dist/chat/ui/rows/Rows.js +49 -0
  193. package/dist/chat/ui/rows/SubagentRow.d.ts +15 -0
  194. package/dist/chat/ui/rows/SubagentRow.js +98 -0
  195. package/dist/chat/ui/rows/WorkRows.d.ts +46 -0
  196. package/dist/chat/ui/rows/WorkRows.js +143 -0
  197. package/dist/chat/ui/rows/WorkflowRow.d.ts +7 -0
  198. package/dist/chat/ui/rows/WorkflowRow.js +37 -0
  199. package/dist/chat/ui/rows/row-rhythm.d.ts +3 -0
  200. package/dist/chat/ui/rows/row-rhythm.js +14 -0
  201. package/dist/chat/ui/useToggleSet.d.ts +6 -0
  202. package/dist/chat/ui/useToggleSet.js +25 -0
  203. package/dist/host.d.ts +167 -0
  204. package/dist/host.js +41 -0
  205. package/dist/lazy.d.ts +5 -0
  206. package/dist/lazy.js +35 -0
  207. package/dist/locales/en/agent-chat.json +417 -0
  208. package/dist/locales/en/agent-prompts.json +42 -0
  209. package/dist/locales/en/agent-providers.json +131 -0
  210. package/dist/locales/en/agent-usage.json +131 -0
  211. package/dist/locales/nl/agent-chat.json +417 -0
  212. package/dist/locales/nl/agent-prompts.json +42 -0
  213. package/dist/locales/nl/agent-providers.json +131 -0
  214. package/dist/locales/nl/agent-usage.json +131 -0
  215. package/dist/locales.d.ts +5 -0
  216. package/dist/locales.js +18 -0
  217. package/dist/mounted-registry.d.ts +7 -0
  218. package/dist/mounted-registry.js +39 -0
  219. package/dist/port-transport.d.ts +6 -0
  220. package/dist/port-transport.js +120 -0
  221. package/dist/prompts/logic/focus.d.ts +1 -0
  222. package/dist/prompts/logic/focus.js +17 -0
  223. package/dist/prompts/logic/keys.d.ts +42 -0
  224. package/dist/prompts/logic/keys.js +84 -0
  225. package/dist/prompts/logic/prompts.d.ts +34 -0
  226. package/dist/prompts/logic/prompts.fixtures.d.ts +5 -0
  227. package/dist/prompts/logic/prompts.fixtures.js +73 -0
  228. package/dist/prompts/logic/prompts.js +22 -0
  229. package/dist/prompts/logic/subjects.d.ts +49 -0
  230. package/dist/prompts/logic/subjects.js +56 -0
  231. package/dist/prompts/logic/useFocusAfterAnswer.d.ts +6 -0
  232. package/dist/prompts/logic/useFocusAfterAnswer.js +28 -0
  233. package/dist/prompts/logic/usePromptSession.d.ts +24 -0
  234. package/dist/prompts/logic/usePromptSession.js +87 -0
  235. package/dist/prompts/ui/ApprovalBody.d.ts +13 -0
  236. package/dist/prompts/ui/ApprovalBody.js +35 -0
  237. package/dist/prompts/ui/PromptActions.d.ts +26 -0
  238. package/dist/prompts/ui/PromptActions.js +19 -0
  239. package/dist/prompts/ui/PromptCard.d.ts +16 -0
  240. package/dist/prompts/ui/PromptCard.js +50 -0
  241. package/dist/prompts/ui/PromptView.d.ts +2 -0
  242. package/dist/prompts/ui/PromptView.js +47 -0
  243. package/dist/prompts/ui/QuestionBody.d.ts +9 -0
  244. package/dist/prompts/ui/QuestionBody.js +87 -0
  245. package/dist/providers/AccountColors.d.ts +5 -0
  246. package/dist/providers/AccountColors.js +8 -0
  247. package/dist/providers/AccountDetail.d.ts +12 -0
  248. package/dist/providers/AccountDetail.js +76 -0
  249. package/dist/providers/AccountVariables.d.ts +8 -0
  250. package/dist/providers/AccountVariables.js +48 -0
  251. package/dist/providers/AddAccountForm.d.ts +10 -0
  252. package/dist/providers/AddAccountForm.js +58 -0
  253. package/dist/providers/CliDetail.d.ts +11 -0
  254. package/dist/providers/CliDetail.js +62 -0
  255. package/dist/providers/ProvidersPane.d.ts +8 -0
  256. package/dist/providers/ProvidersPane.js +90 -0
  257. package/dist/providers/account-actions.d.ts +7 -0
  258. package/dist/providers/account-actions.js +53 -0
  259. package/dist/providers/parts.d.ts +9 -0
  260. package/dist/providers/parts.js +18 -0
  261. package/dist/providers/provider-abilities.d.ts +2 -0
  262. package/dist/providers/provider-abilities.js +7 -0
  263. package/dist/providers/variables.d.ts +13 -0
  264. package/dist/providers/variables.js +53 -0
  265. package/dist/scope.d.ts +11 -0
  266. package/dist/scope.js +10 -0
  267. package/dist/settings/sections.d.ts +13 -0
  268. package/dist/settings/sections.js +30 -0
  269. package/dist/state/chats.d.ts +32 -0
  270. package/dist/state/chats.js +124 -0
  271. package/dist/state/provider-accounts.d.ts +17 -0
  272. package/dist/state/provider-accounts.js +63 -0
  273. package/dist/state/providers.d.ts +17 -0
  274. package/dist/state/providers.js +29 -0
  275. package/dist/state/usage.d.ts +41 -0
  276. package/dist/state/usage.js +100 -0
  277. package/dist/theme.css +186 -0
  278. package/dist/transport.d.ts +24 -0
  279. package/dist/transport.js +16 -0
  280. package/dist/ui/PlainTextarea.d.ts +2 -0
  281. package/dist/ui/PlainTextarea.js +5 -0
  282. package/dist/usage/LimitsList.d.ts +18 -0
  283. package/dist/usage/LimitsList.js +69 -0
  284. package/dist/usage/UsageBreakdown.d.ts +9 -0
  285. package/dist/usage/UsageBreakdown.js +69 -0
  286. package/dist/usage/UsageChart.d.ts +10 -0
  287. package/dist/usage/UsageChart.js +49 -0
  288. package/dist/usage/UsageDialog.d.ts +6 -0
  289. package/dist/usage/UsageDialog.js +15 -0
  290. package/dist/usage/UsageLimits.d.ts +1 -0
  291. package/dist/usage/UsageLimits.js +62 -0
  292. package/dist/usage/UsageLimitsCard.d.ts +4 -0
  293. package/dist/usage/UsageLimitsCard.js +27 -0
  294. package/dist/usage/UsagePage.d.ts +7 -0
  295. package/dist/usage/UsagePage.js +161 -0
  296. package/dist/usage/UsagePane.d.ts +3 -0
  297. package/dist/usage/UsagePane.js +24 -0
  298. package/dist/usage/UsageSummary.d.ts +12 -0
  299. package/dist/usage/UsageSummary.js +15 -0
  300. package/dist/usage/UsageTiles.d.ts +7 -0
  301. package/dist/usage/UsageTiles.js +19 -0
  302. package/dist/usage/format.d.ts +14 -0
  303. package/dist/usage/format.js +50 -0
  304. package/dist/usage/limit-groups.d.ts +22 -0
  305. package/dist/usage/limit-groups.js +78 -0
  306. package/dist/usage/limits.d.ts +4 -0
  307. package/dist/usage/limits.js +58 -0
  308. package/dist/usage/money.d.ts +1 -0
  309. package/dist/usage/money.js +13 -0
  310. package/dist/usage/summary.d.ts +34 -0
  311. package/dist/usage/summary.js +90 -0
  312. package/package.json +639 -2
@@ -0,0 +1,128 @@
1
+ /* What a file dragged out of an app's own list of files carries: the paths, space separated, relative
2
+ to the folder. The composer takes it next to the image files it already accepts. */
3
+ export const MENTION_DRAG_TYPE = 'application/x-ruimte-mention';
4
+ const isBoundary = (char) => char === undefined || /\s/.test(char);
5
+ // A path at the end of a sentence still counts, so "open @a.ts." is a mention of a.ts.
6
+ const isTokenEnd = (char) => isBoundary(char) || /[.,;:!?)]/.test(char);
7
+ /*
8
+ * The `<sigil>word` the caret sits in, if any. The sigil has to open a word (start of text or after
9
+ * whitespace) so an email address never opens the picker, and the word may not have been
10
+ * closed with whitespace yet.
11
+ */
12
+ const findQuery = (text, caret, sigil) => {
13
+ const before = text.slice(0, caret);
14
+ const at = before.lastIndexOf(sigil);
15
+ if (at < 0 || !isBoundary(before[at - 1])) {
16
+ return null;
17
+ }
18
+ const query = before.slice(at + 1);
19
+ if (/\s/.test(query) || !isBoundary(text[caret])) {
20
+ return null;
21
+ }
22
+ return { start: at, query };
23
+ };
24
+ export const findMentionQuery = (text, caret) => findQuery(text, caret, '@');
25
+ /* The `$word` the caret sits in. The name has to start with a letter, so `$20` and `$1e6` stay text. */
26
+ export const findSkillQuery = (text, caret) => {
27
+ const found = findQuery(text, caret, '$');
28
+ return found === null || (found.query !== '' && !/^[A-Za-z]/.test(found.query)) ? null : found;
29
+ };
30
+ /* Replaces the `<sigil>query` under the caret with the chosen token and a space, and says where the caret goes. */
31
+ export const insertToken = (text, query, sigil, value) => {
32
+ const end = query.start + 1 + query.query.length;
33
+ const token = `${sigil}${value} `;
34
+ return { text: `${text.slice(0, query.start)}${token}${text.slice(end)}`, caret: query.start + token.length };
35
+ };
36
+ /* Takes the `<sigil>query` under the caret out, for a pick that lands beside the text instead of in it. */
37
+ export const dropQuery = (text, query) => ({
38
+ text: `${text.slice(0, query.start)}${text.slice(query.start + 1 + query.query.length)}`,
39
+ caret: query.start
40
+ });
41
+ export const insertMention = (text, query, path) => insertToken(text, query, '@', path);
42
+ export const insertSkill = (text, query, name) => insertToken(text, query, '$', name);
43
+ const findToken = (text, sigil, value, from) => {
44
+ const token = `${sigil}${value}`;
45
+ let index = text.indexOf(token, from);
46
+ while (index >= 0) {
47
+ if (isBoundary(text[index - 1]) && isTokenEnd(text[index + token.length])) {
48
+ return index;
49
+ }
50
+ index = text.indexOf(token, index + 1);
51
+ }
52
+ return -1;
53
+ };
54
+ const present = (text, sigil, chosen) => {
55
+ const found = [];
56
+ for (const value of new Set(chosen)) {
57
+ const index = findToken(text, sigil, value, 0);
58
+ if (index >= 0) {
59
+ found.push({ index, value });
60
+ }
61
+ }
62
+ return found.sort((a, b) => a.index - b.index).map((entry) => entry.value);
63
+ };
64
+ /* The chosen mentions that still sit in the text as a whole `@path` token, in text order and without doubles. */
65
+ export const presentMentions = (text, chosen) => present(text, '@', chosen);
66
+ /* The chosen skills that still sit in the text as a whole `$name` token, in text order and without doubles. */
67
+ export const presentSkills = (text, chosen) => present(text, '$', chosen);
68
+ /*
69
+ * The characters a segment stands for, sigil included. Joined, the segments spell the text they
70
+ * were cut from.
71
+ */
72
+ export const chipText = (segment) => {
73
+ if (segment.kind === 'mention') {
74
+ return `@${segment.path}`;
75
+ }
76
+ if (segment.kind === 'skill') {
77
+ return `$${segment.name}`;
78
+ }
79
+ return segment.text;
80
+ };
81
+ /*
82
+ * Where the chosen mentions and skills sit in the text, in text order; longer values win when one
83
+ * prefixes another. A token that overlaps an excluded range (a code span, say) is text.
84
+ */
85
+ export const chipRanges = (text, mentions, skills = [], excluded = []) => {
86
+ const tokens = [
87
+ ...[...new Set(mentions)].map((value) => ({ sigil: '@', kind: 'mention', value })),
88
+ ...[...new Set(skills)].map((value) => ({ sigil: '$', kind: 'skill', value }))
89
+ ].sort((a, b) => b.value.length - a.value.length);
90
+ const allowed = (from, to) => !excluded.some((range) => from < range.to && to > range.from);
91
+ const ranges = [];
92
+ let cursor = 0;
93
+ while (cursor < text.length) {
94
+ let best = null;
95
+ for (const token of tokens) {
96
+ const length = token.value.length + 1;
97
+ let index = findToken(text, token.sigil, token.value, cursor);
98
+ while (index >= 0 && !allowed(index, index + length)) {
99
+ index = findToken(text, token.sigil, token.value, index + 1);
100
+ }
101
+ if (index >= 0 && (best === null || index < best.from)) {
102
+ best = { from: index, to: index + length, kind: token.kind, value: token.value };
103
+ }
104
+ }
105
+ if (best === null) {
106
+ break;
107
+ }
108
+ ranges.push(best);
109
+ cursor = best.to;
110
+ }
111
+ return ranges;
112
+ };
113
+ /* Splits the text so a renderer can draw the mentions and skills as chips. */
114
+ export const tokenizeChips = (text, mentions, skills = []) => {
115
+ const segments = [];
116
+ let cursor = 0;
117
+ for (const range of chipRanges(text, mentions, skills)) {
118
+ if (range.from > cursor) {
119
+ segments.push({ kind: 'text', text: text.slice(cursor, range.from) });
120
+ }
121
+ segments.push(range.kind === 'mention' ? { kind: 'mention', path: range.value } : { kind: 'skill', name: range.value });
122
+ cursor = range.to;
123
+ }
124
+ if (cursor < text.length) {
125
+ segments.push({ kind: 'text', text: text.slice(cursor) });
126
+ }
127
+ return segments;
128
+ };
@@ -0,0 +1,5 @@
1
+ export interface PersistedJson<T> {
2
+ read(): T;
3
+ write(value: T): boolean;
4
+ }
5
+ export declare const persistedJson: <T>(key: string, parse: (raw: string | null) => T, fallback: T) => PersistedJson<T>;
@@ -0,0 +1,24 @@
1
+ /*
2
+ * A value the browser keeps between sessions. Storage that refuses, and a record written by a
3
+ * version that shaped it differently, both come back as the fallback rather than as an error: there
4
+ * is nothing a person could do about either, and the composer has to open regardless.
5
+ */
6
+ export const persistedJson = (key, parse, fallback) => ({
7
+ read: () => {
8
+ try {
9
+ return parse(localStorage.getItem(key));
10
+ }
11
+ catch {
12
+ return fallback;
13
+ }
14
+ },
15
+ write: (value) => {
16
+ try {
17
+ localStorage.setItem(key, JSON.stringify(value));
18
+ return true;
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ });
@@ -0,0 +1,23 @@
1
+ import type { AgentKind, ChatPreferencesPayload, ModelSelection, ProviderAccounts, RuntimeMode } from '@ruimte/agent-contracts';
2
+ export interface ChatPreferences {
3
+ selectionByProvider: Partial<Record<AgentKind, ModelSelection>>;
4
+ lastProvider: AgentKind | null;
5
+ runtimeMode: RuntimeMode;
6
+ terminalRuntimeMode: RuntimeMode;
7
+ accountByMachine: Record<string, Partial<Record<AgentKind, string>>>;
8
+ changedAt: number;
9
+ }
10
+ export declare const DEFAULT_CHAT_PREFERENCES: ChatPreferences;
11
+ export declare const parseChatPreferences: (raw: string | null) => ChatPreferences;
12
+ export declare const defaultProvider: (preferences: ChatPreferences) => AgentKind | null;
13
+ export declare const selectionFor: (preferences: ChatPreferences, provider: AgentKind | null | undefined) => ModelSelection | null;
14
+ export declare const withSelection: (preferences: ChatPreferences, provider: AgentKind, selection: ModelSelection) => ChatPreferences;
15
+ export declare const accountFor: (preferences: ChatPreferences, scopeId: string, provider: AgentKind, accounts?: ProviderAccounts | null) => string | null;
16
+ export declare const withAccount: (preferences: ChatPreferences, scopeId: string, provider: AgentKind, account: string | null) => ChatPreferences;
17
+ export declare const chatPreferencesPayload: (preferences: ChatPreferences, scopeId: string, accounts?: ProviderAccounts | null) => ChatPreferencesPayload;
18
+ export declare const useChatPreferences: import("zustand").UseBoundStore<import("zustand").StoreApi<ChatPreferences>>;
19
+ export declare const readChatPreferences: () => ChatPreferences;
20
+ export declare const rememberChatPreferences: (patch: Partial<ChatPreferences>) => void;
21
+ export declare const rememberChatSelection: (provider: AgentKind, selection: ModelSelection) => void;
22
+ export declare const rememberChatAccount: (scopeId: string, provider: AgentKind, account: string | null) => void;
23
+ export declare const forgetChatSelection: (provider: AgentKind) => void;
@@ -0,0 +1,110 @@
1
+ import { create } from 'zustand';
2
+ import { persistedJson } from './persisted-json';
3
+ const STORAGE_KEY = 'ruimte.chat.preferences';
4
+ export const DEFAULT_CHAT_PREFERENCES = {
5
+ selectionByProvider: {},
6
+ lastProvider: null,
7
+ runtimeMode: 'full-access',
8
+ terminalRuntimeMode: 'full-access',
9
+ accountByMachine: {},
10
+ changedAt: 0
11
+ };
12
+ /*
13
+ * Only the fields we know today, so what an older build wrote (one global `selection`, the plan
14
+ * toggle's `interactionMode`) falls away here instead of riding along in every later write. A
15
+ * global selection cannot be mapped back onto a provider, so it is dropped: one lost pick.
16
+ */
17
+ export const parseChatPreferences = (raw) => {
18
+ if (raw === null) {
19
+ return DEFAULT_CHAT_PREFERENCES;
20
+ }
21
+ try {
22
+ const stored = JSON.parse(raw);
23
+ return {
24
+ selectionByProvider: { ...stored.selectionByProvider },
25
+ lastProvider: stored.lastProvider ?? null,
26
+ runtimeMode: stored.runtimeMode ?? DEFAULT_CHAT_PREFERENCES.runtimeMode,
27
+ terminalRuntimeMode: stored.terminalRuntimeMode ?? DEFAULT_CHAT_PREFERENCES.terminalRuntimeMode,
28
+ accountByMachine: { ...stored.accountByMachine },
29
+ changedAt: typeof stored.changedAt === 'number' ? stored.changedAt : 0
30
+ };
31
+ }
32
+ catch {
33
+ return DEFAULT_CHAT_PREFERENCES;
34
+ }
35
+ };
36
+ /* Which CLI a chat opens on when its node names none: the last one a model was picked for. */
37
+ export const defaultProvider = (preferences) => preferences.lastProvider;
38
+ /* The model a chat of this provider starts with; null lets the host fall back to the CLI's own default. */
39
+ export const selectionFor = (preferences, provider) => {
40
+ const owner = provider ?? preferences.lastProvider;
41
+ if (owner === null) {
42
+ return null;
43
+ }
44
+ return preferences.selectionByProvider[owner] ?? null;
45
+ };
46
+ /* The pick lands in its provider's slot and makes that provider the global default. */
47
+ export const withSelection = (preferences, provider, selection) => ({
48
+ ...preferences,
49
+ selectionByProvider: { ...preferences.selectionByProvider, [provider]: selection },
50
+ lastProvider: provider
51
+ });
52
+ /*
53
+ * The account a new agent of this CLI starts under on this machine; null is the CLI's default account.
54
+ * Given the machine's accounts, a pick it turned off or removed is the default account again, so a new
55
+ * chat is never refused over it; `undefined` is a machine that did not say yet, where the pick stands.
56
+ */
57
+ export const accountFor = (preferences, scopeId, provider, accounts) => {
58
+ const picked = preferences.accountByMachine[scopeId]?.[provider] ?? null;
59
+ if (picked === null || accounts === undefined) {
60
+ return picked;
61
+ }
62
+ const account = accounts?.accounts[picked];
63
+ return account?.kind === provider && account.enabled !== false ? picked : null;
64
+ };
65
+ /* The default account is what an absent pick means, so picking it takes the pick away. */
66
+ export const withAccount = (preferences, scopeId, provider, account) => {
67
+ const { [provider]: _previous, ...others } = preferences.accountByMachine[scopeId] ?? {};
68
+ const picks = account === null || account === provider ? others : { ...others, [provider]: account };
69
+ const { [scopeId]: _machine, ...machines } = preferences.accountByMachine;
70
+ return { ...preferences, accountByMachine: Object.keys(picks).length === 0 ? machines : { ...machines, [scopeId]: picks } };
71
+ };
72
+ /* What a machine starts a chat with when it starts one with no client mounting it; `accounts` as `accountFor` takes them. */
73
+ export const chatPreferencesPayload = (preferences, scopeId, accounts) => {
74
+ const picks = Object.keys(preferences.accountByMachine[scopeId] ?? {}).flatMap((provider) => {
75
+ const account = accountFor(preferences, scopeId, provider, accounts);
76
+ return account === null ? [] : [[provider, account]];
77
+ });
78
+ return {
79
+ runtimeMode: preferences.runtimeMode,
80
+ terminalRuntimeMode: preferences.terminalRuntimeMode,
81
+ selections: preferences.selectionByProvider,
82
+ ...(picks.length === 0 ? {} : { accounts: Object.fromEntries(picks) }),
83
+ changedAt: preferences.changedAt
84
+ };
85
+ };
86
+ const storage = persistedJson(STORAGE_KEY, parseChatPreferences, DEFAULT_CHAT_PREFERENCES);
87
+ /* What a new agent starts with: the last model and modes the person picked, like a remembered default. */
88
+ export const useChatPreferences = create(() => storage.read());
89
+ export const readChatPreferences = () => useChatPreferences.getState();
90
+ const write = (preferences) => {
91
+ const next = { ...preferences, changedAt: Date.now() };
92
+ useChatPreferences.setState(next);
93
+ storage.write(next);
94
+ };
95
+ /* The composer writes here on every change and the settings dialog too; both edit the same remembered default. */
96
+ export const rememberChatPreferences = (patch) => {
97
+ write({ ...useChatPreferences.getState(), ...patch });
98
+ };
99
+ export const rememberChatSelection = (provider, selection) => {
100
+ write(withSelection(useChatPreferences.getState(), provider, selection));
101
+ };
102
+ export const rememberChatAccount = (scopeId, provider, account) => {
103
+ write(withAccount(useChatPreferences.getState(), scopeId, provider, account));
104
+ };
105
+ /* Back to the CLI's own default for this provider; the other providers keep what they had. */
106
+ export const forgetChatSelection = (provider) => {
107
+ const current = useChatPreferences.getState();
108
+ const { [provider]: _dropped, ...rest } = current.selectionByProvider;
109
+ write({ ...current, selectionByProvider: rest, lastProvider: current.lastProvider === provider ? null : current.lastProvider });
110
+ };
@@ -0,0 +1,2 @@
1
+ export declare const quoteOf: (selected: string) => string | null;
2
+ export declare const withQuote: (quote: string, message: string) => string;
@@ -0,0 +1,25 @@
1
+ /*
2
+ * The text a selection in an answer quotes: its lines without trailing spaces or the blank lines
3
+ * around them, and at most one blank line in a row. A selection of only whitespace quotes nothing.
4
+ */
5
+ export const quoteOf = (selected) => {
6
+ const text = selected
7
+ .replace(/\r\n?/g, '\n')
8
+ .split('\n')
9
+ .map((line) => line.replace(/\s+$/, ''))
10
+ .join('\n')
11
+ .replace(/^\n+|\n+$/g, '')
12
+ .replace(/\n{3,}/g, '\n\n');
13
+ return text.trim() === '' ? null : text;
14
+ };
15
+ /* The message as it goes out: the quote as a markdown blockquote, a blank line, then what was typed. */
16
+ export const withQuote = (quote, message) => {
17
+ if (quote === '') {
18
+ return message;
19
+ }
20
+ const block = quote
21
+ .split('\n')
22
+ .map((line) => (line === '' ? '>' : `> ${line}`))
23
+ .join('\n');
24
+ return message === '' ? block : `${block}\n\n${message}`;
25
+ };
@@ -0,0 +1,17 @@
1
+ import type { ChatItem } from '@ruimte/agent-contracts';
2
+ export interface RecentChatMessage {
3
+ role: 'user' | 'assistant';
4
+ text: string;
5
+ createdAt: number;
6
+ }
7
+ export interface RecentChatMessages {
8
+ messages: RecentChatMessage[];
9
+ truncated: boolean;
10
+ }
11
+ type MessageItem = Extract<ChatItem, {
12
+ kind: 'user' | 'assistant';
13
+ }>;
14
+ export declare const recentMessages: (eligible: readonly MessageItem[], limit: number) => RecentChatMessages;
15
+ export declare const recentChatMessages: (items: Record<string, ChatItem>, order: readonly string[], limit: number) => RecentChatMessages;
16
+ export declare const recentSubagentMessages: (items: readonly ChatItem[], limit: number) => RecentChatMessages;
17
+ export {};
@@ -0,0 +1,28 @@
1
+ const MAX_TOTAL_CHARACTERS = 12_000;
2
+ const MAX_MESSAGE_CHARACTERS = 3_000;
3
+ const clipped = (text) => text.length <= MAX_MESSAGE_CHARACTERS ? { text, truncated: false } : { text: `${text.slice(0, MAX_MESSAGE_CHARACTERS - 1)}…`, truncated: true };
4
+ const isMessage = (item) => item !== undefined && (item.kind === 'user' || item.kind === 'assistant') && item.text.trim() !== '';
5
+ /* The last messages of a conversation already cut down to the ones worth reading, clipped to what a reader can take in. */
6
+ export const recentMessages = (eligible, limit) => {
7
+ const candidates = eligible.slice(-limit);
8
+ const messages = [];
9
+ let characters = 0;
10
+ let truncated = candidates.length < eligible.length;
11
+ for (const item of candidates.toReversed()) {
12
+ const message = clipped(item.text.trim());
13
+ const remaining = MAX_TOTAL_CHARACTERS - characters;
14
+ if (remaining <= 0) {
15
+ truncated = true;
16
+ break;
17
+ }
18
+ const text = message.text.length <= remaining ? message.text : `${message.text.slice(0, Math.max(0, remaining - 1))}…`;
19
+ messages.unshift({ role: item.kind, text, createdAt: item.createdAt });
20
+ characters += text.length;
21
+ truncated ||= message.truncated || text.length < message.text.length;
22
+ }
23
+ return { messages, truncated };
24
+ };
25
+ /* A sub-agent's replies sit in the thread too, and belong to its own conversation rather than the chat's. */
26
+ export const recentChatMessages = (items, order, limit) => recentMessages(order.map((id) => items[id]).filter((item) => isMessage(item) && !(item.kind === 'assistant' && item.parentToolUseId)), limit);
27
+ /* Everything in a sub-agent's conversation is its own, so nothing is left out for being nested. */
28
+ export const recentSubagentMessages = (items, limit) => recentMessages(items.filter(isMessage), limit);
@@ -0,0 +1,3 @@
1
+ import { type ChatScope } from '../scope';
2
+ export declare const useResumeCompactionDismissal: (chatId: string) => string | null;
3
+ export declare const dismissResumeCompaction: (scope: ChatScope, chatId: string, turnId: string) => void;
@@ -0,0 +1,14 @@
1
+ import { create } from 'zustand';
2
+ import { useChatScope } from '../scope';
3
+ /*
4
+ * Which turn a person answered "Keep full history" on, per chat. In memory only: the offer appears
5
+ * at most once per silence, so a reload asking again costs nothing, and a refusal is not a setting.
6
+ */
7
+ const useDismissals = create(() => ({ byKey: {} }));
8
+ export const useResumeCompactionDismissal = (chatId) => {
9
+ const { keyOf } = useChatScope();
10
+ return useDismissals((s) => s.byKey[keyOf(chatId)] ?? null);
11
+ };
12
+ export const dismissResumeCompaction = (scope, chatId, turnId) => {
13
+ useDismissals.setState((s) => ({ byKey: { ...s.byKey, [scope.keyOf(chatId)]: turnId } }));
14
+ };
@@ -0,0 +1,4 @@
1
+ import { type RuntimeMode } from '@ruimte/agent-contracts';
2
+ export declare const RUNTIME_MODES: readonly RuntimeMode[];
3
+ export declare const runtimeModeLabel: (mode: RuntimeMode) => string;
4
+ export declare const runtimeModeHint: (mode: RuntimeMode) => string;
@@ -0,0 +1,11 @@
1
+ import i18next from 'i18next';
2
+ import { RuntimeModeSchema } from '@ruimte/agent-contracts';
3
+ /*
4
+ * The runtime modes in the order they are offered, shared by the composer's mode picker, the
5
+ * settings dialog and the agent menus. That order is the schema's, which the host reads as the
6
+ * rank from narrowest to widest. Only the ids live here: the words are in `modes.<id>` of the `chat`
7
+ * namespace, read when a surface draws one rather than when this module loads.
8
+ */
9
+ export const RUNTIME_MODES = RuntimeModeSchema.options;
10
+ export const runtimeModeLabel = (mode) => i18next.t(`agent-chat:modes.${mode}.label`);
11
+ export const runtimeModeHint = (mode) => i18next.t(`agent-chat:modes.${mode}.hint`);
@@ -0,0 +1,24 @@
1
+ import { type ChatDraft } from './drafts';
2
+ export declare const STASH_SHORTCUT: import("@ruimte/ui/shortcut").Shortcut;
3
+ export declare const STASH_LIMIT = 20;
4
+ export interface StashedAttachment {
5
+ name: string;
6
+ mime: string;
7
+ size: number;
8
+ }
9
+ export interface StashedPrompt {
10
+ id: string;
11
+ text: string;
12
+ mentions: string[];
13
+ skills: string[];
14
+ attachments: StashedAttachment[];
15
+ createdAt: number;
16
+ }
17
+ export declare const parseStash: (raw: string | null) => StashedPrompt[];
18
+ export declare const withStashed: (prompts: StashedPrompt[], entry: StashedPrompt) => StashedPrompt[];
19
+ export declare const stashedFrom: (draft: ChatDraft, id: string, now: number) => StashedPrompt | null;
20
+ export declare const useStash: import("zustand").UseBoundStore<import("zustand").StoreApi<{
21
+ prompts: StashedPrompt[];
22
+ }>>;
23
+ export declare const stashDraft: (draft: ChatDraft) => boolean;
24
+ export declare const forgetStashed: (id: string) => void;
@@ -0,0 +1,88 @@
1
+ import { create } from 'zustand';
2
+ import { uploadBytes } from './attachments';
3
+ import { isEmptyDraft } from './drafts';
4
+ import { isRecord } from './logic/json';
5
+ import { persistedJson } from './persisted-json';
6
+ import { withQuote } from './quote';
7
+ import { shortcut } from '@ruimte/ui/shortcut';
8
+ /* Puts the draft away, or takes the last one back on an empty box. */
9
+ export const STASH_SHORTCUT = shortcut('Mod+S');
10
+ const STORAGE_KEY = 'ruimte.chat.stash';
11
+ // Past this the list is an archive nobody reads; the oldest entry makes room for a new one.
12
+ export const STASH_LIMIT = 20;
13
+ const strings = (value) => (Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : []);
14
+ const attachments = (value) => {
15
+ if (!Array.isArray(value)) {
16
+ return [];
17
+ }
18
+ return value.filter(isRecord).map((entry) => ({
19
+ name: typeof entry.name === 'string' ? entry.name : '',
20
+ mime: typeof entry.mime === 'string' ? entry.mime : '',
21
+ size: typeof entry.size === 'number' ? entry.size : 0
22
+ }));
23
+ };
24
+ /* Only the fields we know today, so anything an older or a broken write left behind falls away here. */
25
+ export const parseStash = (raw) => {
26
+ if (raw === null) {
27
+ return [];
28
+ }
29
+ try {
30
+ const stored = JSON.parse(raw);
31
+ if (!Array.isArray(stored)) {
32
+ return [];
33
+ }
34
+ return stored
35
+ .filter(isRecord)
36
+ .filter((entry) => typeof entry.id === 'string' && typeof entry.text === 'string')
37
+ .map((entry) => ({
38
+ id: entry.id,
39
+ text: entry.text,
40
+ mentions: strings(entry.mentions),
41
+ skills: strings(entry.skills),
42
+ attachments: attachments(entry.attachments),
43
+ createdAt: typeof entry.createdAt === 'number' ? entry.createdAt : 0
44
+ }))
45
+ .slice(0, STASH_LIMIT);
46
+ }
47
+ catch {
48
+ return [];
49
+ }
50
+ };
51
+ /* Newest first, capped: the entry that has been waiting longest is the one nobody comes back for. */
52
+ export const withStashed = (prompts, entry) => [entry, ...prompts].slice(0, STASH_LIMIT);
53
+ /* What a draft looks like on the shelf, its quote folded into the text; null when there is nothing worth putting there. */
54
+ export const stashedFrom = (draft, id, now) => {
55
+ if (isEmptyDraft(draft)) {
56
+ return null;
57
+ }
58
+ return {
59
+ id,
60
+ text: withQuote(draft.quote, draft.text),
61
+ mentions: [...draft.mentions],
62
+ skills: [...draft.skills],
63
+ attachments: draft.attachments.map((attachment) => ({ name: attachment.name, mime: attachment.mime, size: uploadBytes(attachment) })),
64
+ createdAt: now
65
+ };
66
+ };
67
+ const storage = persistedJson(STORAGE_KEY, parseStash, []);
68
+ /*
69
+ * Prompts put aside for later, one list for the whole app rather than one per chat: on a canvas a
70
+ * stashed prompt usually moves to another node, which is the reason to stash it at all.
71
+ */
72
+ export const useStash = create(() => ({ prompts: storage.read() }));
73
+ const write = (prompts) => {
74
+ useStash.setState({ prompts });
75
+ storage.write(prompts);
76
+ };
77
+ /* Puts a draft on the shelf; false when the composer held nothing to put there. */
78
+ export const stashDraft = (draft) => {
79
+ const entry = stashedFrom(draft, `stash-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, Date.now());
80
+ if (!entry) {
81
+ return false;
82
+ }
83
+ write(withStashed(useStash.getState().prompts, entry));
84
+ return true;
85
+ };
86
+ export const forgetStashed = (id) => {
87
+ write(useStash.getState().prompts.filter((entry) => entry.id !== id));
88
+ };
@@ -0,0 +1,38 @@
1
+ import type { ChatItem } from '@ruimte/agent-contracts';
2
+ import { type ChatTransport } from '../transport';
3
+ export declare const SUBAGENT_PAGE = 60;
4
+ export interface SubagentConversationState {
5
+ status: 'loading' | 'ready' | 'failed';
6
+ items: ChatItem[];
7
+ cursor: string | null;
8
+ live: boolean;
9
+ loadingEarlier: boolean;
10
+ error: string | null;
11
+ unsupported: boolean;
12
+ }
13
+ export declare const INITIAL_CONVERSATION: SubagentConversationState;
14
+ export declare const mergeNewest: (current: ChatItem[], currentCursor: string | null, page: ChatItem[], pageCursor: string | null) => {
15
+ items: ChatItem[];
16
+ cursor: string | null;
17
+ };
18
+ export declare class SubagentConversation {
19
+ private readonly transport;
20
+ private readonly chatId;
21
+ private readonly toolUseId;
22
+ private readonly onChange;
23
+ private readonly unsubscribe;
24
+ private state;
25
+ private disposed;
26
+ private queue;
27
+ private refreshWaiting;
28
+ constructor(transport: ChatTransport, chatId: string, toolUseId: string, onChange: (state: SubagentConversationState) => void);
29
+ get current(): SubagentConversationState;
30
+ start(): Promise<void>;
31
+ refresh(): Promise<void>;
32
+ loadEarlier(): Promise<void>;
33
+ dispose(): void;
34
+ private readNewest;
35
+ private ask;
36
+ private enqueue;
37
+ private set;
38
+ }