@adecore/agents-react 0.21.0 → 0.21.2

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 (137) hide show
  1. package/dist/agents/status-look.js +2 -3
  2. package/dist/chat/chat-client.d.ts +1 -1
  3. package/dist/chat/chat-client.js +2 -2
  4. package/dist/chat/drafts.js +6 -12
  5. package/dist/chat/logic/ansi.js +5 -1
  6. package/dist/chat/logic/fork.js +8 -9
  7. package/dist/chat/logic/limit.js +17 -14
  8. package/dist/chat/logic/resume-compaction.js +2 -11
  9. package/dist/chat/logic/scrubber.js +9 -18
  10. package/dist/chat/logic/timeline.js +15 -10
  11. package/dist/chat/logic/tool-catalog.js +3 -4
  12. package/dist/chat/mentions.js +5 -6
  13. package/dist/chat/runtime-modes.js +2 -4
  14. package/dist/chat/stash.js +1 -3
  15. package/dist/chat/thumbnails.js +3 -4
  16. package/dist/chat/timeline-scroll.js +39 -45
  17. package/dist/chat/ui/BookmarkSubmenu.js +1 -5
  18. package/dist/chat/ui/CodeBlock.js +2 -3
  19. package/dist/chat/ui/Composer.js +57 -69
  20. package/dist/chat/ui/Markdown.js +11 -11
  21. package/dist/chat/ui/Pickers.js +5 -9
  22. package/dist/chat/ui/QuoteButton.js +2 -3
  23. package/dist/chat/ui/RunSettings.js +8 -2
  24. package/dist/chat/ui/Scrubber.js +2 -3
  25. package/dist/chat/ui/SubagentControls.js +1 -3
  26. package/dist/chat/ui/Timeline.js +15 -18
  27. package/dist/chat/ui/TimelineMenu.js +3 -9
  28. package/dist/chat/ui/UnifiedDiff.js +3 -5
  29. package/dist/chat/ui/VisualFrame.js +3 -4
  30. package/dist/chat/ui/composer/keys.js +19 -11
  31. package/dist/chat/ui/intelligent-ui/UiBlockFrame.d.ts +14 -1
  32. package/dist/chat/ui/intelligent-ui/UiBlockFrame.js +47 -5
  33. package/dist/chat/ui/intelligent-ui/UiReply.d.ts +3 -1
  34. package/dist/chat/ui/intelligent-ui/UiReply.js +34 -30
  35. package/dist/chat/ui/intelligent-ui/block-local.d.ts +9 -0
  36. package/dist/chat/ui/intelligent-ui/block-local.js +30 -1
  37. package/dist/chat/ui/intelligent-ui/node-text.d.ts +6 -0
  38. package/dist/chat/ui/intelligent-ui/node-text.js +54 -0
  39. package/dist/chat/ui/intelligent-ui/registry.js +2 -1
  40. package/dist/chat/ui/intelligent-ui/render-context.d.ts +4 -1
  41. package/dist/chat/ui/intelligent-ui/renderers/chart.js +5 -7
  42. package/dist/chat/ui/intelligent-ui/renderers/choices.js +6 -2
  43. package/dist/chat/ui/intelligent-ui/renderers/content.js +6 -13
  44. package/dist/chat/ui/intelligent-ui/renderers/data.js +8 -15
  45. package/dist/chat/ui/intelligent-ui/renderers/inputs.d.ts +1 -0
  46. package/dist/chat/ui/intelligent-ui/renderers/inputs.js +16 -4
  47. package/dist/chat/ui/intelligent-ui/renderers/links.js +2 -1
  48. package/dist/chat/ui/intelligent-ui/use-ui-links.d.ts +4 -4
  49. package/dist/chat/ui/intelligent-ui/use-ui-links.js +29 -10
  50. package/dist/chat/ui/intelligent-ui/use-ui-queries.d.ts +2 -1
  51. package/dist/chat/ui/intelligent-ui/use-ui-queries.js +34 -21
  52. package/dist/chat/ui/rows/MessageRows.js +3 -4
  53. package/dist/chat/ui/rows/WorkRows.d.ts +4 -3
  54. package/dist/chat/ui/rows/WorkRows.js +19 -14
  55. package/dist/chat/visual-viewport.d.ts +1 -1
  56. package/dist/chat/visual-viewport.js +9 -8
  57. package/dist/locales/en/agent-chat.json +23 -4
  58. package/dist/locales/nl/agent-chat.json +23 -4
  59. package/dist/port-transport.js +4 -8
  60. package/dist/state/chats.js +15 -15
  61. package/dist/state/usage.js +44 -49
  62. package/dist/storage.js +7 -7
  63. package/dist/theme.css +6 -14
  64. package/dist/usage/LimitsList.js +1 -2
  65. package/dist/usage/UsageBreakdown.js +1 -3
  66. package/dist/usage/UsageDialog.js +2 -4
  67. package/dist/usage/format.js +6 -6
  68. package/dist/usage/money.js +1 -5
  69. package/dist/usage/summary.js +2 -4
  70. package/package.json +24 -29
  71. package/src/agents/status-look.ts +2 -3
  72. package/src/chat/chat-client.ts +2 -3
  73. package/src/chat/drafts.ts +6 -14
  74. package/src/chat/logic/ansi.ts +5 -1
  75. package/src/chat/logic/fork.ts +8 -9
  76. package/src/chat/logic/limit.ts +17 -16
  77. package/src/chat/logic/resume-compaction.ts +2 -11
  78. package/src/chat/logic/scrubber.ts +13 -17
  79. package/src/chat/logic/timeline.ts +22 -11
  80. package/src/chat/logic/tool-catalog.ts +7 -6
  81. package/src/chat/mentions.ts +5 -6
  82. package/src/chat/runtime-modes.ts +2 -4
  83. package/src/chat/stash.ts +1 -3
  84. package/src/chat/thumbnails.ts +3 -4
  85. package/src/chat/timeline-scroll.ts +43 -52
  86. package/src/chat/ui/BookmarkSubmenu.tsx +1 -5
  87. package/src/chat/ui/CodeBlock.tsx +2 -3
  88. package/src/chat/ui/Composer.tsx +66 -86
  89. package/src/chat/ui/Markdown.tsx +13 -17
  90. package/src/chat/ui/Pickers.tsx +5 -9
  91. package/src/chat/ui/QuoteButton.tsx +2 -3
  92. package/src/chat/ui/RunSettings.tsx +9 -2
  93. package/src/chat/ui/Scrubber.tsx +2 -3
  94. package/src/chat/ui/SubagentControls.tsx +1 -3
  95. package/src/chat/ui/SubagentTimeline.tsx +1 -2
  96. package/src/chat/ui/Timeline.tsx +18 -20
  97. package/src/chat/ui/TimelineMenu.tsx +3 -9
  98. package/src/chat/ui/UnifiedDiff.tsx +3 -5
  99. package/src/chat/ui/VisualFrame.tsx +3 -4
  100. package/src/chat/ui/composer/keys.ts +21 -11
  101. package/src/chat/ui/intelligent-ui/UiBlockFrame.tsx +135 -5
  102. package/src/chat/ui/intelligent-ui/UiReply.tsx +48 -38
  103. package/src/chat/ui/intelligent-ui/block-local.ts +36 -1
  104. package/src/chat/ui/intelligent-ui/node-text.ts +60 -0
  105. package/src/chat/ui/intelligent-ui/registry.ts +2 -1
  106. package/src/chat/ui/intelligent-ui/render-context.ts +8 -2
  107. package/src/chat/ui/intelligent-ui/renderers/chart.tsx +5 -7
  108. package/src/chat/ui/intelligent-ui/renderers/choices.tsx +6 -2
  109. package/src/chat/ui/intelligent-ui/renderers/content.tsx +9 -14
  110. package/src/chat/ui/intelligent-ui/renderers/data.tsx +13 -19
  111. package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +28 -5
  112. package/src/chat/ui/intelligent-ui/renderers/links.tsx +2 -1
  113. package/src/chat/ui/intelligent-ui/use-ui-links.ts +31 -11
  114. package/src/chat/ui/intelligent-ui/use-ui-queries.ts +48 -22
  115. package/src/chat/ui/rehype-chips.ts +1 -1
  116. package/src/chat/ui/rehype-fade.ts +1 -1
  117. package/src/chat/ui/remark-html-as-text.ts +1 -1
  118. package/src/chat/ui/rows/MessageRows.tsx +4 -5
  119. package/src/chat/ui/rows/WorkRows.tsx +86 -69
  120. package/src/chat/visual-viewport.ts +10 -8
  121. package/src/host.ts +1 -3
  122. package/src/locales/en/agent-chat.json +23 -4
  123. package/src/locales/nl/agent-chat.json +23 -4
  124. package/src/port-transport.ts +4 -8
  125. package/src/state/chats.ts +16 -15
  126. package/src/state/usage.ts +49 -60
  127. package/src/storage.ts +7 -7
  128. package/src/theme.css +6 -14
  129. package/src/usage/LimitsList.tsx +2 -4
  130. package/src/usage/UsageBreakdown.tsx +2 -5
  131. package/src/usage/UsageDialog.tsx +2 -4
  132. package/src/usage/format.ts +6 -6
  133. package/src/usage/money.ts +1 -5
  134. package/src/usage/summary.ts +2 -4
  135. package/dist/chat/ui/intelligent-ui/UiBlockFooter.d.ts +0 -15
  136. package/dist/chat/ui/intelligent-ui/UiBlockFooter.js +0 -50
  137. package/src/chat/ui/intelligent-ui/UiBlockFooter.tsx +0 -139
@@ -498,6 +498,7 @@
498
498
  "from": "from {{previous}}"
499
499
  },
500
500
  "table": {
501
+ "label": "Table of {{columns}}",
501
502
  "showAll": "Show all {{total}} rows"
502
503
  },
503
504
  "chart": {
@@ -514,8 +515,10 @@
514
515
  "outsideProject": "Outside the project, shown as text",
515
516
  "options": "Options",
516
517
  "choices": "Choices",
518
+ "checklist": "Checklist",
517
519
  "choice": {
518
520
  "waiting": "Available when the reply is done",
521
+ "reading": "Available once the live data is read",
519
522
  "unavailable": "Not available",
520
523
  "sent": "Sent",
521
524
  "queued": "Queued",
@@ -525,14 +528,11 @@
525
528
  "answered": "Answered with “{{label}}”",
526
529
  "chose": "Chose “{{label}}”",
527
530
  "choseOlder": "Chose “{{label}}” on the reply of {{time}}",
528
- "sendContext": "Send",
529
- "copyText": "Copy text",
530
531
  "fixes_one": "{{count}} fix",
531
532
  "fixes_other": "{{count}} fixes",
532
533
  "shownAsText": "Shown as text",
533
534
  "unknownComponent": "This version cannot draw {{component}}",
534
535
  "renderFailed": "Could not draw this part",
535
- "tooLarge": "The block is too large to draw",
536
536
  "unreadable": "The block could not be read",
537
537
  "live": {
538
538
  "label": "Live",
@@ -540,7 +540,26 @@
540
540
  "sources": "Reads {{sources}}",
541
541
  "failed": "Could not read {{source}}",
542
542
  "refused": "Not allowed to read {{source}}",
543
- "notRead": "Not read"
543
+ "notRead": "Not read",
544
+ "reasons": {
545
+ "access-unavailable": "The access of the chat that wrote this is no longer available.",
546
+ "block-stale": "This block changed since it was drawn.",
547
+ "busy": "Too many live blocks are reading right now.",
548
+ "link-unchecked": "This link has to be checked again.",
549
+ "link-unsupported": "This is not a link the machine can open.",
550
+ "links-unsupported": "The machine does not open links from blocks.",
551
+ "no-unique-match": "The source found no single match for that name.",
552
+ "outside-project": "This is outside the project the agent wrote this in.",
553
+ "query-undeclared": "This block does not read that source.",
554
+ "refresh-limit": "This source reads at most once every ten seconds.",
555
+ "result-too-large": "What the source returned is too large.",
556
+ "snapshot-too-large": "The first readings were too large to keep.",
557
+ "source-unregistered": "The machine does not know this source.",
558
+ "stale-read": "Read the source again before you choose.",
559
+ "target-unavailable": "What this points to is no longer available.",
560
+ "timed-out": "The source took too long to answer.",
561
+ "unreadable": "The source could not be read."
562
+ }
544
563
  }
545
564
  }
546
565
  }
@@ -498,6 +498,7 @@
498
498
  "from": "vanaf {{previous}}"
499
499
  },
500
500
  "table": {
501
+ "label": "Tabel van {{columns}}",
501
502
  "showAll": "Alle {{total}} rijen tonen"
502
503
  },
503
504
  "chart": {
@@ -514,8 +515,10 @@
514
515
  "outsideProject": "Buiten het project, als tekst getoond",
515
516
  "options": "Opties",
516
517
  "choices": "Keuzes",
518
+ "checklist": "Checklijst",
517
519
  "choice": {
518
520
  "waiting": "Beschikbaar zodra het antwoord klaar is",
521
+ "reading": "Beschikbaar zodra de live gegevens gelezen zijn",
519
522
  "unavailable": "Niet beschikbaar",
520
523
  "sent": "Verstuurd",
521
524
  "queued": "In de wachtrij",
@@ -525,14 +528,11 @@
525
528
  "answered": "Beantwoord met “{{label}}”",
526
529
  "chose": "Gekozen: “{{label}}”",
527
530
  "choseOlder": "Gekozen: “{{label}}”, bij het antwoord van {{time}}",
528
- "sendContext": "Versturen",
529
- "copyText": "Tekst kopiëren",
530
531
  "fixes_one": "{{count}} correctie",
531
532
  "fixes_other": "{{count}} correcties",
532
533
  "shownAsText": "Als tekst getoond",
533
534
  "unknownComponent": "Deze versie kan {{component}} niet tekenen",
534
535
  "renderFailed": "Dit deel kon niet getekend worden",
535
- "tooLarge": "Het blok is te groot om te tekenen",
536
536
  "unreadable": "Het blok kon niet gelezen worden",
537
537
  "live": {
538
538
  "label": "Live",
@@ -540,7 +540,26 @@
540
540
  "sources": "Leest {{sources}}",
541
541
  "failed": "{{source}} lezen lukte niet",
542
542
  "refused": "Geen toegang tot {{source}}",
543
- "notRead": "Niet gelezen"
543
+ "notRead": "Niet gelezen",
544
+ "reasons": {
545
+ "access-unavailable": "De toegang van de chat die dit schreef is niet meer beschikbaar.",
546
+ "block-stale": "Dit blok is veranderd sinds het getekend werd.",
547
+ "busy": "Er lezen nu te veel live blokken tegelijk.",
548
+ "link-unchecked": "Deze link moet opnieuw gecontroleerd worden.",
549
+ "link-unsupported": "Dit is geen link die de machine kan openen.",
550
+ "links-unsupported": "De machine opent geen links uit blokken.",
551
+ "no-unique-match": "De bron vond voor die naam niet precies één overeenkomst.",
552
+ "outside-project": "Dit valt buiten het project waarin de agent dit schreef.",
553
+ "query-undeclared": "Dit blok leest die bron niet.",
554
+ "refresh-limit": "Deze bron leest hooguit eens per tien seconden.",
555
+ "result-too-large": "Wat de bron teruggaf is te groot.",
556
+ "snapshot-too-large": "De eerste metingen waren te groot om te bewaren.",
557
+ "source-unregistered": "De machine kent deze bron niet.",
558
+ "stale-read": "Lees de bron opnieuw voordat je kiest.",
559
+ "target-unavailable": "Waar dit naar verwijst is niet meer beschikbaar.",
560
+ "timed-out": "De bron deed er te lang over om te antwoorden.",
561
+ "unreadable": "De bron kon niet gelezen worden."
562
+ }
544
563
  }
545
564
  }
546
565
  }
@@ -93,15 +93,11 @@ export function portTransport(port) {
93
93
  return promise;
94
94
  },
95
95
  on(event, handler) {
96
- let handlers = eventHandlers.get(event);
97
- if (!handlers) {
98
- handlers = new Set();
99
- eventHandlers.set(event, handlers);
100
- }
101
- const set = handlers;
102
- set.add(handler);
96
+ const handlers = eventHandlers.get(event) ?? new Set();
97
+ eventHandlers.set(event, handlers);
98
+ handlers.add(handler);
103
99
  return () => {
104
- set.delete(handler);
100
+ handlers.delete(handler);
105
101
  };
106
102
  },
107
103
  subscribeStatus(handler) {
@@ -80,7 +80,11 @@ export function applyEvent(state, event) {
80
80
  case 'delta': {
81
81
  const item = state.items[event.itemId];
82
82
  if (event.uiQueries && item?.kind === 'assistant') {
83
- return { ...state, items: { ...state.items, [event.itemId]: { ...item, uiQueries: event.uiQueries } } };
83
+ // The text first, as the daemon's thread applies it, then the metadata on what it grew into.
84
+ const { uiQueries, ...text } = event;
85
+ const grown = event.text === '' ? state : applyEvent(state, text);
86
+ const reply = grown.items[event.itemId] ?? item;
87
+ return { ...grown, items: { ...grown.items, [event.itemId]: { ...reply, uiQueries } } };
84
88
  }
85
89
  if (event.ui !== undefined && event.text === '') {
86
90
  if (item?.kind === 'assistant' && item.streaming && event.textLength === item.text.length) {
@@ -109,6 +113,14 @@ export function applyEvent(state, event) {
109
113
  return stateOf(event.info, event.items);
110
114
  }
111
115
  }
116
+ /* Fields of a held chat that are not part of its thread; a chat this client does not hold stays as it is. */
117
+ function withFields(store, key, fields) {
118
+ const current = store.byKey[key];
119
+ if (!current) {
120
+ return {};
121
+ }
122
+ return { byKey: { ...store.byKey, [key]: { ...current, ...fields } } };
123
+ }
112
124
  export const useChats = create((set) => ({
113
125
  byKey: {},
114
126
  statusByKey: {},
@@ -162,22 +174,10 @@ export const useChats = create((set) => ({
162
174
  });
163
175
  },
164
176
  bookmarks(key, bookmarks) {
165
- set((s) => {
166
- const current = s.byKey[key];
167
- if (!current) {
168
- return {};
169
- }
170
- return { byKey: { ...s.byKey, [key]: { ...current, bookmarks } } };
171
- });
177
+ set((s) => withFields(s, key, { bookmarks }));
172
178
  },
173
179
  visuals(key, visuals) {
174
- set((s) => {
175
- const current = s.byKey[key];
176
- if (!current) {
177
- return {};
178
- }
179
- return { byKey: { ...s.byKey, [key]: { ...current, visuals } } };
180
- });
180
+ set((s) => withFields(s, key, { visuals }));
181
181
  },
182
182
  forget(key) {
183
183
  set((s) => {
@@ -32,12 +32,6 @@ function parsePreferences(raw) {
32
32
  }
33
33
  }
34
34
  const storage = persistedJson(storageKey, parsePreferences, DEFAULTS, () => chatStorageLegacyKeys('usage'));
35
- function readPreferences() {
36
- return storage.read();
37
- }
38
- function persist(preferences) {
39
- storage.write(preferences);
40
- }
41
35
  /* The three stored choices out of the store, so a setter writes the other two back unchanged. */
42
36
  function chosen(state) {
43
37
  return { period: state.period, metric: state.metric, currency: state.currency };
@@ -49,10 +43,7 @@ function pad(value) {
49
43
  export function dayOf(date) {
50
44
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
51
45
  }
52
- /*
53
- * What a period asks for. Today counts from local midnight in hours, the way the commit log already
54
- * groups days, so every other period reads as one bar per calendar day.
55
- */
46
+ /* What a period asks for: today in hours from local midnight, every other period a bar per calendar day. */
56
47
  export function windowFor(period, now = new Date()) {
57
48
  const days = USAGE_PERIODS.find((entry) => entry.id === period)?.days ?? 7;
58
49
  const from = new Date(now.getFullYear(), now.getMonth(), now.getDate() - (days - 1));
@@ -75,46 +66,50 @@ export function askedKey(payload) {
75
66
  const EMPTY = { asked: null, summary: null, loading: false, failed: false, limits: null };
76
67
  /* Reads the period, metric and currency again, once another window of the same origin wrote them. */
77
68
  export function reloadUsagePreferences() {
78
- useUsageStore.setState(readPreferences());
69
+ useUsageStore.setState(storage.read());
79
70
  }
80
- export const useUsageStore = createChatStore((set, get) => ({
81
- ...DEFAULTS,
82
- byScope: {},
83
- chosen: null,
84
- choose(scopeId) {
85
- set({ chosen: scopeId });
86
- },
87
- setPeriod(period) {
88
- persist({ ...chosen(get()), period });
89
- set({ period });
90
- },
91
- setMetric(metric) {
92
- persist({ ...chosen(get()), metric });
93
- set({ metric });
94
- },
95
- setCurrency(currency) {
96
- persist({ ...chosen(get()), currency });
97
- set({ currency });
98
- },
99
- setLoading(scopeId, loading) {
100
- set({ byScope: { ...get().byScope, [scopeId]: { ...(get().byScope[scopeId] ?? EMPTY), loading } } });
101
- },
102
- receive(scopeId, asked, summary) {
103
- set({
104
- byScope: { ...get().byScope, [scopeId]: { ...(get().byScope[scopeId] ?? EMPTY), asked, summary, loading: false, failed: false } }
105
- });
106
- },
107
- fail(scopeId) {
108
- set({ byScope: { ...get().byScope, [scopeId]: { ...(get().byScope[scopeId] ?? EMPTY), loading: false, failed: true } } });
109
- },
110
- setLimits(scopeId, limits) {
111
- set({ byScope: { ...get().byScope, [scopeId]: { ...(get().byScope[scopeId] ?? EMPTY), limits } } });
112
- },
113
- forget(scopeId) {
114
- const { [scopeId]: _gone, ...rest } = get().byScope;
115
- set({ byScope: rest });
116
- }
117
- }), (store) => store.setState(readPreferences()));
71
+ export const useUsageStore = createChatStore((set, get) => {
72
+ const choosePreference = (change) => {
73
+ storage.write({ ...chosen(get()), ...change });
74
+ set(change);
75
+ };
76
+ const patchRow = (scopeId, change) => {
77
+ set({ byScope: { ...get().byScope, [scopeId]: { ...(get().byScope[scopeId] ?? EMPTY), ...change } } });
78
+ };
79
+ return {
80
+ ...DEFAULTS,
81
+ byScope: {},
82
+ chosen: null,
83
+ choose(scopeId) {
84
+ set({ chosen: scopeId });
85
+ },
86
+ setPeriod(period) {
87
+ choosePreference({ period });
88
+ },
89
+ setMetric(metric) {
90
+ choosePreference({ metric });
91
+ },
92
+ setCurrency(currency) {
93
+ choosePreference({ currency });
94
+ },
95
+ setLoading(scopeId, loading) {
96
+ patchRow(scopeId, { loading });
97
+ },
98
+ receive(scopeId, asked, summary) {
99
+ patchRow(scopeId, { asked, summary, loading: false, failed: false });
100
+ },
101
+ fail(scopeId) {
102
+ patchRow(scopeId, { loading: false, failed: true });
103
+ },
104
+ setLimits(scopeId, limits) {
105
+ patchRow(scopeId, { limits });
106
+ },
107
+ forget(scopeId) {
108
+ const { [scopeId]: _gone, ...rest } = get().byScope;
109
+ set({ byScope: rest });
110
+ }
111
+ };
112
+ }, (store) => store.setState(storage.read()));
118
113
  export function useUsage(select) {
119
114
  const { id } = useChatScope();
120
115
  return useUsageStore((s) => select({ period: s.period, metric: s.metric, currency: s.currency, ...(s.byScope[id] ?? EMPTY) }));
package/dist/storage.js CHANGED
@@ -64,25 +64,25 @@ export function createChatStore(initial, hydrate) {
64
64
  hydrate(store);
65
65
  };
66
66
  hydrators.push(hydrateOnce);
67
- const useStore = ((...args) => {
67
+ const ready = () => {
68
68
  hydrateChatStorage();
69
69
  hydrateOnce();
70
+ };
71
+ const useStore = ((...args) => {
72
+ ready();
70
73
  return store(...args);
71
74
  });
72
75
  Object.assign(useStore, store);
73
76
  useStore.getState = () => {
74
- hydrateChatStorage();
75
- hydrateOnce();
77
+ ready();
76
78
  return store.getState();
77
79
  };
78
80
  useStore.subscribe = (...args) => {
79
- hydrateChatStorage();
80
- hydrateOnce();
81
+ ready();
81
82
  return store.subscribe(...args);
82
83
  };
83
84
  useStore.setState = ((...args) => {
84
- hydrateChatStorage();
85
- hydrateOnce();
85
+ ready();
86
86
  store.setState(...args);
87
87
  });
88
88
  return useStore;
package/dist/theme.css CHANGED
@@ -76,8 +76,6 @@
76
76
  }
77
77
 
78
78
  @layer components {
79
- /* ---------------------------------------------------------------- Chat */
80
-
81
79
  /* Text in a chat is there to be read and copied, so the thread selects even though the app does
82
80
  not. Its chrome stays out of it: a drag on a fold header or a chip would select the row.
83
81
  The pretty wrap from `body` would be recomputed on the growing paragraph every frame a reply
@@ -160,18 +158,12 @@
160
158
  }
161
159
 
162
160
  /*
163
- * Markdown, outside every layer on purpose. The typography plugin registers `.prose` as a utility,
164
- * and a later layer beats an earlier one whatever the specificity, so a rule that has to outrank
165
- * prose cannot live in `components`. Unlayered styles win over every layer, which is the one
166
- * place the app's tokens and scale are sure to land on top of the plugin's.
167
- */
168
- /*
169
- * Markdown anywhere in the app: the chat timeline, a note node and the file preview all render
170
- * the same `Markdown` component, which carries `prose prose-sm max-w-none text-sm` next to this
171
- * class. Prose brings the rhythm; everything here is what the app decides instead of the plugin.
172
- * The semantic tokens flip with the theme on their own, so the prose variables read from them
173
- * and `prose-invert` is a second source of truth the app does not need. The size comes from the
174
- * `text-sm` utility, which a node, a view and the file preview each move.
161
+ * Markdown, outside every layer on purpose: the typography plugin registers `.prose` as a utility,
162
+ * and only unlayered styles are sure to land on top of it.
163
+ *
164
+ * Every `Markdown` carries `prose prose-sm max-w-none text-sm` next to this class. Prose brings the
165
+ * rhythm; everything here is what the app decides instead. The semantic tokens flip with the theme on
166
+ * their own, so the prose variables read from them and `prose-invert` is not needed.
175
167
  */
176
168
  .prose.chat-markdown {
177
169
  --tw-prose-body: var(--text);
@@ -9,8 +9,7 @@ import { formatClock, formatWeekdayClock, isSameDay, formatCountdown, formatPerc
9
9
  import { ProviderLogo } from '../agents/ProviderLogo.js';
10
10
  import { PROVIDER_COLORS, PROVIDER_LABELS } from './format.js';
11
11
  import { accountNote, explain, isSignedOut, nextReset } from './limit-groups.js';
12
- /* Red where a window is nearly spent, amber where it is worth knowing. A window with room to spare
13
- is not news, so it takes the text color rather than a hue that competes with the two that are. */
12
+ /* Red where a window is nearly spent, amber where it is worth knowing; room to spare is not news and takes the text color. */
14
13
  function toneOf(used) {
15
14
  return used >= 0.9 ? 'bg-status-error' : used >= 0.7 ? 'bg-status-needs-you' : 'bg-text';
16
15
  }
@@ -12,11 +12,9 @@ import { modelNameFromSlug } from '../agents/model-name.js';
12
12
  import { formatCount, formatTokens, PROVIDER_COLORS, PROVIDER_LABELS, shortPath, slotLabel } from './format.js';
13
13
  import { useMoney } from './money.js';
14
14
  import { deriveDays } from './summary.js';
15
- /* The row of every table, the same height and the same hover as a commit row in the git panel. */
16
15
  const ROW = 'flex h-8 items-center gap-2 rounded-md px-2 text-xs hover:bg-surface-hover';
17
16
  const HEAD = 'flex h-6 items-center gap-2 px-2 text-xs text-text-faint';
18
- /* A row is padded so its hover has room around the text, and the table pulls that padding back off
19
- again. The first column then starts on the same line as every other section of the page. */
17
+ /* The table pulls the padding that gives a row's hover room back off, so its first column lines up with the page. */
20
18
  const TABLE = '-mx-2 flex flex-col';
21
19
  const TABS = ['models', 'projects', 'day'];
22
20
  /* A share of the biggest row, never under one pixel. A row that did something has to be visible. */
@@ -2,10 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import i18next from 'i18next';
3
3
  import { Dialog } from '@adecore/ui';
4
4
  /*
5
- * The frame of the usage page: larger than the settings, since the chart and the breakdown need the
6
- * width. What goes in it (`UsagePage`, in an `ErrorBoundary` of the app's) mounts only while it is
7
- * open, so nothing is scanned behind a closed dialog. It is a dialog and not a view, since none of it
8
- * belongs to one project.
5
+ * The frame of the usage page, wider than the settings for the chart and the breakdown. What goes in
6
+ * it mounts only while it is open, so nothing is scanned behind a closed dialog.
9
7
  */
10
8
  export function UsageDialog({ open, onOpenChange, children }) {
11
9
  return (_jsx(Dialog.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(Dialog.Popup, { className: "flex h-[820px] w-[1080px] flex-col", children: [children, _jsx(Dialog.Description, { className: "sr-only", children: i18next.t('agent-usage:dialog.description') })] }) }));
@@ -1,7 +1,6 @@
1
1
  import { formatDay, formatHour, formatWeekdayDay, formatMoney, formatNumber, formatTokens } from '@adecore/ui/format';
2
2
  export { formatClock } from '@adecore/ui/format';
3
- /* Every number on this page is read in the region of the machine looking at it, as a bank statement
4
- would be: `$5,480.96` in the US, `$ 5.480,96` in the Netherlands. The labels stay English. */
3
+ /* Read in the region of the machine looking at it: `$5,480.96` in the US, `$ 5.480,96` in the Netherlands. */
5
4
  export function formatCount(value) {
6
5
  return formatNumber(value);
7
6
  }
@@ -23,8 +22,10 @@ export function moneyFormat(currency, rate) {
23
22
  }
24
23
  export const PROVIDER_LABELS = { claude: 'Claude Code', codex: 'Codex' };
25
24
  export const PROVIDER_COLORS = { claude: 'var(--chart-claude)', codex: 'var(--chart-codex)' };
26
- /* A slot is `2026-09-10` or `2026-09-10T14`, already in the viewer's own zone, so it is read as
27
- plain wall clock and never handed to a parser that would shift it back. */
25
+ /*
26
+ * A slot is `2026-09-10` or `2026-09-10T14`, already in the viewer's own zone, so it is read as plain
27
+ * wall clock and never handed to a parser that would shift it back.
28
+ */
28
29
  function dateOfSlot(slot) {
29
30
  return new Date(Number(slot.slice(0, 4)), Number(slot.slice(5, 7)) - 1, Number(slot.slice(8, 10)));
30
31
  }
@@ -47,8 +48,7 @@ export function slotAxisLabel(slot) {
47
48
  export function formatDate(at) {
48
49
  return formatDay(at);
49
50
  }
50
- /* A path under a row that already carries the name: enough of the tail to tell two checkouts of the
51
- same repository apart, without the home directory nobody needs to read again. */
51
+ /* Enough of a path's tail to tell two checkouts of the same repository apart, under a row that already carries the name. */
52
52
  export function shortPath(path) {
53
53
  const parts = path.split('/').filter(Boolean);
54
54
  return parts.length <= 3 ? path : `…/${parts.slice(-3).join('/')}`;
@@ -1,11 +1,7 @@
1
1
  import { useMemo } from 'react';
2
2
  import { useUsage } from '../state/usage.js';
3
3
  import { moneyFormat } from './format.js';
4
- /*
5
- * The formatter every amount on the page goes through: the currency that was chosen, at the rate
6
- * the summary on screen carries. It is a hook rather than a prop because every row wants it and
7
- * none of them wants to know where the rate came from.
8
- */
4
+ /* The formatter every amount on the page goes through: the chosen currency, at the rate the summary on screen carries. */
9
5
  export function useMoney() {
10
6
  const currency = useUsage((s) => s.currency);
11
7
  const rate = useUsage((s) => s.summary?.rate ?? null);
@@ -74,10 +74,8 @@ export function deriveUsage(summary, metric) {
74
74
  };
75
75
  }
76
76
  /*
77
- * The same buckets the chart draws, written out as a table of calendar days, newest first. Today is
78
- * drawn per hour but read per day, so an hour slot is folded onto the date it names. A day nothing
79
- * happened in is left out, since the chart already shows the gap, and a quiet fortnight would otherwise
80
- * be fourteen rows of zeroes between the days worth reading.
77
+ * The chart's buckets as a table of calendar days, newest first; an hour slot folds onto the date it
78
+ * names. A day nothing happened in is left out, since the chart already shows the gap.
81
79
  */
82
80
  export function deriveDays(summary) {
83
81
  const rows = new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/agents-react",
3
- "version": "0.21.0",
3
+ "version": "0.21.2",
4
4
  "description": "React agent chat threads, composers, approvals, provider accounts and usage views.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -519,11 +519,6 @@
519
519
  "types": "./dist/chat/ui/icons.d.ts",
520
520
  "default": "./dist/chat/ui/icons.js"
521
521
  },
522
- "./chat/ui/intelligent-ui/UiBlockFooter": {
523
- "source": "./src/chat/ui/intelligent-ui/UiBlockFooter.tsx",
524
- "types": "./dist/chat/ui/intelligent-ui/UiBlockFooter.d.ts",
525
- "default": "./dist/chat/ui/intelligent-ui/UiBlockFooter.js"
526
- },
527
522
  "./chat/ui/intelligent-ui/UiBlockFrame": {
528
523
  "source": "./src/chat/ui/intelligent-ui/UiBlockFrame.tsx",
529
524
  "types": "./dist/chat/ui/intelligent-ui/UiBlockFrame.d.ts",
@@ -534,6 +529,11 @@
534
529
  "types": "./dist/chat/ui/intelligent-ui/UiFallbackPart.d.ts",
535
530
  "default": "./dist/chat/ui/intelligent-ui/UiFallbackPart.js"
536
531
  },
532
+ "./chat/ui/intelligent-ui/UiReply": {
533
+ "source": "./src/chat/ui/intelligent-ui/UiReply.tsx",
534
+ "types": "./dist/chat/ui/intelligent-ui/UiReply.d.ts",
535
+ "default": "./dist/chat/ui/intelligent-ui/UiReply.js"
536
+ },
537
537
  "./chat/ui/intelligent-ui/block-local": {
538
538
  "source": "./src/chat/ui/intelligent-ui/block-local.ts",
539
539
  "types": "./dist/chat/ui/intelligent-ui/block-local.d.ts",
@@ -599,6 +599,11 @@
599
599
  "types": "./dist/chat/ui/intelligent-ui/renderers/text.d.ts",
600
600
  "default": "./dist/chat/ui/intelligent-ui/renderers/text.js"
601
601
  },
602
+ "./chat/ui/intelligent-ui/reply-parts": {
603
+ "source": "./src/chat/ui/intelligent-ui/reply-parts.ts",
604
+ "types": "./dist/chat/ui/intelligent-ui/reply-parts.d.ts",
605
+ "default": "./dist/chat/ui/intelligent-ui/reply-parts.js"
606
+ },
602
607
  "./chat/ui/intelligent-ui/table-data": {
603
608
  "source": "./src/chat/ui/intelligent-ui/table-data.ts",
604
609
  "types": "./dist/chat/ui/intelligent-ui/table-data.d.ts",
@@ -614,6 +619,16 @@
614
619
  "types": "./dist/chat/ui/intelligent-ui/use-later.d.ts",
615
620
  "default": "./dist/chat/ui/intelligent-ui/use-later.js"
616
621
  },
622
+ "./chat/ui/intelligent-ui/use-ui-links": {
623
+ "source": "./src/chat/ui/intelligent-ui/use-ui-links.ts",
624
+ "types": "./dist/chat/ui/intelligent-ui/use-ui-links.d.ts",
625
+ "default": "./dist/chat/ui/intelligent-ui/use-ui-links.js"
626
+ },
627
+ "./chat/ui/intelligent-ui/use-ui-queries": {
628
+ "source": "./src/chat/ui/intelligent-ui/use-ui-queries.ts",
629
+ "types": "./dist/chat/ui/intelligent-ui/use-ui-queries.d.ts",
630
+ "default": "./dist/chat/ui/intelligent-ui/use-ui-queries.js"
631
+ },
617
632
  "./chat/ui/markdown-blocks": {
618
633
  "source": "./src/chat/ui/markdown-blocks.ts",
619
634
  "types": "./dist/chat/ui/markdown-blocks.d.ts",
@@ -987,26 +1002,6 @@
987
1002
  "source": "./src/chat/visual-viewport.ts",
988
1003
  "types": "./dist/chat/visual-viewport.d.ts",
989
1004
  "default": "./dist/chat/visual-viewport.js"
990
- },
991
- "./chat/ui/intelligent-ui/use-ui-links": {
992
- "source": "./src/chat/ui/intelligent-ui/use-ui-links.ts",
993
- "types": "./dist/chat/ui/intelligent-ui/use-ui-links.d.ts",
994
- "default": "./dist/chat/ui/intelligent-ui/use-ui-links.js"
995
- },
996
- "./chat/ui/intelligent-ui/use-ui-queries": {
997
- "source": "./src/chat/ui/intelligent-ui/use-ui-queries.ts",
998
- "types": "./dist/chat/ui/intelligent-ui/use-ui-queries.d.ts",
999
- "default": "./dist/chat/ui/intelligent-ui/use-ui-queries.js"
1000
- },
1001
- "./chat/ui/intelligent-ui/UiReply": {
1002
- "source": "./src/chat/ui/intelligent-ui/UiReply.tsx",
1003
- "types": "./dist/chat/ui/intelligent-ui/UiReply.d.ts",
1004
- "default": "./dist/chat/ui/intelligent-ui/UiReply.js"
1005
- },
1006
- "./chat/ui/intelligent-ui/reply-parts": {
1007
- "source": "./src/chat/ui/intelligent-ui/reply-parts.ts",
1008
- "types": "./dist/chat/ui/intelligent-ui/reply-parts.d.ts",
1009
- "default": "./dist/chat/ui/intelligent-ui/reply-parts.js"
1010
1005
  }
1011
1006
  },
1012
1007
  "scripts": {
@@ -1016,8 +1011,8 @@
1016
1011
  "test:visuals": "bun --config=integration.bunfig.toml --conditions=source test ./src/chat/visual-viewport.integration.test.ts"
1017
1012
  },
1018
1013
  "dependencies": {
1019
- "@adecore/agent-contracts": "0.21.0",
1020
- "@adecore/intelligent-ui": "0.21.0",
1014
+ "@adecore/agent-contracts": "0.21.2",
1015
+ "@adecore/intelligent-ui": "0.21.2",
1021
1016
  "@codemirror/commands": "^6.11.1",
1022
1017
  "@codemirror/language": "^6.12.4",
1023
1018
  "@codemirror/state": "^6.7.6",
@@ -1038,7 +1033,7 @@
1038
1033
  "zustand": "^5.0.15"
1039
1034
  },
1040
1035
  "peerDependencies": {
1041
- "@adecore/ui": "^0.21.0",
1036
+ "@adecore/ui": "^0.21.2",
1042
1037
  "i18next": "^26.4.2",
1043
1038
  "react": "^19.3.0",
1044
1039
  "react-dom": "^19.3.0",
@@ -10,9 +10,8 @@ export interface StatusLook {
10
10
  }
11
11
 
12
12
  /*
13
- * One state, one mark: an entry in the sub-agent flyout, a task on its node (`TaskMark`), what finished
14
- * in the toolbar (`StatusSummary`) and work in progress in a toast. A spinner stops under reduced
15
- * motion with every other animation (`styles.css`).
13
+ * One state, one mark, wherever work is drawn: a sub-agent entry, a task, a summary or a toast. A
14
+ * spinner stops under reduced motion with every other animation.
16
15
  */
17
16
  const LOOKS: Record<StatusWord, StatusLook> = {
18
17
  running: { icon: LoaderCircle, tone: 'text-status-running', spins: true },
@@ -63,6 +63,7 @@ export class ChatClient {
63
63
  private readonly mounted = new MountedRegistry<Mounted>();
64
64
  private readonly unsubscribe: Array<() => void> = [];
65
65
  private readonly earlier = new Map<string, Promise<void>>();
66
+ private readonly inspections = new Map<string, Promise<ChatAttachResult>>();
66
67
  private preferences: ChatPreferencesPayload | null = null;
67
68
 
68
69
  constructor(transport: ChatTransport, sink: ChatSink, providers: ProviderSink | null = null) {
@@ -89,8 +90,6 @@ export class ChatClient {
89
90
  }
90
91
  }
91
92
 
92
- private readonly inspections = new Map<string, Promise<ChatAttachResult>>();
93
-
94
93
  inspect(chatId: string): Promise<ChatAttachResult> {
95
94
  const pending = this.inspections.get(chatId);
96
95
  if (pending) {
@@ -347,7 +346,7 @@ export class ChatClient {
347
346
  this.sendPreferences();
348
347
  void this.loadProviders();
349
348
  void this.loadStatuses();
350
- void this.mounted.reattachAll((chatId) => this.attach(chatId).then(() => undefined));
349
+ void this.mounted.reattachAll((chatId) => this.attach(chatId));
351
350
  return;
352
351
  }
353
352
  this.mounted.detachAll();