@adecore/agents-react 0.35.5 → 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/Pickers.js +6 -3
- package/dist/chat/ui/PromptComposer.d.ts +2 -2
- package/dist/chat/ui/PromptComposer.js +92 -98
- package/dist/chat/ui/RunSettings.js +2 -2
- 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/ComposerControls.js +1 -1
- 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/chat/ui/rows/MessageRows.js +1 -1
- 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/Pickers.tsx +3 -6
- package/src/chat/ui/PromptComposer.tsx +10 -7
- package/src/chat/ui/RunSettings.tsx +2 -2
- 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/ComposerControls.tsx +1 -1
- package/src/chat/ui/composer/use-pending-requests.ts +13 -21
- package/src/chat/ui/highlighter.ts +4 -3
- package/src/chat/ui/rows/MessageRows.tsx +1 -1
- 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
|
@@ -27,10 +27,13 @@ export declare class ChatClient {
|
|
|
27
27
|
private readonly mounted;
|
|
28
28
|
private readonly unsubscribe;
|
|
29
29
|
private readonly earlier;
|
|
30
|
+
private readonly observers;
|
|
30
31
|
private readonly inspections;
|
|
31
32
|
private preferences;
|
|
33
|
+
private checkpoints;
|
|
32
34
|
constructor(transport: ChatTransport, sink: ChatSink, providers?: ProviderSink | null);
|
|
33
35
|
inspect(chatId: string): Promise<ChatAttachResult>;
|
|
36
|
+
observe(chatId: string): () => void;
|
|
34
37
|
open(chatId: string, options: ChatOpenOptions): Promise<boolean>;
|
|
35
38
|
retarget(chatId: string, provider: AgentKind, selection: ModelSelection): Promise<void>;
|
|
36
39
|
setPreferences(preferences: ChatPreferencesPayload): void;
|
|
@@ -53,6 +56,8 @@ export declare class ChatClient {
|
|
|
53
56
|
isMounted(chatId: string): boolean;
|
|
54
57
|
dispose(): void;
|
|
55
58
|
private attach;
|
|
59
|
+
private sequence;
|
|
60
|
+
private rememberSequence;
|
|
56
61
|
private sendPreferences;
|
|
57
62
|
private onStatus;
|
|
58
63
|
}
|
package/dist/chat/chat-client.js
CHANGED
|
@@ -15,8 +15,10 @@ export class ChatClient {
|
|
|
15
15
|
mounted = new MountedRegistry();
|
|
16
16
|
unsubscribe = [];
|
|
17
17
|
earlier = new Map();
|
|
18
|
+
observers = new Map();
|
|
18
19
|
inspections = new Map();
|
|
19
20
|
preferences = null;
|
|
21
|
+
checkpoints = new WeakMap();
|
|
20
22
|
constructor(transport, sink, providers = null) {
|
|
21
23
|
this.transport = transport;
|
|
22
24
|
this.sink = sink;
|
|
@@ -26,10 +28,10 @@ export class ChatClient {
|
|
|
26
28
|
event,
|
|
27
29
|
seq
|
|
28
30
|
}) => {
|
|
31
|
+
const previous = this.sequence(chatId);
|
|
29
32
|
this.sink.apply(chatId, event);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
entry.seq = seq;
|
|
33
|
+
if (previous !== undefined || !this.sink.historyIdentity && this.mounted.get(chatId)?.attached) {
|
|
34
|
+
this.rememberSequence(chatId, seq);
|
|
33
35
|
}
|
|
34
36
|
}),
|
|
35
37
|
// Every chat on the machine, attached or not, so a node waiting on a person says so on a view nobody has open.
|
|
@@ -68,6 +70,33 @@ export class ChatClient {
|
|
|
68
70
|
this.inspections.set(chatId, read);
|
|
69
71
|
return read;
|
|
70
72
|
}
|
|
73
|
+
/* Shares a live attachment without creating a chat; a visible composer and several ancestors may hold it together. */
|
|
74
|
+
observe(chatId) {
|
|
75
|
+
this.observers.set(chatId, (this.observers.get(chatId) ?? 0) + 1);
|
|
76
|
+
if (!this.mounted.has(chatId)) {
|
|
77
|
+
this.mounted.set(chatId, {
|
|
78
|
+
attached: false,
|
|
79
|
+
observerOnly: true
|
|
80
|
+
});
|
|
81
|
+
void this.attach(chatId).catch(() => undefined);
|
|
82
|
+
}
|
|
83
|
+
let released = false;
|
|
84
|
+
return () => {
|
|
85
|
+
if (released) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
released = true;
|
|
89
|
+
const remaining = (this.observers.get(chatId) ?? 1) - 1;
|
|
90
|
+
if (remaining > 0) {
|
|
91
|
+
this.observers.set(chatId, remaining);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this.observers.delete(chatId);
|
|
95
|
+
if (this.mounted.get(chatId)?.observerOnly) {
|
|
96
|
+
void this.detach(chatId);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
71
100
|
/* Answers false when the transport is not connected; the chat opens once it is. */
|
|
72
101
|
async open(chatId, options) {
|
|
73
102
|
this.mounted.set(chatId, {
|
|
@@ -100,8 +129,10 @@ export class ChatClient {
|
|
|
100
129
|
attached: _attached,
|
|
101
130
|
account: _account,
|
|
102
131
|
seq: _seq,
|
|
132
|
+
observerOnly: _observerOnly,
|
|
103
133
|
...options
|
|
104
134
|
} = entry;
|
|
135
|
+
this.rememberSequence(chatId, undefined);
|
|
105
136
|
this.mounted.delete(chatId);
|
|
106
137
|
await this.transport.request('chat.kill', {
|
|
107
138
|
chatId
|
|
@@ -127,6 +158,10 @@ export class ChatClient {
|
|
|
127
158
|
}
|
|
128
159
|
async detach(chatId) {
|
|
129
160
|
const entry = this.mounted.get(chatId);
|
|
161
|
+
if (entry && this.observers.has(chatId)) {
|
|
162
|
+
entry.observerOnly = true;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
130
165
|
this.mounted.delete(chatId);
|
|
131
166
|
if (!entry?.attached) {
|
|
132
167
|
return;
|
|
@@ -271,12 +306,16 @@ export class ChatClient {
|
|
|
271
306
|
chatId,
|
|
272
307
|
cursor,
|
|
273
308
|
limit: HISTORY_PAGE
|
|
274
|
-
}).then(page =>
|
|
309
|
+
}).then(page => {
|
|
310
|
+
const seq = this.sequence(chatId);
|
|
311
|
+
this.sink.prepend(chatId, cursor, page);
|
|
312
|
+
this.rememberSequence(chatId, seq);
|
|
313
|
+
}).catch(async e => {
|
|
275
314
|
const entry = this.mounted.get(chatId);
|
|
276
315
|
if (errorCode(e) !== 'history-expired' || !entry) {
|
|
277
316
|
throw e;
|
|
278
317
|
}
|
|
279
|
-
|
|
318
|
+
this.rememberSequence(chatId, undefined);
|
|
280
319
|
await this.attach(chatId);
|
|
281
320
|
}).finally(() => this.earlier.delete(chatId));
|
|
282
321
|
this.earlier.set(chatId, read);
|
|
@@ -291,6 +330,8 @@ export class ChatClient {
|
|
|
291
330
|
off();
|
|
292
331
|
}
|
|
293
332
|
this.unsubscribe.length = 0;
|
|
333
|
+
this.observers.clear();
|
|
334
|
+
this.checkpoints = new WeakMap();
|
|
294
335
|
for (const chatId of [...this.mounted.keys()]) {
|
|
295
336
|
void this.detach(chatId);
|
|
296
337
|
}
|
|
@@ -305,20 +346,22 @@ export class ChatClient {
|
|
|
305
346
|
if (!entry) {
|
|
306
347
|
return;
|
|
307
348
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
349
|
+
if (!entry.observerOnly) {
|
|
350
|
+
await this.transport.request('chat.create', {
|
|
351
|
+
chatId,
|
|
352
|
+
provider: entry.provider,
|
|
353
|
+
account: entry.account,
|
|
354
|
+
cwd: entry.cwd,
|
|
355
|
+
resume: entry.resume,
|
|
356
|
+
selection: entry.selection,
|
|
357
|
+
runtimeMode: entry.runtimeMode
|
|
358
|
+
});
|
|
359
|
+
}
|
|
317
360
|
// Closed or opened again in the meantime: an opening that is still there attaches itself.
|
|
318
361
|
if (this.mounted.get(chatId) !== entry) {
|
|
319
362
|
return;
|
|
320
363
|
}
|
|
321
|
-
const since =
|
|
364
|
+
const since = this.sequence(chatId);
|
|
322
365
|
const result = await this.transport.request('chat.attach', {
|
|
323
366
|
chatId,
|
|
324
367
|
historyLimit: HISTORY_PAGE,
|
|
@@ -336,13 +379,19 @@ export class ChatClient {
|
|
|
336
379
|
}
|
|
337
380
|
return;
|
|
338
381
|
}
|
|
339
|
-
|
|
340
|
-
if (result.
|
|
341
|
-
|
|
382
|
+
// A cache eviction while the request was in flight also invalidates its replay base.
|
|
383
|
+
if (result.events && since !== undefined && this.sequence(chatId) === undefined) {
|
|
384
|
+
await this.attach(chatId);
|
|
385
|
+
return;
|
|
342
386
|
}
|
|
387
|
+
entry.attached = true;
|
|
343
388
|
if (result.events && since !== undefined) {
|
|
344
|
-
|
|
345
|
-
this.sink.
|
|
389
|
+
if (this.sink.replay) {
|
|
390
|
+
this.sink.replay(chatId, result.events);
|
|
391
|
+
} else {
|
|
392
|
+
for (const event of result.events) {
|
|
393
|
+
this.sink.apply(chatId, event);
|
|
394
|
+
}
|
|
346
395
|
}
|
|
347
396
|
} else {
|
|
348
397
|
const page = result.history === undefined ? undefined : {
|
|
@@ -352,10 +401,32 @@ export class ChatClient {
|
|
|
352
401
|
};
|
|
353
402
|
this.sink.reset(chatId, result.info, result.items, page);
|
|
354
403
|
}
|
|
404
|
+
this.rememberSequence(chatId, result.seq);
|
|
355
405
|
// A host without bookmarks or visuals sends none, and then this chat has none.
|
|
356
406
|
this.sink.bookmarks(chatId, result.bookmarks ?? []);
|
|
357
407
|
this.sink.visuals(chatId, result.visuals ?? []);
|
|
358
408
|
}
|
|
409
|
+
sequence(chatId) {
|
|
410
|
+
if (!this.sink.historyIdentity) {
|
|
411
|
+
return this.mounted.get(chatId)?.seq;
|
|
412
|
+
}
|
|
413
|
+
const identity = this.sink.historyIdentity(chatId);
|
|
414
|
+
return identity === undefined ? undefined : this.checkpoints.get(identity);
|
|
415
|
+
}
|
|
416
|
+
rememberSequence(chatId, seq) {
|
|
417
|
+
const entry = this.mounted.get(chatId);
|
|
418
|
+
if (entry) {
|
|
419
|
+
entry.seq = seq;
|
|
420
|
+
}
|
|
421
|
+
const identity = this.sink.historyIdentity?.(chatId);
|
|
422
|
+
if (identity !== undefined) {
|
|
423
|
+
if (seq === undefined) {
|
|
424
|
+
this.checkpoints.delete(identity);
|
|
425
|
+
} else {
|
|
426
|
+
this.checkpoints.set(identity, seq);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
359
430
|
sendPreferences() {
|
|
360
431
|
if (this.preferences === null) {
|
|
361
432
|
return;
|
package/dist/chat/index.d.ts
CHANGED
|
@@ -29,3 +29,4 @@ export { useSubagentSupport } from './subagent-support.js';
|
|
|
29
29
|
export { crumbOf, openFromMain, composerWrites, canOpenSubagent, useSubagentView, useSubagentTrail, useSubagentItem, type SubagentStep, type SubagentTrail } from './subagent-view.js';
|
|
30
30
|
export { stepTimelineMessage, jumpToTimelineItem, type TurnTarget, registerTurnJumper, jumpToTimelineTurn, type ItemJumpOptions } from './timeline-scroll.js';
|
|
31
31
|
export { type StatusWord, statusLookOf, taskStatusWord, type StatusLook, type TaskState } from '../agents/status-look.js';
|
|
32
|
+
export { ChatPromptFeed } from './pending-prompts.js';
|
package/dist/chat/index.js
CHANGED
|
@@ -24,4 +24,5 @@ export { subagentTitle, summaryWordOf, stopOf } from './subagent-list.js';
|
|
|
24
24
|
export { useSubagentSupport } from './subagent-support.js';
|
|
25
25
|
export { crumbOf, openFromMain, composerWrites, canOpenSubagent, useSubagentView, useSubagentTrail, useSubagentItem } from './subagent-view.js';
|
|
26
26
|
export { stepTimelineMessage, jumpToTimelineItem, registerTurnJumper, jumpToTimelineTurn } from './timeline-scroll.js';
|
|
27
|
-
export { statusLookOf, taskStatusWord } from '../agents/status-look.js';
|
|
27
|
+
export { statusLookOf, taskStatusWord } from '../agents/status-look.js';
|
|
28
|
+
export { ChatPromptFeed } from './pending-prompts.js';
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
import { nameCollator } from '@adecore/ui/format';
|
|
1
2
|
/*
|
|
2
3
|
* A provider with more live models than this keeps the list short: the model that is picked and the default,
|
|
3
4
|
* with the rest behind one row that opens all of them. A list of three hundred models is not one to scroll.
|
|
4
5
|
*/
|
|
5
6
|
export const COMPACT_MODEL_LIMIT = 8;
|
|
6
7
|
const NO_FAVORITES = () => new Set();
|
|
8
|
+
const VENDOR_ORDER = {
|
|
9
|
+
sensitivity: 'base'
|
|
10
|
+
};
|
|
11
|
+
const MODEL_ORDER = {
|
|
12
|
+
sensitivity: 'base',
|
|
13
|
+
numeric: true
|
|
14
|
+
};
|
|
7
15
|
function matches(model, query) {
|
|
8
16
|
return model.name.toLowerCase().includes(query) || model.slug.toLowerCase().includes(query) || (model.vendor?.toLowerCase().includes(query) ?? false);
|
|
9
17
|
}
|
|
@@ -15,16 +23,13 @@ export function modelsByVendor(models) {
|
|
|
15
23
|
if (!models.some(model => model.vendor !== undefined)) {
|
|
16
24
|
return models;
|
|
17
25
|
}
|
|
26
|
+
const vendorOrder = nameCollator(VENDOR_ORDER).compare;
|
|
27
|
+
const modelOrder = nameCollator(MODEL_ORDER).compare;
|
|
18
28
|
return [...models].sort((a, b) => {
|
|
19
29
|
if (a.vendor === undefined || b.vendor === undefined) {
|
|
20
30
|
return a.vendor === b.vendor ? 0 : a.vendor === undefined ? 1 : -1;
|
|
21
31
|
}
|
|
22
|
-
return a.vendor
|
|
23
|
-
sensitivity: 'base'
|
|
24
|
-
}) || a.name.localeCompare(b.name, undefined, {
|
|
25
|
-
sensitivity: 'base',
|
|
26
|
-
numeric: true
|
|
27
|
-
});
|
|
32
|
+
return vendorOrder(a.vendor, b.vendor) || modelOrder(a.name, b.name);
|
|
28
33
|
});
|
|
29
34
|
}
|
|
30
35
|
/* The starred models first, in the order they came, then the rest by maker. */
|
|
@@ -36,8 +36,9 @@ export declare function ticksWithHits(ticks: readonly ScrubberTick[], hitRows: r
|
|
|
36
36
|
export declare function messageAt(layout: ScrubberLayout, y: number): number | null;
|
|
37
37
|
export declare function slotOf(layout: ScrubberLayout, message: number): number | null;
|
|
38
38
|
export declare function tickWidth(kind: TickKind, distanceFromPointer: number | null): number;
|
|
39
|
+
type MessageStarts = Pick<readonly number[], 'length' | 'at'>;
|
|
39
40
|
export interface ReadingPosition {
|
|
40
|
-
starts:
|
|
41
|
+
starts: MessageStarts;
|
|
41
42
|
scrollTop: number;
|
|
42
43
|
visibleHeight: number;
|
|
43
44
|
}
|
|
@@ -47,5 +48,6 @@ export interface MessageRange {
|
|
|
47
48
|
}
|
|
48
49
|
export declare function messagesInView({ starts, scrollTop, visibleHeight }: ReadingPosition): MessageRange | null;
|
|
49
50
|
export declare function slotInView(slot: TickSlot, range: MessageRange | null): boolean;
|
|
50
|
-
export declare function stepMessage(starts:
|
|
51
|
+
export declare function stepMessage(starts: MessageStarts, eligible: (index: number) => boolean, scrollTop: number, direction: -1 | 1): number | null;
|
|
51
52
|
export declare function threadPaddingLeft(width: number, strip: boolean, measure?: number): number;
|
|
53
|
+
export {};
|
|
@@ -197,7 +197,7 @@ function lastIndexAtOrBelow(length, valueAt, limit) {
|
|
|
197
197
|
}
|
|
198
198
|
/* The last index whose start lies at or above a line, or -1. */
|
|
199
199
|
function lastAtOrAbove(starts, line) {
|
|
200
|
-
return lastIndexAtOrBelow(starts.length, index => starts
|
|
200
|
+
return lastIndexAtOrBelow(starts.length, index => starts.at(index), line);
|
|
201
201
|
}
|
|
202
202
|
/*
|
|
203
203
|
* The messages on screen. A message counts from its own start to the start of the next one, so the
|
|
@@ -209,13 +209,13 @@ export function messagesInView({
|
|
|
209
209
|
visibleHeight
|
|
210
210
|
}) {
|
|
211
211
|
const bottom = scrollTop + visibleHeight;
|
|
212
|
-
if (starts.length === 0 || visibleHeight <= 0 || starts
|
|
212
|
+
if (starts.length === 0 || visibleHeight <= 0 || starts.at(0) >= bottom) {
|
|
213
213
|
return null;
|
|
214
214
|
}
|
|
215
215
|
const first = Math.max(0, lastAtOrAbove(starts, scrollTop));
|
|
216
216
|
let last = lastAtOrAbove(starts, bottom);
|
|
217
217
|
// A row that starts on the very last line has not a pixel on screen yet.
|
|
218
|
-
if (starts
|
|
218
|
+
if (starts.at(last) >= bottom) {
|
|
219
219
|
last--;
|
|
220
220
|
}
|
|
221
221
|
return {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PromptSubject } from '../prompts/logic/subjects.js';
|
|
2
|
+
import type { ChatScope } from '../scope.js';
|
|
3
|
+
export declare class ChatPromptFeed {
|
|
4
|
+
private readonly scope;
|
|
5
|
+
private readonly chatId;
|
|
6
|
+
private readonly changed;
|
|
7
|
+
private readonly children;
|
|
8
|
+
private unsubscribe;
|
|
9
|
+
private current;
|
|
10
|
+
private disposed;
|
|
11
|
+
constructor(scope: ChatScope, chatId: string, changed: (subjects: PromptSubject[]) => void);
|
|
12
|
+
start(): void;
|
|
13
|
+
dispose(): void;
|
|
14
|
+
private update;
|
|
15
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { CHAT_REQUEST_LIMITS } from '@adecore/agent-contracts';
|
|
2
|
+
import { useChats, waitingRequestsOf } from '../state/chats.js';
|
|
3
|
+
import { subagentTitle } from './subagent-list.js';
|
|
4
|
+
function stillWaiting(item, info) {
|
|
5
|
+
return info.status === 'needs-you' && (info.requests === undefined || info.requests.length >= CHAT_REQUEST_LIMITS.perChat || info.requests.some(request => request.requestId === item.requestId));
|
|
6
|
+
}
|
|
7
|
+
/* Follows descendant requests while the main chat is open, including children discovered in later events or older history pages. */
|
|
8
|
+
export class ChatPromptFeed {
|
|
9
|
+
scope;
|
|
10
|
+
chatId;
|
|
11
|
+
changed;
|
|
12
|
+
children = new Map();
|
|
13
|
+
unsubscribe;
|
|
14
|
+
current = null;
|
|
15
|
+
disposed = false;
|
|
16
|
+
constructor(scope, chatId, changed) {
|
|
17
|
+
this.scope = scope;
|
|
18
|
+
this.chatId = chatId;
|
|
19
|
+
this.changed = changed;
|
|
20
|
+
}
|
|
21
|
+
start() {
|
|
22
|
+
this.unsubscribe = useChats.subscribe(() => this.update());
|
|
23
|
+
this.update();
|
|
24
|
+
}
|
|
25
|
+
dispose() {
|
|
26
|
+
this.disposed = true;
|
|
27
|
+
this.unsubscribe?.();
|
|
28
|
+
for (const release of this.children.values()) {
|
|
29
|
+
release();
|
|
30
|
+
}
|
|
31
|
+
this.children.clear();
|
|
32
|
+
}
|
|
33
|
+
update() {
|
|
34
|
+
if (this.disposed) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const store = useChats.getState();
|
|
38
|
+
const subjects = new Map();
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
const visit = (chatId, source) => {
|
|
41
|
+
if (seen.has(chatId)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
seen.add(chatId);
|
|
45
|
+
const key = this.scope.keyOf(chatId);
|
|
46
|
+
const row = store.byKey[key];
|
|
47
|
+
const info = store.statusByKey[key]?.info ?? row?.info;
|
|
48
|
+
for (const item of waitingRequestsOf(row)) {
|
|
49
|
+
if (info !== undefined && (chatId === this.chatId || stillWaiting(item, info))) {
|
|
50
|
+
subjects.set(`${chatId}:${item.requestId}`, {
|
|
51
|
+
kind: 'chat',
|
|
52
|
+
nodeId: chatId,
|
|
53
|
+
item,
|
|
54
|
+
...(source === undefined ? {} : {
|
|
55
|
+
source
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for (const item of Object.values(row?.structure ?? {})) {
|
|
61
|
+
if (item.kind !== 'subagent' || item.childId === undefined || seen.has(item.childId)) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const childInfo = store.statusByKey[this.scope.keyOf(item.childId)]?.info;
|
|
65
|
+
if (item.status !== 'running' && childInfo?.status !== 'needs-you') {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (!this.children.has(item.childId)) {
|
|
69
|
+
this.children.set(item.childId, this.scope.chats.observe(item.childId));
|
|
70
|
+
}
|
|
71
|
+
visit(item.childId, {
|
|
72
|
+
label: subagentTitle(item),
|
|
73
|
+
provider: childInfo?.provider
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
visit(this.chatId);
|
|
78
|
+
for (const [chatId, release] of this.children) {
|
|
79
|
+
if (!seen.has(chatId)) {
|
|
80
|
+
this.children.delete(chatId);
|
|
81
|
+
release();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const next = [...subjects.values()];
|
|
85
|
+
if (this.current === null || next.length !== this.current.length || next.some((subject, index) => {
|
|
86
|
+
const previous = this.current?.[index];
|
|
87
|
+
return subject.nodeId !== previous?.nodeId || subject.item !== previous.item || subject.source?.label !== previous.source?.label || subject.source?.provider !== previous.source?.provider;
|
|
88
|
+
})) {
|
|
89
|
+
this.current = next;
|
|
90
|
+
this.changed(next);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -14,11 +14,15 @@ export function isChatReadingWidth(value) {
|
|
|
14
14
|
export function readingMeasure(width) {
|
|
15
15
|
return MEASURES[width];
|
|
16
16
|
}
|
|
17
|
-
/*
|
|
17
|
+
/* A wide visual outgrows the reading column, but only a full-width chat lets it run past this. */
|
|
18
|
+
const WIDE_MEASURE = 1200;
|
|
19
|
+
/* What `.chat-column-content`, `.chat-wide-content` and `.chat-composer-content` read their width from. */
|
|
18
20
|
export function readingWidthStyle(width) {
|
|
19
21
|
const measure = MEASURES[width];
|
|
22
|
+
const bounded = Number.isFinite(measure);
|
|
20
23
|
return {
|
|
21
|
-
'--chat-measure':
|
|
24
|
+
'--chat-measure': bounded ? `${measure}px` : '100%',
|
|
25
|
+
'--chat-wide-measure': bounded ? `${WIDE_MEASURE}px` : '100%'
|
|
22
26
|
};
|
|
23
27
|
}
|
|
24
28
|
export function readingWidthLabel(width) {
|
package/dist/chat/ui/Pickers.js
CHANGED
|
@@ -13,11 +13,10 @@ import { ModelMarks } from './ModelMarks.js';
|
|
|
13
13
|
import { pickerEntriesOf } from '../logic/picker-entries.js';
|
|
14
14
|
import { favoriteModelsOf, toggleFavoriteModel, useChatPreferences } from '../preferences.js';
|
|
15
15
|
import { forgetStashed, STASH_SHORTCUT, useStash } from '../stash.js';
|
|
16
|
-
import { Icon, IconButton, Menu, Popover, Tooltip } from '@adecore/ui';
|
|
16
|
+
import { Button, Icon, IconButton, Menu, Popover, Tooltip } from '@adecore/ui';
|
|
17
17
|
import { useHostMachineLabel } from '../../host-hooks.js';
|
|
18
18
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
19
|
const triggerClass = 'flex h-7 shrink-0 items-center gap-1 whitespace-nowrap rounded-md px-2 text-xs text-text-muted hover:bg-surface-hover hover:text-text data-[popup-open]:bg-surface-active data-[popup-open]:text-text';
|
|
20
|
-
const chipClass = 'flex h-6 min-w-0 shrink items-center gap-1.5 whitespace-nowrap rounded-md bg-surface-hover px-2 text-xs text-text hover:bg-surface-active data-[popup-open]:bg-surface-active';
|
|
21
20
|
/*
|
|
22
21
|
* Which model answers: a search field over one group per provider, with the legacy models behind an
|
|
23
22
|
* expander. Before the first message the list carries every installed CLI, so picking another
|
|
@@ -251,7 +250,11 @@ export function ModelPicker(t0) {
|
|
|
251
250
|
let t18;
|
|
252
251
|
if ($[37] !== chip || $[38] !== current || $[39] !== provider || $[40] !== trigger) {
|
|
253
252
|
t18 = trigger === "chip" ? /*#__PURE__*/_jsxs(Popover.Trigger, {
|
|
254
|
-
|
|
253
|
+
render: /*#__PURE__*/_jsx(Button, {
|
|
254
|
+
variant: "secondary",
|
|
255
|
+
size: "sm",
|
|
256
|
+
className: "min-w-0 shrink gap-1.5"
|
|
257
|
+
}),
|
|
255
258
|
children: [chip.mark === "logo" ? /*#__PURE__*/_jsx(ProviderLogo, {
|
|
256
259
|
provider: provider,
|
|
257
260
|
size: 12
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { HoldRelease } from '../prompt-hold.js';
|
|
3
|
-
import { type
|
|
3
|
+
import { type PromptSubject } from '../../prompts/logic/subjects.js';
|
|
4
4
|
export interface PromptHold {
|
|
5
5
|
held: boolean;
|
|
6
6
|
releasedBy: HoldRelease | null;
|
|
@@ -8,7 +8,7 @@ export interface PromptHold {
|
|
|
8
8
|
}
|
|
9
9
|
export declare function PromptComposer({ chatId, pending, focused, onShowElsewhere, disabled, hasDraft, denyReason, onAllAnswered, hold, children }: {
|
|
10
10
|
chatId: string;
|
|
11
|
-
pending:
|
|
11
|
+
pending: PromptSubject[];
|
|
12
12
|
focused: boolean;
|
|
13
13
|
onShowElsewhere?: () => void;
|
|
14
14
|
disabled: boolean;
|