@adecore/agents 0.20.0 → 0.21.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 (42) hide show
  1. package/dist/chat/attachment-store.d.ts +2 -0
  2. package/dist/chat/attachment-store.js +38 -2
  3. package/dist/chat/backend.d.ts +13 -1
  4. package/dist/chat/chat-core.d.ts +53 -1
  5. package/dist/chat/chat-core.js +53 -10
  6. package/dist/chat/chat-session.d.ts +29 -2
  7. package/dist/chat/chat-session.js +258 -15
  8. package/dist/chat/claude-backend.d.ts +4 -0
  9. package/dist/chat/claude-backend.js +39 -0
  10. package/dist/chat/claude-protocol.d.ts +2 -0
  11. package/dist/chat/claude-protocol.js +12 -1
  12. package/dist/chat/codex-backend.d.ts +4 -0
  13. package/dist/chat/codex-backend.js +77 -5
  14. package/dist/chat/codex-protocol.d.ts +1 -0
  15. package/dist/chat/codex-protocol.js +74 -5
  16. package/dist/chat/delta-coalescer.js +1 -1
  17. package/dist/chat/errors.d.ts +1 -1
  18. package/dist/chat/fake-codex.js +33 -0
  19. package/dist/chat/generated-image.d.ts +6 -0
  20. package/dist/chat/generated-image.js +74 -0
  21. package/dist/chat/projector.js +9 -2
  22. package/dist/chat/thread.js +6 -0
  23. package/dist/chat/ui-queries.d.ts +38 -0
  24. package/dist/chat/ui-queries.js +311 -0
  25. package/dist/host/handlers.js +4 -1
  26. package/package.json +5 -4
  27. package/src/chat/attachment-store.ts +38 -2
  28. package/src/chat/backend.ts +16 -2
  29. package/src/chat/chat-core.ts +60 -9
  30. package/src/chat/chat-session.ts +294 -16
  31. package/src/chat/claude-backend.ts +40 -0
  32. package/src/chat/claude-protocol.ts +16 -1
  33. package/src/chat/codex-backend.ts +77 -5
  34. package/src/chat/codex-protocol.ts +78 -6
  35. package/src/chat/delta-coalescer.ts +1 -1
  36. package/src/chat/errors.ts +2 -0
  37. package/src/chat/fake-codex.ts +34 -0
  38. package/src/chat/generated-image.ts +88 -0
  39. package/src/chat/projector.ts +9 -2
  40. package/src/chat/thread.ts +6 -0
  41. package/src/chat/ui-queries.ts +360 -0
  42. package/src/host/handlers.ts +9 -1
@@ -4,6 +4,8 @@ export declare class AttachmentStore {
4
4
  readonly dir: string;
5
5
  constructor(home: string);
6
6
  save(chatId: string, upload: ChatAttachmentUpload): Promise<ChatAttachment>;
7
+ saveGenerated(chatId: string, ref: string, upload: ChatAttachmentUpload): Promise<ChatAttachment>;
8
+ copy(chatId: string, attachment: ChatAttachment): Promise<ChatAttachment>;
7
9
  removeAll(chatId: string): Promise<void>;
8
10
  folderOf(chatId: string): string;
9
11
  }
@@ -1,5 +1,5 @@
1
- import { randomBytes } from 'node:crypto';
2
- import { mkdir, rm, writeFile } from 'node:fs/promises';
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { link, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
3
3
  import { extname, join } from 'node:path';
4
4
  import { attachmentImageMime } from '@adecore/agent-contracts';
5
5
  // The extension the file gets on disk when its name does not carry a usable one.
@@ -50,6 +50,42 @@ export class AttachmentStore {
50
50
  await writeFile(path, bytes, { mode: 0o600 });
51
51
  return { id, name: upload.name, mime, size: bytes.byteLength, path };
52
52
  }
53
+ async saveGenerated(chatId, ref, upload) {
54
+ const id = createHash('sha256').update(`codex-image:${ref}`).digest('hex');
55
+ const bytes = Buffer.from(upload.data, 'base64');
56
+ const folder = this.folderOf(chatId);
57
+ await mkdir(folder, { recursive: true, mode: 0o700 });
58
+ const path = join(folder, `${id}.${extensionFor('', upload.mime)}`);
59
+ const temporary = join(folder, `.${id}-${randomBytes(8).toString('hex')}.tmp`);
60
+ try {
61
+ await writeFile(temporary, bytes, { mode: 0o600, flag: 'wx' });
62
+ try {
63
+ // Publish complete bytes without replacing an image another replay has already saved.
64
+ await link(temporary, path);
65
+ }
66
+ catch (error) {
67
+ if (error.code !== 'EEXIST') {
68
+ throw error;
69
+ }
70
+ const previous = await readFile(path);
71
+ if (!previous.equals(bytes)) {
72
+ throw new Error('The generated image id already belongs to another image');
73
+ }
74
+ }
75
+ }
76
+ finally {
77
+ await rm(temporary, { force: true });
78
+ }
79
+ return { id, name: upload.name, mime: upload.mime, size: bytes.byteLength, path };
80
+ }
81
+ async copy(chatId, attachment) {
82
+ const bytes = await readFile(attachment.path);
83
+ const folder = this.folderOf(chatId);
84
+ await mkdir(folder, { recursive: true, mode: 0o700 });
85
+ const path = join(folder, `${createHash('sha256').update(attachment.id).digest('hex')}.${extensionFor(attachment.name, attachment.mime)}`);
86
+ await writeFile(path, bytes, { mode: 0o600 });
87
+ return { ...attachment, path };
88
+ }
53
89
  /* Everything a chat attached; used when the chat itself is deleted. */
54
90
  async removeAll(chatId) {
55
91
  await rm(this.folderOf(chatId), { recursive: true, force: true });
@@ -1,4 +1,4 @@
1
- import type { ChatAttachment, ChatFileChange, ChatQuestion, ChatSkill, ChatSubagentUsage, ChatTurnLimit, ChatWorkflow, ModelSelection, RuntimeMode } from '@adecore/agent-contracts';
1
+ import type { ChatAttachment, ChatAttachmentUpload, ChatFileChange, ChatQuestion, ChatSkill, ChatSubagentUsage, ChatTurnLimit, ChatWorkflow, ModelSelection, RuntimeMode } from '@adecore/agent-contracts';
2
2
  import type { LimitsUpdate } from '../usage/limits/normalize.ts';
3
3
  import type { SpawnChatProcess } from './chat-process.ts';
4
4
  export interface BackendLaunch {
@@ -14,6 +14,7 @@ export interface BackendLaunch {
14
14
  resumeNote: string | null;
15
15
  folders?: readonly string[];
16
16
  spawn?: SpawnChatProcess;
17
+ saveGeneratedImage?(ref: string, upload: ChatAttachmentUpload): Promise<ChatAttachment>;
17
18
  }
18
19
  export interface TurnInput {
19
20
  promptId?: string;
@@ -47,11 +48,21 @@ export type BackendEvent = {
47
48
  type: 'text.delta';
48
49
  ref: string;
49
50
  text: string;
51
+ phase?: string;
50
52
  } | {
51
53
  type: 'text.done';
52
54
  ref: string;
53
55
  text: string;
54
56
  parentRef?: string | null;
57
+ phase?: string;
58
+ } | {
59
+ type: 'image.generated';
60
+ ref: string;
61
+ upload: ChatAttachmentUpload;
62
+ width?: number;
63
+ height?: number;
64
+ revisedPrompt?: string;
65
+ transparentBackground?: boolean;
55
66
  } | {
56
67
  type: 'thinking.delta';
57
68
  ref: string;
@@ -194,6 +205,7 @@ export interface ChatBackend {
194
205
  readonly pid: number | null;
195
206
  start(): Promise<void>;
196
207
  sendTurn(input: TurnInput): void;
208
+ steerTurn?(input: TurnInput): Promise<boolean>;
197
209
  compact(): void;
198
210
  interrupt(): void;
199
211
  respondApproval(requestId: string, decision: ApprovalDecision, message?: string): boolean;
@@ -1,4 +1,5 @@
1
- import { type AgentKind, type ChatAttachment, type ChatAttachResult, type ChatAttachmentUpload, type ChatBookmark, type ChatCheckpointDiff, type ChatConfigurePayload, type ChatCreatePayload, type ChatEvent, type ChatHistoryResult, type ChatInfo, type ChatItem, type ChatQueuedMessage, type ChatSkill, type ChatSubagentPayload, type ChatSubagentResult, type ChatTurnItem, type ChatVisual, type ModelSelection, type RuntimeMode } from '@adecore/agent-contracts';
1
+ import { type AgentKind, type ChatAttachment, type ChatAttachResult, type ChatAttachmentUpload, type ChatBookmark, type ChatCheckpointDiff, type ChatConfigurePayload, type ChatCreatePayload, type ChatEvent, type ChatHistoryResult, type ChatInfo, type ChatItem, type ChatQueuedMessage, type ChatUiChoicePayload, type ChatUiQueryPayload, type ChatUiLinkPayload, type ChatSkill, type ChatSubagentPayload, type ChatSubagentResult, type ChatTurnItem, type ChatVisual, type ModelSelection, type RuntimeMode } from '@adecore/agent-contracts';
2
+ import { type ChatUiHost } from './ui-queries.ts';
2
3
  import { ClientSinks } from '../client-sinks.ts';
3
4
  import type { AgentEvent, AgentSink } from '../events.ts';
4
5
  import type { OutboxOutcome } from '../outbox/outbox-worker.ts';
@@ -25,6 +26,7 @@ export interface InterruptedRun {
25
26
  attempt: number;
26
27
  }
27
28
  export interface ChatCoreOptions {
29
+ intelligentUi?: ChatUiHost;
28
30
  providers: ProviderRegistry;
29
31
  store?: ChatStore;
30
32
  checkpoints?: TurnCheckpoints;
@@ -95,8 +97,21 @@ export declare class ChatCore {
95
97
  mime: string;
96
98
  size: number;
97
99
  path: string;
100
+ width?: number | undefined;
101
+ height?: number | undefined;
98
102
  }[] | undefined;
99
103
  createdAt: number;
104
+ uiChoice?: {
105
+ itemId: string;
106
+ blockId: string;
107
+ revision: string;
108
+ choiceId: string;
109
+ label: string;
110
+ sourceAt: number;
111
+ older: boolean;
112
+ values?: Record<string, unknown> | undefined;
113
+ at: number;
114
+ } | undefined;
100
115
  }[] | undefined;
101
116
  queuePaused?: boolean | undefined;
102
117
  background?: {
@@ -174,6 +189,7 @@ export declare class ChatCore {
174
189
  protected readonly subagents: SubagentReader;
175
190
  readonly claudeProjectsDir: string;
176
191
  readonly composerPreferences: ComposerPreferences;
192
+ private readonly uiQueries;
177
193
  private readonly checkpoints;
178
194
  private readonly instructions;
179
195
  private readonly skillIndex;
@@ -251,6 +267,42 @@ export declare class ChatCore {
251
267
  queued: boolean;
252
268
  turnId: string;
253
269
  }>;
270
+ linkUi(payload: ChatUiLinkPayload, clientId: string): Promise<{
271
+ state: "chip" | "plain";
272
+ target?: {
273
+ type: "File";
274
+ path: string;
275
+ line?: number | undefined;
276
+ } | {
277
+ type: "Diff";
278
+ path: string;
279
+ } | {
280
+ type: "Commit";
281
+ sha: string;
282
+ } | {
283
+ type: "Node";
284
+ id: string;
285
+ } | undefined;
286
+ cwd?: string | undefined;
287
+ viewId?: string | undefined;
288
+ projectId?: string | undefined;
289
+ staged?: boolean | undefined;
290
+ conflicted?: boolean | undefined;
291
+ relativePath?: string | undefined;
292
+ label?: string | undefined;
293
+ reason?: string | undefined;
294
+ }>;
295
+ queryUi(payload: ChatUiQueryPayload, clientId: string): Promise<{
296
+ state: "failed" | "fresh" | "refused";
297
+ value?: unknown;
298
+ readId?: string | undefined;
299
+ readAt: number;
300
+ reason?: string | undefined;
301
+ }>;
302
+ choose(payload: ChatUiChoicePayload): Promise<{
303
+ queued: boolean;
304
+ turnId: string;
305
+ }>;
254
306
  attachment(chatId: string, id: string): ChatAttachment | null;
255
307
  unqueue(chatId: string, messageId: string): ChatQueuedMessage;
256
308
  sendNow(chatId: string, messageId: string): void;
@@ -1,6 +1,7 @@
1
1
  import { homedir } from 'node:os';
2
2
  import { join } from 'node:path';
3
- import { ChatAttachmentUploadsSchema } from '@adecore/agent-contracts';
3
+ import { ChatAttachmentUploadsSchema, generatedImageAttachment } from '@adecore/agent-contracts';
4
+ import { ChatUiQueries } from './ui-queries.js';
4
5
  import { ClientSinks } from '../client-sinks.js';
5
6
  import { errorText } from '../error-text.js';
6
7
  import { AccountError, definedEnv, isDefaultAccountOf, launchEnv, storedAccount } from '../providers/accounts/launch.js';
@@ -59,6 +60,7 @@ export class ChatCore {
59
60
  /* Where Claude Code keeps the projects of its default account on this machine; empty when it has none. */
60
61
  claudeProjectsDir;
61
62
  composerPreferences = new ComposerPreferences();
63
+ uiQueries;
62
64
  checkpoints;
63
65
  instructions;
64
66
  skillIndex;
@@ -84,6 +86,7 @@ export class ChatCore {
84
86
  onInterruptedRun;
85
87
  limitResume;
86
88
  constructor(options) {
89
+ this.uiQueries = options.intelligentUi ? new ChatUiQueries(options.intelligentUi) : null;
87
90
  this.providers = options.providers;
88
91
  this.store = options.store ?? null;
89
92
  this.bookmarks = options.bookmarks ?? null;
@@ -269,6 +272,7 @@ export class ChatCore {
269
272
  const references = this.referencesFor(payload.chatId);
270
273
  const session = new ChatSession({
271
274
  info,
275
+ ...(this.uiQueries ? { ui: { schemas: this.uiQueries.schemas, observe: (item, final) => this.uiQueries.observe(session, item, final) } } : {}),
272
276
  items: stored?.items ?? [],
273
277
  preambles: stored?.preambles ?? [],
274
278
  provider,
@@ -279,6 +283,7 @@ export class ChatCore {
279
283
  this.accounts?.launched?.(kind, chatAccount);
280
284
  return env;
281
285
  },
286
+ saveGeneratedImage: (ref, upload) => this.attachments.saveGenerated(payload.chatId, ref, upload),
282
287
  instructions: () => this.instructionsFor(payload.chatId),
283
288
  resumeNote: () => this.resumeNoteFor(payload.chatId),
284
289
  folders: () => this.foldersFor(payload.chatId),
@@ -362,11 +367,16 @@ export class ChatCore {
362
367
  const log = this.logs.get(chatId);
363
368
  const events = since === undefined ? null : log.after(since);
364
369
  if (events !== null) {
365
- return { info: session.info, items: [], events, seq: log.seq };
370
+ return { info: session.info, items: [], events: [...events, ...session.uiPreviews], seq: log.seq };
366
371
  }
367
- return historyLimit === undefined
368
- ? { ...session.thread.snapshot(), seq: log.seq }
369
- : { info: session.info, ...session.thread.history(historyLimit), pending: session.thread.pending(), seq: log.seq };
372
+ const page = historyLimit === undefined ? session.thread.snapshot() : session.thread.history(historyLimit);
373
+ return {
374
+ ...page,
375
+ info: session.info,
376
+ items: session.previewItems(page.items),
377
+ ...(historyLimit === undefined ? {} : { pending: session.previewItems(session.thread.pending()) }),
378
+ seq: log.seq
379
+ };
370
380
  }
371
381
  /*
372
382
  * `attach` with the chat's bookmarks and visuals. They are read before the client joins, so a
@@ -546,7 +556,29 @@ export class ChatCore {
546
556
  throw new ChatError('invalid-attachments', checked.error.issues[0].message);
547
557
  }
548
558
  const attachments = await Promise.all(uploads.map((upload) => this.attachments.save(chatId, upload)));
549
- return session.send(text, { ...extras, ...(attachments.length > 0 ? { attachments } : {}) });
559
+ return session.sendInput(text, { ...extras, ...(attachments.length > 0 ? { attachments } : {}) });
560
+ }
561
+ async linkUi(payload, clientId) {
562
+ if (!this.attached.get(payload.chatId)?.has(clientId) || !this.uiQueries) {
563
+ throw new ChatError('refused-query', 'Attach to the chat before resolving its UI links.');
564
+ }
565
+ return this.uiQueries.link(this.require(payload.chatId), payload);
566
+ }
567
+ async queryUi(payload, clientId) {
568
+ if (!this.attached.get(payload.chatId)?.has(clientId)) {
569
+ throw new ChatError('refused-query', 'Attach to the chat before reading its UI queries.');
570
+ }
571
+ if (!this.uiQueries) {
572
+ throw new ChatError('refused-query', 'This host does not provide UI query sources.');
573
+ }
574
+ return this.uiQueries.query(this.require(payload.chatId), payload);
575
+ }
576
+ async choose(payload) {
577
+ const session = this.require(payload.chatId);
578
+ const queries = await this.uiQueries?.choiceValues(session, payload);
579
+ const result = session.choose(payload, queries);
580
+ await this.persisted(payload.chatId);
581
+ return result;
550
582
  }
551
583
  /* The file behind an attachment id: what this chat's thread or queue says it is, or a visual's page. */
552
584
  attachment(chatId, id) {
@@ -555,7 +587,8 @@ export class ChatCore {
555
587
  return null;
556
588
  }
557
589
  for (const item of session.thread.list()) {
558
- const found = item.kind === 'user' ? item.attachments?.find((attachment) => attachment.id === id) : undefined;
590
+ const generated = generatedImageAttachment(item);
591
+ const found = item.kind === 'user' ? item.attachments?.find((attachment) => attachment.id === id) : generated?.id === id ? generated : null;
559
592
  if (found) {
560
593
  return found;
561
594
  }
@@ -599,7 +632,7 @@ export class ChatCore {
599
632
  throw new ChatError('chat-busy', `Chat ${chatId} is still working on the previous message`);
600
633
  }
601
634
  await this.clearing(chatId);
602
- session.clear(force);
635
+ const ended = session.clear(force);
603
636
  // Before the record is written, so what it takes along from the record is gone from the write as well.
604
637
  const cleared = this.cleared(chatId);
605
638
  // A debounced write still waiting holds the old thread and must not land after the empty one.
@@ -607,7 +640,7 @@ export class ChatCore {
607
640
  // Folded right away: every line before the reset describes a thread that is gone.
608
641
  await Promise.all([
609
642
  this.persistNow(chatId, true),
610
- this.attachments.removeAll(chatId),
643
+ ended.then(() => this.attachments.removeAll(chatId)),
611
644
  this.bookmarks?.removeChat(chatId),
612
645
  this.visuals?.removeChat(chatId),
613
646
  cleared
@@ -646,8 +679,9 @@ export class ChatCore {
646
679
  const session = this.require(chatId);
647
680
  // A write already out would put the record back after the delete.
648
681
  const writing = this.writes.get(chatId);
649
- void session.dispose();
682
+ const ended = session.dispose();
650
683
  this.subagents.releaseChat(chatId);
684
+ this.uiQueries?.forget(chatId);
651
685
  this.coalescers.get(chatId)?.dispose();
652
686
  this.coalescers.delete(chatId);
653
687
  this.logs.get(chatId)?.close();
@@ -663,6 +697,7 @@ export class ChatCore {
663
697
  const snapshot = session.thread.snapshot();
664
698
  this.forgotten(chatId);
665
699
  await writing;
700
+ await ended;
666
701
  await Promise.all([
667
702
  this.store?.delete(chatId),
668
703
  this.attachments.removeAll(chatId),
@@ -955,6 +990,14 @@ export class ChatCore {
955
990
  return next;
956
991
  }
957
992
  emit(chatId, event) {
993
+ if (event.type === 'delta' && event.ui !== undefined) {
994
+ // Previews follow all pending text but have no log position or observer side effects.
995
+ this.coalescers.get(chatId)?.flush();
996
+ for (const clientId of this.attached.get(chatId) ?? []) {
997
+ this.sinks.to(clientId, { event: 'chat.event', payload: { chatId, event } });
998
+ }
999
+ return;
1000
+ }
958
1001
  this.activity.set(chatId, Date.now());
959
1002
  let coalescer = this.coalescers.get(chatId);
960
1003
  if (!coalescer) {
@@ -1,6 +1,8 @@
1
- import type { ChatAttachment, ChatCheckpointDiff, ChatEvent, ChatInfo, ChatItem, ChatQueuedMessage, ChatSkill, ModelSelection, RuntimeMode } from '@adecore/agent-contracts';
1
+ import type { ChatAttachment, ChatAssistantItem, ChatCheckpointDiff, ChatEvent, ChatInfo, ChatItem, ChatQueuedMessage, ChatSkill, ChatUiChoiceOrigin, ChatUiChoicePayload, ChatUiQueryState, ModelSelection, RuntimeMode } from '@adecore/agent-contracts';
2
+ import type { UiCompileOptions } from '@adecore/intelligent-ui/compiler';
2
3
  import type { ChatProvider } from '../providers/provider.ts';
3
4
  import type { LimitsUpdate } from '../usage/limits/normalize.ts';
5
+ import type { BackendLaunch } from './backend.ts';
4
6
  import type { SpawnChatProcess } from './chat-process.ts';
5
7
  import type { ChatTitleInput } from './chat-title.ts';
6
8
  import type { SubagentSettlement } from './subagent-settlement.ts';
@@ -34,12 +36,17 @@ export interface ChatSessionOptions {
34
36
  command: string[];
35
37
  env(account: string | undefined): Record<string, string>;
36
38
  spawn?: SpawnChatProcess;
39
+ saveGeneratedImage?: BackendLaunch['saveGeneratedImage'];
37
40
  instructions?(): string | null;
38
41
  resumeNote?(): string | null;
39
42
  folders?(): readonly string[];
40
43
  promptNotes?: PromptNotes;
41
44
  references?: ChatReferences;
42
45
  checkpoints?: TurnCheckpoints;
46
+ ui?: {
47
+ schemas: UiCompileOptions['querySchemas'];
48
+ observe(item: ChatAssistantItem, final: boolean): void;
49
+ };
43
50
  emit(event: ChatEvent): void;
44
51
  onLimits?(update: LimitsUpdate): void;
45
52
  persist(): void;
@@ -58,6 +65,8 @@ export interface LimitResumeHooks {
58
65
  owed(): boolean;
59
66
  }
60
67
  export interface ChatSendExtras {
68
+ uiChoice?: ChatUiChoiceOrigin;
69
+ delivery?: 'steer' | 'queue';
61
70
  mentions?: string[];
62
71
  skills?: string[];
63
72
  chats?: string[];
@@ -93,7 +102,12 @@ export declare class ChatSession {
93
102
  private readonly orphans;
94
103
  private pendingPreambles;
95
104
  private submission;
105
+ private readonly steering;
106
+ private readonly uiStreams;
107
+ private readonly uiCompiled;
96
108
  constructor(options: ChatSessionOptions);
109
+ get uiPreviews(): ChatEvent[];
110
+ previewItems(items: ChatItem[]): ChatItem[];
97
111
  get id(): string;
98
112
  get info(): ChatInfo;
99
113
  get running(): boolean;
@@ -110,6 +124,17 @@ export declare class ChatSession {
110
124
  queued: boolean;
111
125
  turnId: string;
112
126
  };
127
+ updateUiQueries(itemId: string, uiQueries: ChatUiQueryState): void;
128
+ choose(payload: ChatUiChoicePayload, queries?: Readonly<Record<string, unknown>>): {
129
+ queued: boolean;
130
+ turnId: string;
131
+ };
132
+ private recordUiAnswer;
133
+ sendInput(text: string, extras?: ChatSendExtras): Promise<{
134
+ queued: boolean;
135
+ turnId: string;
136
+ }>;
137
+ private submitInput;
113
138
  unqueue(messageId: string): ChatQueuedMessage | null;
114
139
  sendNow(messageId: string): boolean;
115
140
  private get queue();
@@ -122,7 +147,7 @@ export declare class ChatSession {
122
147
  approve(requestId: string, decision: 'allow' | 'allow-always' | 'deny', message?: string): boolean;
123
148
  answer(requestId: string, answers: Record<string, string>): boolean;
124
149
  dismiss(itemId: string): boolean;
125
- clear(force: boolean): void;
150
+ clear(force: boolean): Promise<void>;
126
151
  settleStored(selection: ModelSelection, resume: ResumeDecision): void;
127
152
  resume(turnId: string, attempt: number, words: ResumeWords): Promise<void>;
128
153
  abandon(turnId: string, reason: string | null): void;
@@ -201,6 +226,8 @@ export declare class ChatSession {
201
226
  private settleFromTranscripts;
202
227
  private settleRows;
203
228
  private emit;
229
+ private latestUiAttachment;
230
+ private clearUi;
204
231
  private delegatingChange;
205
232
  }
206
233
  export interface ResumeDecision {