@adecore/agents-react 0.36.0 → 0.37.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.
- package/dist/chat/chat-client.d.ts +5 -0
- package/dist/chat/chat-client.js +91 -20
- package/dist/chat/index.d.ts +1 -0
- package/dist/chat/index.js +2 -1
- package/dist/chat/logic/picker-entries.js +11 -6
- package/dist/chat/logic/scrubber.d.ts +4 -2
- package/dist/chat/logic/scrubber.js +3 -3
- package/dist/chat/pending-prompts.d.ts +15 -0
- package/dist/chat/pending-prompts.js +93 -0
- package/dist/chat/reading-width.js +6 -2
- package/dist/chat/ui/PromptComposer.d.ts +2 -2
- package/dist/chat/ui/PromptComposer.js +92 -98
- package/dist/chat/ui/SubagentTimeline.js +128 -182
- package/dist/chat/ui/Timeline.js +86 -60
- package/dist/chat/ui/code-lines.d.ts +5 -0
- package/dist/chat/ui/code-lines.js +37 -0
- package/dist/chat/ui/composer/use-pending-requests.d.ts +2 -2
- package/dist/chat/ui/composer/use-pending-requests.js +29 -37
- package/dist/chat/ui/highlighter.d.ts +1 -1
- package/dist/chat/ui/highlighter.js +4 -2
- package/dist/locales/en/agent-chat.d.ts +1 -0
- package/dist/locales/en/agent-chat.js +1 -0
- package/dist/locales/nl/agent-chat.d.ts +1 -0
- package/dist/locales/nl/agent-chat.js +1 -0
- package/dist/prompts/logic/subjects.d.ts +5 -0
- package/dist/prompts/ui/PromptCard.js +1 -1
- package/dist/prompts/ui/PromptView.js +104 -95
- package/dist/state/chats.d.ts +4 -0
- package/dist/state/chats.js +84 -48
- package/dist/theme.css +1 -1
- package/package.json +8 -8
- package/src/chat/chat-client.ts +92 -21
- package/src/chat/index.ts +2 -0
- package/src/chat/logic/picker-entries.ts +6 -4
- package/src/chat/logic/scrubber.ts +8 -6
- package/src/chat/pending-prompts.ts +108 -0
- package/src/chat/reading-width.ts +9 -2
- package/src/chat/ui/PromptComposer.tsx +10 -7
- package/src/chat/ui/SubagentTimeline.tsx +1 -24
- package/src/chat/ui/Timeline.tsx +66 -40
- package/src/chat/ui/code-lines.ts +37 -0
- package/src/chat/ui/composer/use-pending-requests.ts +13 -21
- package/src/chat/ui/highlighter.ts +4 -3
- package/src/locales/en/agent-chat.ts +1 -0
- package/src/locales/nl/agent-chat.ts +1 -0
- package/src/prompts/logic/subjects.ts +4 -1
- package/src/prompts/ui/PromptCard.tsx +1 -1
- package/src/prompts/ui/PromptView.tsx +7 -1
- package/src/state/chats.ts +63 -18
- package/src/theme.css +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { c as _c } from "react/compiler-runtime";
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Pill } from '@adecore/ui';
|
|
3
4
|
import { toolSummary } from '../../chat/logic/tools.js';
|
|
4
5
|
import { useChatHost } from '../../host.js';
|
|
5
6
|
import { answerValue, promptAnswers, questionAnswer } from '../logic/prompts.js';
|
|
@@ -11,7 +12,7 @@ import { QuestionBody } from './QuestionBody.js';
|
|
|
11
12
|
/* A permission request or question, answered in place: a chat's, or one of the app's own that it draws itself. */
|
|
12
13
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
14
|
export function PromptView(props) {
|
|
14
|
-
const $ = _c(
|
|
15
|
+
const $ = _c(99);
|
|
15
16
|
const {
|
|
16
17
|
subject,
|
|
17
18
|
draft,
|
|
@@ -143,9 +144,16 @@ export function PromptView(props) {
|
|
|
143
144
|
const commit = t4;
|
|
144
145
|
const multiple = item.kind === "question" && item.questions.length > 1;
|
|
145
146
|
let t5;
|
|
146
|
-
if ($[27] !== draft.index || $[28] !== hasDraft || $[29] !== item.questions || $[30] !== more || $[31] !== multiple || $[32] !== t) {
|
|
147
|
-
t5 = more > 0 || hasDraft || multiple ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
148
|
-
children: [
|
|
147
|
+
if ($[27] !== draft.index || $[28] !== hasDraft || $[29] !== item.questions || $[30] !== more || $[31] !== multiple || $[32] !== subject.source || $[33] !== t) {
|
|
148
|
+
t5 = subject.source !== undefined || more > 0 || hasDraft || multiple ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
149
|
+
children: [subject.source && /*#__PURE__*/_jsx(Pill, {
|
|
150
|
+
size: "sm",
|
|
151
|
+
className: "max-w-full",
|
|
152
|
+
children: /*#__PURE__*/_jsx("span", {
|
|
153
|
+
className: "truncate",
|
|
154
|
+
children: subject.source.label
|
|
155
|
+
})
|
|
156
|
+
}), more > 0 && /*#__PURE__*/_jsx("span", {
|
|
149
157
|
className: "tabular-nums",
|
|
150
158
|
children: t("meta.waiting", {
|
|
151
159
|
count: more + 1
|
|
@@ -165,63 +173,64 @@ export function PromptView(props) {
|
|
|
165
173
|
$[29] = item.questions;
|
|
166
174
|
$[30] = more;
|
|
167
175
|
$[31] = multiple;
|
|
168
|
-
$[32] =
|
|
169
|
-
$[33] =
|
|
176
|
+
$[32] = subject.source;
|
|
177
|
+
$[33] = t;
|
|
178
|
+
$[34] = t5;
|
|
170
179
|
} else {
|
|
171
|
-
t5 = $[
|
|
180
|
+
t5 = $[34];
|
|
172
181
|
}
|
|
173
182
|
const meta = t5;
|
|
174
183
|
if (item.kind === "approval") {
|
|
175
184
|
let t6;
|
|
176
|
-
if ($[
|
|
185
|
+
if ($[35] !== item.toolName || $[36] !== t) {
|
|
177
186
|
t6 = item.toolName === "Bash" ? t("approval.runCommand") : item.toolName;
|
|
178
|
-
$[
|
|
179
|
-
$[
|
|
180
|
-
$[
|
|
187
|
+
$[35] = item.toolName;
|
|
188
|
+
$[36] = t;
|
|
189
|
+
$[37] = t6;
|
|
181
190
|
} else {
|
|
182
|
-
t6 = $[
|
|
191
|
+
t6 = $[37];
|
|
183
192
|
}
|
|
184
193
|
let t7;
|
|
185
|
-
if ($[
|
|
194
|
+
if ($[38] !== item.input || $[39] !== item.toolName) {
|
|
186
195
|
t7 = item.toolName === "Bash" ? "" : toolSummary(item.toolName, item.input);
|
|
187
|
-
$[
|
|
188
|
-
$[
|
|
189
|
-
$[
|
|
196
|
+
$[38] = item.input;
|
|
197
|
+
$[39] = item.toolName;
|
|
198
|
+
$[40] = t7;
|
|
190
199
|
} else {
|
|
191
|
-
t7 = $[
|
|
200
|
+
t7 = $[40];
|
|
192
201
|
}
|
|
193
202
|
const t8 = `${t6} ${t7}`;
|
|
194
203
|
let t9;
|
|
195
|
-
if ($[
|
|
204
|
+
if ($[41] !== denyReason || $[42] !== draft || $[43] !== onDraft) {
|
|
196
205
|
t9 = denyReason && !draft.showReason ? () => onDraft({
|
|
197
206
|
...draft,
|
|
198
207
|
showReason: true
|
|
199
208
|
}) : undefined;
|
|
200
|
-
$[
|
|
201
|
-
$[
|
|
202
|
-
$[
|
|
203
|
-
$[
|
|
209
|
+
$[41] = denyReason;
|
|
210
|
+
$[42] = draft;
|
|
211
|
+
$[43] = onDraft;
|
|
212
|
+
$[44] = t9;
|
|
204
213
|
} else {
|
|
205
|
-
t9 = $[
|
|
214
|
+
t9 = $[44];
|
|
206
215
|
}
|
|
207
216
|
let t10;
|
|
208
|
-
if ($[
|
|
217
|
+
if ($[45] !== buttons || $[46] !== locked || $[47] !== sending || $[48] !== t9) {
|
|
209
218
|
t10 = /*#__PURE__*/_jsx(ApprovalActions, {
|
|
210
219
|
buttons: buttons,
|
|
211
220
|
onAddReason: t9,
|
|
212
221
|
locked: locked,
|
|
213
222
|
sending: sending
|
|
214
223
|
});
|
|
215
|
-
$[
|
|
216
|
-
$[
|
|
217
|
-
$[
|
|
218
|
-
$[
|
|
219
|
-
$[
|
|
224
|
+
$[45] = buttons;
|
|
225
|
+
$[46] = locked;
|
|
226
|
+
$[47] = sending;
|
|
227
|
+
$[48] = t9;
|
|
228
|
+
$[49] = t10;
|
|
220
229
|
} else {
|
|
221
|
-
t10 = $[
|
|
230
|
+
t10 = $[49];
|
|
222
231
|
}
|
|
223
232
|
let t11;
|
|
224
|
-
if ($[
|
|
233
|
+
if ($[50] !== denyReason || $[51] !== draft || $[52] !== item || $[53] !== locked || $[54] !== onDraft) {
|
|
225
234
|
t11 = /*#__PURE__*/_jsx(ApprovalBody, {
|
|
226
235
|
item: item,
|
|
227
236
|
draft: draft,
|
|
@@ -229,17 +238,17 @@ export function PromptView(props) {
|
|
|
229
238
|
denyReason: denyReason,
|
|
230
239
|
locked: locked
|
|
231
240
|
});
|
|
232
|
-
$[
|
|
233
|
-
$[
|
|
234
|
-
$[
|
|
235
|
-
$[
|
|
236
|
-
$[
|
|
237
|
-
$[
|
|
241
|
+
$[50] = denyReason;
|
|
242
|
+
$[51] = draft;
|
|
243
|
+
$[52] = item;
|
|
244
|
+
$[53] = locked;
|
|
245
|
+
$[54] = onDraft;
|
|
246
|
+
$[55] = t11;
|
|
238
247
|
} else {
|
|
239
|
-
t11 = $[
|
|
248
|
+
t11 = $[55];
|
|
240
249
|
}
|
|
241
250
|
let t12;
|
|
242
|
-
if ($[
|
|
251
|
+
if ($[56] !== error || $[57] !== meta || $[58] !== notice || $[59] !== sending || $[60] !== t10 || $[61] !== t11 || $[62] !== t8 || $[63] !== top) {
|
|
243
252
|
t12 = /*#__PURE__*/_jsx(PromptCard, {
|
|
244
253
|
kind: "approval",
|
|
245
254
|
heading: t8,
|
|
@@ -251,17 +260,17 @@ export function PromptView(props) {
|
|
|
251
260
|
actions: t10,
|
|
252
261
|
children: t11
|
|
253
262
|
});
|
|
254
|
-
$[
|
|
255
|
-
$[
|
|
256
|
-
$[
|
|
257
|
-
$[
|
|
258
|
-
$[
|
|
259
|
-
$[
|
|
260
|
-
$[
|
|
261
|
-
$[
|
|
262
|
-
$[
|
|
263
|
+
$[56] = error;
|
|
264
|
+
$[57] = meta;
|
|
265
|
+
$[58] = notice;
|
|
266
|
+
$[59] = sending;
|
|
267
|
+
$[60] = t10;
|
|
268
|
+
$[61] = t11;
|
|
269
|
+
$[62] = t8;
|
|
270
|
+
$[63] = top;
|
|
271
|
+
$[64] = t12;
|
|
263
272
|
} else {
|
|
264
|
-
t12 = $[
|
|
273
|
+
t12 = $[64];
|
|
265
274
|
}
|
|
266
275
|
return t12;
|
|
267
276
|
}
|
|
@@ -270,38 +279,38 @@ export function PromptView(props) {
|
|
|
270
279
|
const t8 = item.async === true;
|
|
271
280
|
const t9 = readyWith(draft);
|
|
272
281
|
let t10;
|
|
273
|
-
if ($[
|
|
282
|
+
if ($[65] !== draft || $[66] !== onDraft) {
|
|
274
283
|
t10 = () => onDraft({
|
|
275
284
|
...draft,
|
|
276
285
|
index: draft.index - 1
|
|
277
286
|
});
|
|
278
|
-
$[
|
|
279
|
-
$[
|
|
280
|
-
$[
|
|
287
|
+
$[65] = draft;
|
|
288
|
+
$[66] = onDraft;
|
|
289
|
+
$[67] = t10;
|
|
281
290
|
} else {
|
|
282
|
-
t10 = $[
|
|
291
|
+
t10 = $[67];
|
|
283
292
|
}
|
|
284
293
|
let t11;
|
|
285
|
-
if ($[
|
|
294
|
+
if ($[68] !== onAction) {
|
|
286
295
|
t11 = () => onAction({
|
|
287
296
|
kind: "dismiss"
|
|
288
297
|
});
|
|
289
|
-
$[
|
|
290
|
-
$[
|
|
298
|
+
$[68] = onAction;
|
|
299
|
+
$[69] = t11;
|
|
291
300
|
} else {
|
|
292
|
-
t11 = $[
|
|
301
|
+
t11 = $[69];
|
|
293
302
|
}
|
|
294
303
|
let t12;
|
|
295
|
-
if ($[
|
|
304
|
+
if ($[70] !== commit || $[71] !== draft) {
|
|
296
305
|
t12 = () => commit(draft);
|
|
297
|
-
$[
|
|
298
|
-
$[
|
|
299
|
-
$[
|
|
306
|
+
$[70] = commit;
|
|
307
|
+
$[71] = draft;
|
|
308
|
+
$[72] = t12;
|
|
300
309
|
} else {
|
|
301
|
-
t12 = $[
|
|
310
|
+
t12 = $[72];
|
|
302
311
|
}
|
|
303
312
|
let t13;
|
|
304
|
-
if ($[
|
|
313
|
+
if ($[73] !== draft.index || $[74] !== last || $[75] !== locked || $[76] !== sending || $[77] !== t10 || $[78] !== t11 || $[79] !== t12 || $[80] !== t8 || $[81] !== t9) {
|
|
305
314
|
t13 = /*#__PURE__*/_jsx(QuestionActions, {
|
|
306
315
|
index: t7,
|
|
307
316
|
dismissable: t8,
|
|
@@ -313,21 +322,21 @@ export function PromptView(props) {
|
|
|
313
322
|
onDismiss: t11,
|
|
314
323
|
onCommit: t12
|
|
315
324
|
});
|
|
316
|
-
$[
|
|
317
|
-
$[
|
|
318
|
-
$[
|
|
319
|
-
$[
|
|
320
|
-
$[
|
|
321
|
-
$[
|
|
322
|
-
$[
|
|
323
|
-
$[
|
|
324
|
-
$[
|
|
325
|
-
$[
|
|
325
|
+
$[73] = draft.index;
|
|
326
|
+
$[74] = last;
|
|
327
|
+
$[75] = locked;
|
|
328
|
+
$[76] = sending;
|
|
329
|
+
$[77] = t10;
|
|
330
|
+
$[78] = t11;
|
|
331
|
+
$[79] = t12;
|
|
332
|
+
$[80] = t8;
|
|
333
|
+
$[81] = t9;
|
|
334
|
+
$[82] = t13;
|
|
326
335
|
} else {
|
|
327
|
-
t13 = $[
|
|
336
|
+
t13 = $[82];
|
|
328
337
|
}
|
|
329
338
|
let t14;
|
|
330
|
-
if ($[
|
|
339
|
+
if ($[83] !== answer || $[84] !== commit || $[85] !== draft || $[86] !== locked || $[87] !== onDraft || $[88] !== question) {
|
|
331
340
|
t14 = question && answer && /*#__PURE__*/_jsx(QuestionBody, {
|
|
332
341
|
question: question,
|
|
333
342
|
answer: answer,
|
|
@@ -347,18 +356,18 @@ export function PromptView(props) {
|
|
|
347
356
|
}),
|
|
348
357
|
locked: locked
|
|
349
358
|
});
|
|
350
|
-
$[
|
|
351
|
-
$[
|
|
352
|
-
$[
|
|
353
|
-
$[
|
|
354
|
-
$[
|
|
355
|
-
$[
|
|
356
|
-
$[
|
|
359
|
+
$[83] = answer;
|
|
360
|
+
$[84] = commit;
|
|
361
|
+
$[85] = draft;
|
|
362
|
+
$[86] = locked;
|
|
363
|
+
$[87] = onDraft;
|
|
364
|
+
$[88] = question;
|
|
365
|
+
$[89] = t14;
|
|
357
366
|
} else {
|
|
358
|
-
t14 = $[
|
|
367
|
+
t14 = $[89];
|
|
359
368
|
}
|
|
360
369
|
let t15;
|
|
361
|
-
if ($[
|
|
370
|
+
if ($[90] !== error || $[91] !== meta || $[92] !== notice || $[93] !== sending || $[94] !== t13 || $[95] !== t14 || $[96] !== t6 || $[97] !== top) {
|
|
362
371
|
t15 = /*#__PURE__*/_jsx(PromptCard, {
|
|
363
372
|
kind: "question",
|
|
364
373
|
heading: t6,
|
|
@@ -370,17 +379,17 @@ export function PromptView(props) {
|
|
|
370
379
|
actions: t13,
|
|
371
380
|
children: t14
|
|
372
381
|
});
|
|
373
|
-
$[
|
|
374
|
-
$[
|
|
375
|
-
$[
|
|
376
|
-
$[
|
|
377
|
-
$[
|
|
378
|
-
$[
|
|
379
|
-
$[
|
|
380
|
-
$[
|
|
381
|
-
$[
|
|
382
|
+
$[90] = error;
|
|
383
|
+
$[91] = meta;
|
|
384
|
+
$[92] = notice;
|
|
385
|
+
$[93] = sending;
|
|
386
|
+
$[94] = t13;
|
|
387
|
+
$[95] = t14;
|
|
388
|
+
$[96] = t6;
|
|
389
|
+
$[97] = top;
|
|
390
|
+
$[98] = t15;
|
|
382
391
|
} else {
|
|
383
|
-
t15 = $[
|
|
392
|
+
t15 = $[98];
|
|
384
393
|
}
|
|
385
394
|
return t15;
|
|
386
395
|
}
|
package/dist/state/chats.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AgentKind, ChatApprovalItem, ChatBookmark, ChatEvent, ChatHistoryResult, ChatInfo, ChatItem, ChatQuestionItem, ChatSubagentItem, ChatVisual } from '@adecore/agent-contracts';
|
|
2
2
|
export interface ChatState {
|
|
3
3
|
info: ChatInfo;
|
|
4
|
+
historyPending?: boolean;
|
|
4
5
|
items: Record<string, ChatItem>;
|
|
5
6
|
structure: Record<string, ChatItem>;
|
|
6
7
|
order: string[];
|
|
@@ -19,9 +20,11 @@ export interface ChatPage {
|
|
|
19
20
|
export type ChatsById = Record<string, ChatState>;
|
|
20
21
|
export type ChatStatuses = Record<string, Pick<ChatState, 'info'>>;
|
|
21
22
|
export interface ChatSink {
|
|
23
|
+
historyIdentity?(chatId: string): object | undefined;
|
|
22
24
|
reset(chatId: string, info: ChatInfo, items: ChatItem[], page?: ChatPage): void;
|
|
23
25
|
prepend(chatId: string, cursor: string, page: ChatHistoryResult): void;
|
|
24
26
|
apply(chatId: string, event: ChatEvent): void;
|
|
27
|
+
replay?(chatId: string, events: readonly ChatEvent[]): void;
|
|
25
28
|
status(chatId: string, info: ChatInfo): void;
|
|
26
29
|
bookmarks(chatId: string, bookmarks: ChatBookmark[]): void;
|
|
27
30
|
visuals(chatId: string, visuals: ChatVisual[]): void;
|
|
@@ -33,6 +36,7 @@ interface ChatsStore {
|
|
|
33
36
|
reset(key: string, info: ChatInfo, items: ChatItem[], page?: ChatPage): void;
|
|
34
37
|
prepend(key: string, cursor: string, page: ChatHistoryResult): void;
|
|
35
38
|
apply(key: string, event: ChatEvent): void;
|
|
39
|
+
replay(key: string, events: readonly ChatEvent[]): void;
|
|
36
40
|
status(key: string, info: ChatInfo): void;
|
|
37
41
|
bookmarks(key: string, bookmarks: ChatBookmark[]): void;
|
|
38
42
|
visuals(key: string, visuals: ChatVisual[]): void;
|
package/dist/state/chats.js
CHANGED
|
@@ -37,11 +37,11 @@ function isBeforePage(state, historyIndex) {
|
|
|
37
37
|
return state.history !== undefined && historyIndex !== undefined && historyIndex < state.history.start;
|
|
38
38
|
}
|
|
39
39
|
/* An item from before the page this client holds matters only as a sub-agent, a request that waits, or one that stopped waiting. */
|
|
40
|
-
function withEarlierItem(state, item) {
|
|
40
|
+
function withEarlierItem(state, item, write) {
|
|
41
41
|
if (item.kind === 'subagent') {
|
|
42
42
|
const subagents = state.subagentsBefore ?? [];
|
|
43
43
|
return {
|
|
44
|
-
...
|
|
44
|
+
...write(state, item),
|
|
45
45
|
subagentsBefore: subagents.includes(item.id) ? subagents : [...subagents, item.id]
|
|
46
46
|
};
|
|
47
47
|
}
|
|
@@ -49,12 +49,12 @@ function withEarlierItem(state, item) {
|
|
|
49
49
|
const held = waiting.includes(item.id);
|
|
50
50
|
if (isWaiting(item)) {
|
|
51
51
|
return {
|
|
52
|
-
...
|
|
52
|
+
...write(state, item),
|
|
53
53
|
waitingBefore: held ? waiting : [...waiting, item.id]
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
return held ? {
|
|
57
|
-
...
|
|
57
|
+
...write(state, item),
|
|
58
58
|
waitingBefore: waiting.filter(id => id !== item.id)
|
|
59
59
|
} : state;
|
|
60
60
|
}
|
|
@@ -63,6 +63,10 @@ export function prependPage(state, cursor, page) {
|
|
|
63
63
|
if (state.history?.cursor !== cursor) {
|
|
64
64
|
return state;
|
|
65
65
|
}
|
|
66
|
+
const {
|
|
67
|
+
historyPending: _historyPending,
|
|
68
|
+
...loaded
|
|
69
|
+
} = state;
|
|
66
70
|
const held = new Set(state.order);
|
|
67
71
|
const older = page.items.filter(item => !held.has(item.id));
|
|
68
72
|
const olderIds = new Set(older.map(item => item.id));
|
|
@@ -80,7 +84,7 @@ export function prependPage(state, cursor, page) {
|
|
|
80
84
|
waitingBefore: _waitingBefore,
|
|
81
85
|
subagentsBefore: _subagentsBefore,
|
|
82
86
|
...rest
|
|
83
|
-
} =
|
|
87
|
+
} = loaded;
|
|
84
88
|
return {
|
|
85
89
|
...rest,
|
|
86
90
|
items,
|
|
@@ -91,7 +95,7 @@ export function prependPage(state, cursor, page) {
|
|
|
91
95
|
const waitingBefore = (state.waitingBefore ?? []).filter(id => !olderIds.has(id));
|
|
92
96
|
const subagentsBefore = (state.subagentsBefore ?? []).filter(id => !olderIds.has(id));
|
|
93
97
|
return {
|
|
94
|
-
...
|
|
98
|
+
...loaded,
|
|
95
99
|
items,
|
|
96
100
|
structure: items,
|
|
97
101
|
order,
|
|
@@ -104,16 +108,18 @@ export function prependPage(state, cursor, page) {
|
|
|
104
108
|
export function waitingRequestsOf(chat) {
|
|
105
109
|
return [...(chat?.waitingBefore ?? []), ...(chat?.order ?? [])].map(id => chat?.structure[id]).filter(isWaiting);
|
|
106
110
|
}
|
|
107
|
-
|
|
108
|
-
function withItem(state, item) {
|
|
111
|
+
function withItem(state, item, structural = true) {
|
|
109
112
|
const items = {
|
|
110
113
|
...state.items,
|
|
111
114
|
[item.id]: item
|
|
112
115
|
};
|
|
113
|
-
return {
|
|
116
|
+
return structural ? {
|
|
114
117
|
...state,
|
|
115
118
|
items,
|
|
116
119
|
structure: items
|
|
120
|
+
} : {
|
|
121
|
+
...state,
|
|
122
|
+
items
|
|
117
123
|
};
|
|
118
124
|
}
|
|
119
125
|
/* Cheap enough, since a status only arrives when the host saw one change, never on a streamed word. */
|
|
@@ -133,15 +139,48 @@ function without(rows, matches) {
|
|
|
133
139
|
return Object.fromEntries(Object.entries(rows).filter(([key]) => !matches(key)));
|
|
134
140
|
}
|
|
135
141
|
export function applyEvent(state, event) {
|
|
142
|
+
return applyUpdate(state, event, withItem);
|
|
143
|
+
}
|
|
144
|
+
function replayEvents(state, events) {
|
|
145
|
+
let draft;
|
|
146
|
+
const write = (current, item, structural = true) => {
|
|
147
|
+
// A text delta must leave the last structural snapshot intact, including within this replay.
|
|
148
|
+
if (draft !== current.items || !structural && draft === current.structure) {
|
|
149
|
+
draft = {
|
|
150
|
+
...current.items
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// Item ids may be __proto__, so this must define an own data property.
|
|
154
|
+
Object.defineProperty(draft, item.id, {
|
|
155
|
+
value: item,
|
|
156
|
+
enumerable: true,
|
|
157
|
+
configurable: true,
|
|
158
|
+
writable: true
|
|
159
|
+
});
|
|
160
|
+
return structural ? {
|
|
161
|
+
...current,
|
|
162
|
+
items: draft,
|
|
163
|
+
structure: draft
|
|
164
|
+
} : {
|
|
165
|
+
...current,
|
|
166
|
+
items: draft
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
for (const event of events) {
|
|
170
|
+
state = applyUpdate(state, event, write);
|
|
171
|
+
}
|
|
172
|
+
return state;
|
|
173
|
+
}
|
|
174
|
+
function applyUpdate(state, event, write) {
|
|
136
175
|
switch (event.type) {
|
|
137
176
|
case 'item':
|
|
138
177
|
{
|
|
139
178
|
if (isBeforePage(state, event.historyIndex)) {
|
|
140
|
-
return withEarlierItem(state, event.item);
|
|
179
|
+
return withEarlierItem(state, event.item, write);
|
|
141
180
|
}
|
|
142
181
|
const known = event.item.id in state.items;
|
|
143
182
|
return {
|
|
144
|
-
...
|
|
183
|
+
...write(state, event.item),
|
|
145
184
|
order: known ? state.order : [...state.order, event.item.id]
|
|
146
185
|
};
|
|
147
186
|
}
|
|
@@ -154,31 +193,19 @@ export function applyEvent(state, event) {
|
|
|
154
193
|
uiQueries,
|
|
155
194
|
...text
|
|
156
195
|
} = event;
|
|
157
|
-
const grown = event.text === '' ? state :
|
|
196
|
+
const grown = event.text === '' ? state : applyUpdate(state, text, write);
|
|
158
197
|
const reply = grown.items[event.itemId] ?? item;
|
|
159
|
-
return {
|
|
160
|
-
...
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
[event.itemId]: {
|
|
164
|
-
...reply,
|
|
165
|
-
uiQueries
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
};
|
|
198
|
+
return write(grown, {
|
|
199
|
+
...reply,
|
|
200
|
+
uiQueries
|
|
201
|
+
}, false);
|
|
169
202
|
}
|
|
170
203
|
if (event.ui !== undefined && event.text === '') {
|
|
171
204
|
if (item?.kind === 'assistant' && item.streaming && event.textLength === item.text.length) {
|
|
172
|
-
return {
|
|
173
|
-
...
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
[event.itemId]: {
|
|
177
|
-
...item,
|
|
178
|
-
ui: event.ui
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
};
|
|
205
|
+
return write(state, {
|
|
206
|
+
...item,
|
|
207
|
+
ui: event.ui
|
|
208
|
+
}, false);
|
|
182
209
|
}
|
|
183
210
|
return state;
|
|
184
211
|
}
|
|
@@ -189,15 +216,9 @@ export function applyEvent(state, event) {
|
|
|
189
216
|
};
|
|
190
217
|
// The first text is structure after all, since an empty reply that is not streaming has no row.
|
|
191
218
|
if (item.text === '') {
|
|
192
|
-
return
|
|
219
|
+
return write(state, grown);
|
|
193
220
|
}
|
|
194
|
-
return
|
|
195
|
-
...state,
|
|
196
|
-
items: {
|
|
197
|
-
...state.items,
|
|
198
|
-
[event.itemId]: grown
|
|
199
|
-
}
|
|
200
|
-
};
|
|
221
|
+
return write(state, grown, false);
|
|
201
222
|
}
|
|
202
223
|
if (item?.kind === 'tool' && item.state === 'running') {
|
|
203
224
|
const progress = item.progress ?? {
|
|
@@ -212,13 +233,7 @@ export function applyEvent(state, event) {
|
|
|
212
233
|
output: appendToolOutput(progress.output, event.text)
|
|
213
234
|
}
|
|
214
235
|
};
|
|
215
|
-
return
|
|
216
|
-
...state,
|
|
217
|
-
items: {
|
|
218
|
-
...state.items,
|
|
219
|
-
[event.itemId]: grown
|
|
220
|
-
}
|
|
221
|
-
};
|
|
236
|
+
return write(state, grown, false);
|
|
222
237
|
}
|
|
223
238
|
return state;
|
|
224
239
|
}
|
|
@@ -306,6 +321,22 @@ export const useChats = create(set => ({
|
|
|
306
321
|
};
|
|
307
322
|
});
|
|
308
323
|
},
|
|
324
|
+
replay(key, events) {
|
|
325
|
+
set(store => {
|
|
326
|
+
const current = store.byKey[key];
|
|
327
|
+
if (!current || events.length === 0) {
|
|
328
|
+
return store;
|
|
329
|
+
}
|
|
330
|
+
const next = replayEvents(current, events);
|
|
331
|
+
return next === current ? store : {
|
|
332
|
+
byKey: {
|
|
333
|
+
...store.byKey,
|
|
334
|
+
[key]: next
|
|
335
|
+
},
|
|
336
|
+
statusByKey: withStatus(store.statusByKey, key, next.info)
|
|
337
|
+
};
|
|
338
|
+
});
|
|
339
|
+
},
|
|
309
340
|
/*
|
|
310
341
|
* The status of a chat this window is not reading, which the host sends for every chat it has
|
|
311
342
|
* loaded. The thread stays empty until somebody attaches, since this is what a header, a list and
|
|
@@ -318,7 +349,10 @@ export const useChats = create(set => ({
|
|
|
318
349
|
return {
|
|
319
350
|
byKey: {
|
|
320
351
|
...s.byKey,
|
|
321
|
-
[key]:
|
|
352
|
+
[key]: {
|
|
353
|
+
...stateOf(info, []),
|
|
354
|
+
historyPending: true
|
|
355
|
+
}
|
|
322
356
|
},
|
|
323
357
|
statusByKey: withStatus(s.statusByKey, key, info)
|
|
324
358
|
};
|
|
@@ -375,9 +409,11 @@ export const useChats = create(set => ({
|
|
|
375
409
|
/* The sink of one host's chat client: it hands over chat ids, this puts them under the keys of its scope. */
|
|
376
410
|
export function chatSink(keyOf) {
|
|
377
411
|
return {
|
|
412
|
+
historyIdentity: chatId => useChats.getState().byKey[keyOf(chatId)]?.order,
|
|
378
413
|
reset: (chatId, info, items, page) => useChats.getState().reset(keyOf(chatId), info, items, page),
|
|
379
414
|
prepend: (chatId, cursor, page) => useChats.getState().prepend(keyOf(chatId), cursor, page),
|
|
380
415
|
apply: (chatId, event) => useChats.getState().apply(keyOf(chatId), event),
|
|
416
|
+
replay: (chatId, events) => useChats.getState().replay(keyOf(chatId), events),
|
|
381
417
|
status: (chatId, info) => useChats.getState().status(keyOf(chatId), info),
|
|
382
418
|
bookmarks: (chatId, bookmarks) => useChats.getState().bookmarks(keyOf(chatId), bookmarks),
|
|
383
419
|
visuals: (chatId, visuals) => useChats.getState().visuals(keyOf(chatId), visuals),
|
package/dist/theme.css
CHANGED
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
*/
|
|
111
111
|
.chat-column .chat-column-content { margin-inline: auto; max-width: var(--chat-measure, 768px); }
|
|
112
112
|
/* Wide pages clear the scrubber even when the reply column needs no extra padding. */
|
|
113
|
-
.chat-wide-content { max-width: calc(100% - 2 * var(--chat-wide-inset, 0px)); }
|
|
113
|
+
.chat-wide-content { max-width: min(calc(100% - 2 * var(--chat-wide-inset, 0px)), var(--chat-wide-measure, 100%)); }
|
|
114
114
|
/* Wider by the input's inset (`.composer-input .cm-content` padding plus the card's border), so what is typed lines up with the thread. */
|
|
115
115
|
.chat-column .chat-composer-content { margin-inline: auto; max-width: calc(var(--chat-measure, 768px) + 2 * 21px); }
|
|
116
116
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adecore/agents-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "React agent chat threads, composers, approvals, provider accounts and usage views.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -67,11 +67,11 @@
|
|
|
67
67
|
"test:visuals": "bun --config=integration.bunfig.toml --conditions=source test ./src/chat/visual-viewport.integration.test.ts"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@adecore/agent-contracts": "0.
|
|
71
|
-
"@adecore/editor": "0.
|
|
72
|
-
"@adecore/editor-react": "0.
|
|
73
|
-
"@adecore/intelligent-ui": "0.
|
|
74
|
-
"@adecore/merge": "0.
|
|
70
|
+
"@adecore/agent-contracts": "0.37.0",
|
|
71
|
+
"@adecore/editor": "0.37.0",
|
|
72
|
+
"@adecore/editor-react": "0.37.0",
|
|
73
|
+
"@adecore/intelligent-ui": "0.37.0",
|
|
74
|
+
"@adecore/merge": "0.37.0",
|
|
75
75
|
"@codemirror/commands": "^6.11.1",
|
|
76
76
|
"@codemirror/language": "^6.12.4",
|
|
77
77
|
"@codemirror/state": "^6.7.6",
|
|
@@ -94,14 +94,14 @@
|
|
|
94
94
|
"zustand": "^5.0.15"
|
|
95
95
|
},
|
|
96
96
|
"peerDependencies": {
|
|
97
|
-
"@adecore/ui": "^0.
|
|
97
|
+
"@adecore/ui": "^0.37.0",
|
|
98
98
|
"i18next": "^26.4.2",
|
|
99
99
|
"react": "^19.3.0",
|
|
100
100
|
"react-dom": "^19.3.0",
|
|
101
101
|
"react-i18next": "^17.0.15"
|
|
102
102
|
},
|
|
103
103
|
"devDependencies": {
|
|
104
|
-
"@adecore/ui": "0.
|
|
104
|
+
"@adecore/ui": "0.37.0",
|
|
105
105
|
"@types/bun": "^1.4.2",
|
|
106
106
|
"@types/react": "^19.3.0",
|
|
107
107
|
"@types/react-dom": "^19.3.0",
|