@mono-agent/web 0.14.0 → 0.15.1

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/store.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AgentStreamWireFrame } from "@mono-agent/agent-contracts";
1
+ import { type AgentStreamWireFrame } from "@mono-agent/agent-contracts";
2
2
  import { type WebAgentSummary, type WebAttachment, type WebMessage, type WebNotificationTriggerKind, type WebQuote, type WebThread, type WebThreadDetail } from "./contracts.js";
3
3
  import { type WebStatePathOptions, type WebStatePaths } from "./state-paths.js";
4
4
  export interface StoredAttachment {
@@ -42,6 +42,20 @@ export interface BeginStoredTurnResult {
42
42
  readonly attachments: readonly StoredAttachment[];
43
43
  readonly thread: WebThread;
44
44
  }
45
+ export interface StoredLiveInput {
46
+ readonly id: string;
47
+ readonly threadId: string;
48
+ readonly messageId: string;
49
+ readonly text: string;
50
+ readonly status: "offered" | "queued";
51
+ readonly createdAt: string;
52
+ }
53
+ export interface ReserveStoredLiveInputResult {
54
+ readonly input: StoredLiveInput;
55
+ readonly message: WebMessage;
56
+ readonly thread: WebThread;
57
+ readonly offered: boolean;
58
+ }
45
59
  export interface ReserveWebNotificationInput {
46
60
  readonly sourceId: string;
47
61
  readonly deliveryKey: string;
@@ -82,6 +96,9 @@ export declare class WebStore {
82
96
  readonly title?: string;
83
97
  readonly archived?: boolean;
84
98
  }): WebThread;
99
+ deleteArchivedThread(id: string): Promise<{
100
+ readonly orphanedFiles: number;
101
+ }>;
85
102
  createUpload(input: CreateStoredUploadInput): StoredAttachment;
86
103
  markUploadComplete(id: string, sizeBytes: number): StoredAttachment;
87
104
  getStoredAttachment(id: string): StoredAttachment | undefined;
@@ -93,7 +110,15 @@ export declare class WebStore {
93
110
  removeStagedAttachment(id: string): Promise<void>;
94
111
  purgeStagedAttachments(before: string): Promise<number>;
95
112
  purgePartialUploadFiles(before?: string): Promise<number>;
113
+ purgeUnreferencedAttachmentFiles(): Promise<number>;
96
114
  beginTurn(input: BeginStoredTurnInput): BeginStoredTurnResult;
115
+ reserveLiveInput(threadId: string, text: string): ReserveStoredLiveInputResult;
116
+ markLiveInputApplied(id: string): WebMessage | undefined;
117
+ queueLiveInput(id: string): WebMessage | undefined;
118
+ cancelLiveInput(id: string): WebMessage | undefined;
119
+ cancelLiveInputs(threadId: string): WebMessage[];
120
+ queuedLiveInputThreadIds(): string[];
121
+ promoteNextQueuedLiveInput(threadId: string): BeginStoredTurnResult | undefined;
97
122
  applyStreamFrame(turnId: string, frame: AgentStreamWireFrame): WebMessage;
98
123
  applyStreamFrames(turnId: string, frames: readonly AgentStreamWireFrame[]): WebMessage;
99
124
  completeTurn(turnId: string, finalText?: string, metadata?: Readonly<Record<string, unknown>>): WebThreadDetail;
@@ -112,6 +137,7 @@ export declare class WebStore {
112
137
  private initialize;
113
138
  private validateStorage;
114
139
  private recoverInterruptedTurns;
140
+ private recoverLiveInputs;
115
141
  private finishTurn;
116
142
  private mapThread;
117
143
  private mapMessage;
@@ -121,6 +147,8 @@ export declare class WebStore {
121
147
  private requireThreadDetail;
122
148
  private requireTurn;
123
149
  private requireMessage;
150
+ private getLiveInput;
151
+ private requireLiveInput;
124
152
  private requireStoredAttachment;
125
153
  private recordThreadRevision;
126
154
  private setSetting;
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
package/dist/store.js CHANGED
@@ -2,7 +2,8 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import { chmod, lstat, readdir, unlink } from "node:fs/promises";
3
3
  import { resolve } from "node:path";
4
4
  import { DatabaseSync } from "node:sqlite";
5
- import { WEB_MAX_FILES_PER_TURN, WEB_MAX_TURN_ATTACHMENT_BYTES, } from "./contracts.js";
5
+ import { AGENT_LIVE_INPUT_MAX_CHARACTERS, } from "@mono-agent/agent-contracts";
6
+ import { WEB_MAX_FILES_PER_TURN, WEB_MAX_LIVE_INPUTS_PER_THREAD, WEB_MAX_TURN_ATTACHMENT_BYTES, WEB_MAX_TURN_TEXT_CHARACTERS, } from "./contracts.js";
6
7
  import { WebConsoleError } from "./errors.js";
7
8
  import { prepareWebStatePaths } from "./state-paths.js";
8
9
  export class WebStore {
@@ -38,6 +39,7 @@ export class WebStore {
38
39
  chmod(`${paths.database}-shm`, 0o600).catch(ignoreMissing),
39
40
  ]);
40
41
  store.recoverInterruptedTurns();
42
+ store.recoverLiveInputs();
41
43
  return store;
42
44
  }
43
45
  catch (error) {
@@ -213,7 +215,17 @@ export class WebStore {
213
215
  const thread = this.getThread(id);
214
216
  if (thread === undefined)
215
217
  return undefined;
216
- const rows = this.database.prepare("SELECT * FROM messages WHERE thread_id = ? ORDER BY created_at, rowid").all(id);
218
+ const rows = this.database.prepare(`
219
+ SELECT m.* FROM messages m
220
+ LEFT JOIN turns t ON t.id = m.turn_id
221
+ WHERE m.thread_id = ?
222
+ ORDER BY COALESCE(t.started_at, m.created_at),
223
+ CASE WHEN m.turn_id IS NOT NULL AND m.role = 'user' THEN 0
224
+ WHEN m.turn_id IS NOT NULL AND m.role = 'system' THEN 1
225
+ WHEN m.turn_id IS NOT NULL THEN 2
226
+ ELSE 3 END,
227
+ m.created_at, m.rowid
228
+ `).all(id);
217
229
  return { thread, messages: rows.map((row) => this.mapMessage(row)) };
218
230
  }
219
231
  currentThreadId() {
@@ -251,6 +263,28 @@ export class WebStore {
251
263
  });
252
264
  return { ...this.requireThread(id), sourceId: current.sourceId };
253
265
  }
266
+ async deleteArchivedThread(id) {
267
+ const thread = this.requireThread(id);
268
+ if (thread.archivedAt === null) {
269
+ throw new WebConsoleError("thread_not_archived", "Archive the conversation before deleting it.", 409);
270
+ }
271
+ const attachments = this.database.prepare("SELECT * FROM attachments WHERE thread_id = ?")
272
+ .all(id);
273
+ this.transaction(() => {
274
+ this.database.prepare("DELETE FROM notification_deliveries WHERE thread_id = ?").run(id);
275
+ this.database.prepare("DELETE FROM revisions WHERE entity_kind = 'thread' AND entity_id = ?").run(id);
276
+ this.database.prepare("DELETE FROM threads WHERE id = ?").run(id);
277
+ this.database.prepare("DELETE FROM settings WHERE key = 'current_thread_id' AND value = ?").run(id);
278
+ });
279
+ let orphanedFiles = 0;
280
+ for (const row of attachments) {
281
+ await unlink(this.attachmentPath(mapStoredAttachment(row))).catch((error) => {
282
+ if (error.code !== "ENOENT")
283
+ orphanedFiles += 1;
284
+ });
285
+ }
286
+ return { orphanedFiles };
287
+ }
254
288
  createUpload(input) {
255
289
  const id = randomUUID();
256
290
  const now = this.now();
@@ -335,6 +369,23 @@ export class WebStore {
335
369
  }
336
370
  return removed;
337
371
  }
372
+ async purgeUnreferencedAttachmentFiles() {
373
+ const referenced = new Set(this.database.prepare("SELECT storage_name FROM attachments").all()
374
+ .map((row) => row.storage_name));
375
+ const entries = await readdir(this.paths.uploads, { withFileTypes: true });
376
+ let removed = 0;
377
+ for (const entry of entries) {
378
+ if (!/^[0-9a-f-]{36}\.bin$/iu.test(entry.name) || referenced.has(entry.name))
379
+ continue;
380
+ const path = resolve(this.paths.uploads, entry.name);
381
+ const info = await lstat(path);
382
+ if (!info.isFile() || info.isSymbolicLink())
383
+ continue;
384
+ await unlink(path);
385
+ removed += 1;
386
+ }
387
+ return removed;
388
+ }
338
389
  beginTurn(input) {
339
390
  const thread = this.requireThread(input.threadId);
340
391
  if (thread.archivedAt !== null) {
@@ -430,6 +481,181 @@ export class WebStore {
430
481
  thread: this.requireThread(input.threadId),
431
482
  };
432
483
  }
484
+ reserveLiveInput(threadId, text) {
485
+ const thread = this.requireThread(threadId);
486
+ if (thread.archivedAt !== null) {
487
+ throw new WebConsoleError("thread_archived", "Unarchive this conversation before sending another message.", 409);
488
+ }
489
+ if (!thread.canSend) {
490
+ throw new WebConsoleError("agent_offline", "This agent is offline. The conversation remains available read-only.", 409);
491
+ }
492
+ if (text.trim().length === 0) {
493
+ throw new WebConsoleError("empty_turn", "Enter a message.", 400);
494
+ }
495
+ if (text.length > AGENT_LIVE_INPUT_MAX_CHARACTERS) {
496
+ throw new WebConsoleError("turn_text_too_large", `A live follow-up may contain at most ${AGENT_LIVE_INPUT_MAX_CHARACTERS} characters.`, 413);
497
+ }
498
+ const usage = this.database.prepare("SELECT COUNT(*) AS count FROM live_inputs WHERE thread_id = ?").get(threadId);
499
+ if (usage.count >= WEB_MAX_LIVE_INPUTS_PER_THREAD) {
500
+ throw new WebConsoleError("live_input_queue_full", "Too many follow-up messages are waiting.", 429);
501
+ }
502
+ const active = this.database.prepare("SELECT id, model, effort FROM turns WHERE thread_id = ? AND status = 'running'").get(threadId);
503
+ const id = randomUUID();
504
+ const messageId = randomUUID();
505
+ const now = this.now();
506
+ const status = active === undefined ? "queued" : "offered";
507
+ const parts = [
508
+ liveInputTelemetry(status === "offered" ? "pending" : "queued"),
509
+ { type: "text", text },
510
+ ];
511
+ this.transaction(() => {
512
+ this.database.prepare(`
513
+ INSERT INTO messages (id, thread_id, turn_id, role, parts_json, created_at, updated_at, status)
514
+ VALUES (?, ?, ?, 'user', ?, ?, ?, 'complete')
515
+ `).run(messageId, threadId, active?.id ?? null, JSON.stringify(parts), now, now);
516
+ this.database.prepare(`
517
+ INSERT INTO live_inputs (
518
+ id, thread_id, message_id, active_turn_id, text, model, effort, status, created_at, updated_at
519
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
520
+ `).run(id, threadId, messageId, active?.id ?? null, text, active?.model ?? null, active?.effort ?? null, status, now, now);
521
+ const title = deriveAutomaticTitle(text, []);
522
+ this.database.prepare(`
523
+ UPDATE threads
524
+ SET title = CASE WHEN title_manual = 0 AND title = 'New conversation' THEN ? ELSE title END,
525
+ updated_at = ?, revision = revision + 1
526
+ WHERE id = ?
527
+ `).run(title, now, threadId);
528
+ this.recordThreadRevision(threadId, "live_input_received", now);
529
+ this.setSetting("current_thread_id", threadId);
530
+ });
531
+ const stored = this.requireLiveInput(id);
532
+ return {
533
+ input: mapLiveInput(stored),
534
+ message: this.requireMessage(messageId),
535
+ thread: this.requireThread(threadId),
536
+ offered: status === "offered",
537
+ };
538
+ }
539
+ markLiveInputApplied(id) {
540
+ const row = this.getLiveInput(id);
541
+ if (row === undefined)
542
+ return undefined;
543
+ const message = this.requireMessage(row.message_id);
544
+ const now = this.now();
545
+ this.transaction(() => {
546
+ this.database.prepare("UPDATE messages SET parts_json = ?, updated_at = ? WHERE id = ?")
547
+ .run(JSON.stringify(withLiveInputStatus(message.parts, "applied")), now, row.message_id);
548
+ this.database.prepare("DELETE FROM live_inputs WHERE id = ?").run(id);
549
+ this.database.prepare("UPDATE threads SET updated_at = ?, revision = revision + 1 WHERE id = ?")
550
+ .run(now, row.thread_id);
551
+ this.recordThreadRevision(row.thread_id, "live_input_applied", now);
552
+ });
553
+ return this.requireMessage(row.message_id);
554
+ }
555
+ queueLiveInput(id) {
556
+ const row = this.getLiveInput(id);
557
+ if (row === undefined)
558
+ return undefined;
559
+ const message = this.requireMessage(row.message_id);
560
+ const now = this.now();
561
+ this.transaction(() => {
562
+ this.database.prepare(`
563
+ UPDATE live_inputs SET status = 'queued', active_turn_id = NULL, updated_at = ? WHERE id = ?
564
+ `).run(now, id);
565
+ this.database.prepare("UPDATE messages SET turn_id = NULL, parts_json = ?, updated_at = ? WHERE id = ?")
566
+ .run(JSON.stringify(withLiveInputStatus(message.parts, "queued")), now, row.message_id);
567
+ this.database.prepare("UPDATE threads SET updated_at = ?, revision = revision + 1 WHERE id = ?")
568
+ .run(now, row.thread_id);
569
+ this.recordThreadRevision(row.thread_id, "live_input_queued", now);
570
+ });
571
+ return this.requireMessage(row.message_id);
572
+ }
573
+ cancelLiveInput(id) {
574
+ const row = this.getLiveInput(id);
575
+ if (row === undefined)
576
+ return undefined;
577
+ const message = this.requireMessage(row.message_id);
578
+ const now = this.now();
579
+ this.transaction(() => {
580
+ this.database.prepare("UPDATE messages SET turn_id = NULL, parts_json = ?, updated_at = ? WHERE id = ?")
581
+ .run(JSON.stringify(withLiveInputStatus(message.parts, "cancelled")), now, row.message_id);
582
+ this.database.prepare("DELETE FROM live_inputs WHERE id = ?").run(id);
583
+ this.database.prepare("UPDATE threads SET updated_at = ?, revision = revision + 1 WHERE id = ?")
584
+ .run(now, row.thread_id);
585
+ this.recordThreadRevision(row.thread_id, "live_input_cancelled", now);
586
+ });
587
+ return this.requireMessage(row.message_id);
588
+ }
589
+ cancelLiveInputs(threadId) {
590
+ const rows = this.database.prepare("SELECT * FROM live_inputs WHERE thread_id = ? ORDER BY created_at, rowid").all(threadId);
591
+ if (rows.length === 0)
592
+ return [];
593
+ const now = this.now();
594
+ this.transaction(() => {
595
+ const update = this.database.prepare("UPDATE messages SET turn_id = NULL, parts_json = ?, updated_at = ? WHERE id = ?");
596
+ for (const row of rows) {
597
+ const message = this.requireMessage(row.message_id);
598
+ update.run(JSON.stringify(withLiveInputStatus(message.parts, "cancelled")), now, row.message_id);
599
+ }
600
+ this.database.prepare("DELETE FROM live_inputs WHERE thread_id = ?").run(threadId);
601
+ this.database.prepare("UPDATE threads SET updated_at = ?, revision = revision + 1 WHERE id = ?")
602
+ .run(now, threadId);
603
+ this.recordThreadRevision(threadId, "live_inputs_cancelled", now);
604
+ });
605
+ return rows.map((row) => this.requireMessage(row.message_id));
606
+ }
607
+ queuedLiveInputThreadIds() {
608
+ return this.database.prepare(`
609
+ SELECT thread_id FROM live_inputs WHERE status = 'queued'
610
+ GROUP BY thread_id ORDER BY MIN(created_at), thread_id
611
+ `).all().map((row) => row.thread_id);
612
+ }
613
+ promoteNextQueuedLiveInput(threadId) {
614
+ const active = this.database.prepare("SELECT id FROM turns WHERE thread_id = ? AND status = 'running'").get(threadId);
615
+ if (active !== undefined)
616
+ return undefined;
617
+ const row = this.database.prepare(`
618
+ SELECT * FROM live_inputs
619
+ WHERE thread_id = ? AND status = 'queued'
620
+ ORDER BY created_at, rowid LIMIT 1
621
+ `).get(threadId);
622
+ if (row === undefined)
623
+ return undefined;
624
+ const thread = this.requireThread(threadId);
625
+ if (!thread.canSend || thread.archivedAt !== null)
626
+ return undefined;
627
+ const turnId = randomUUID();
628
+ const assistantMessageId = randomUUID();
629
+ const now = this.now();
630
+ const userMessage = this.requireMessage(row.message_id);
631
+ this.transaction(() => {
632
+ this.database.prepare(`
633
+ INSERT INTO turns (
634
+ id, thread_id, status, text, model, effort, assistant_message_id,
635
+ started_at, finished_at, error_code, error_message
636
+ ) VALUES (?, ?, 'running', ?, ?, ?, ?, ?, NULL, NULL, NULL)
637
+ `).run(turnId, threadId, row.text, row.model, row.effort, assistantMessageId, now);
638
+ this.database.prepare("UPDATE messages SET turn_id = ?, parts_json = ?, updated_at = ? WHERE id = ?")
639
+ .run(turnId, JSON.stringify(withoutLiveInputTelemetry(userMessage.parts)), now, row.message_id);
640
+ this.database.prepare(`
641
+ INSERT INTO messages (id, thread_id, turn_id, role, parts_json, created_at, updated_at, status)
642
+ VALUES (?, ?, ?, 'assistant', '[]', ?, ?, 'running')
643
+ `).run(assistantMessageId, threadId, turnId, now, now);
644
+ this.database.prepare("DELETE FROM live_inputs WHERE id = ?").run(row.id);
645
+ this.database.prepare("UPDATE threads SET updated_at = ?, revision = revision + 1 WHERE id = ?")
646
+ .run(now, threadId);
647
+ this.recordThreadRevision(threadId, "turn_started", now);
648
+ });
649
+ return {
650
+ turnId,
651
+ conversationId: `web:${threadId}`,
652
+ text: row.text,
653
+ userMessageId: row.message_id,
654
+ assistantMessageId,
655
+ attachments: [],
656
+ thread: this.requireThread(threadId),
657
+ };
658
+ }
433
659
  applyStreamFrame(turnId, frame) {
434
660
  return this.applyStreamFrames(turnId, [frame]);
435
661
  }
@@ -502,13 +728,13 @@ export class WebStore {
502
728
  try {
503
729
  this.database.exec("PRAGMA foreign_keys = ON; PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL;");
504
730
  const versionRow = this.database.prepare("PRAGMA user_version").get();
505
- if (versionRow.user_version > 2) {
506
- throw new WebConsoleError("unsupported_storage_schema", `Web state schema ${versionRow.user_version} is newer than supported schema 2.`, 500);
731
+ if (versionRow.user_version > 3) {
732
+ throw new WebConsoleError("unsupported_storage_schema", `Web state schema ${versionRow.user_version} is newer than supported schema 3.`, 500);
507
733
  }
508
734
  if (versionRow.user_version < 0) {
509
735
  throw new WebConsoleError("storage_corrupt", "Web state schema version is invalid.", 500);
510
736
  }
511
- const migrating = versionRow.user_version < 2;
737
+ const migrating = versionRow.user_version < 3;
512
738
  if (migrating)
513
739
  this.database.exec("BEGIN IMMEDIATE");
514
740
  try {
@@ -564,6 +790,20 @@ export class WebStore {
564
790
  status TEXT NOT NULL
565
791
  );
566
792
  CREATE INDEX IF NOT EXISTS messages_by_thread ON messages(thread_id, created_at);
793
+ CREATE TABLE IF NOT EXISTS live_inputs (
794
+ id TEXT PRIMARY KEY,
795
+ thread_id TEXT NOT NULL REFERENCES threads(id) ON DELETE CASCADE,
796
+ message_id TEXT NOT NULL UNIQUE REFERENCES messages(id) ON DELETE CASCADE,
797
+ active_turn_id TEXT REFERENCES turns(id) ON DELETE SET NULL,
798
+ text TEXT NOT NULL,
799
+ model TEXT,
800
+ effort TEXT,
801
+ status TEXT NOT NULL CHECK (status IN ('offered', 'queued')),
802
+ created_at TEXT NOT NULL,
803
+ updated_at TEXT NOT NULL
804
+ );
805
+ CREATE INDEX IF NOT EXISTS live_inputs_by_thread
806
+ ON live_inputs(thread_id, status, created_at);
567
807
  CREATE TABLE IF NOT EXISTS attachments (
568
808
  id TEXT PRIMARY KEY,
569
809
  thread_id TEXT REFERENCES threads(id) ON DELETE CASCADE,
@@ -610,7 +850,7 @@ export class WebStore {
610
850
  }
611
851
  }
612
852
  if (migrating)
613
- this.database.exec("PRAGMA user_version = 2; COMMIT");
853
+ this.database.exec("PRAGMA user_version = 3; COMMIT");
614
854
  }
615
855
  catch (error) {
616
856
  if (this.database.isTransaction)
@@ -635,6 +875,7 @@ export class WebStore {
635
875
  "threads",
636
876
  "turns",
637
877
  "messages",
878
+ "live_inputs",
638
879
  "attachments",
639
880
  "revisions",
640
881
  "settings",
@@ -662,6 +903,33 @@ export class WebStore {
662
903
  this.interruptTurn(turnId, "The web service restarted before this turn completed.");
663
904
  }
664
905
  }
906
+ recoverLiveInputs() {
907
+ const rows = this.database.prepare("SELECT * FROM live_inputs WHERE status = 'offered' ORDER BY created_at, rowid").all();
908
+ if (rows.length === 0)
909
+ return;
910
+ const now = this.now();
911
+ const threadIds = new Set(rows.map((row) => row.thread_id));
912
+ this.transaction(() => {
913
+ const updateInput = this.database.prepare(`
914
+ UPDATE live_inputs SET status = 'queued', active_turn_id = NULL, updated_at = ? WHERE id = ?
915
+ `);
916
+ const updateMessage = this.database.prepare("UPDATE messages SET turn_id = NULL, parts_json = ?, updated_at = ? WHERE id = ?");
917
+ for (const row of rows) {
918
+ const persisted = this.database.prepare("SELECT parts_json FROM messages WHERE id = ?")
919
+ .get(row.message_id);
920
+ if (persisted === undefined) {
921
+ throw new WebConsoleError("storage_corrupt", `Live input ${row.id} has no message.`, 500);
922
+ }
923
+ updateInput.run(now, row.id);
924
+ updateMessage.run(JSON.stringify(withLiveInputStatus(parseParts(persisted.parts_json), "queued")), now, row.message_id);
925
+ }
926
+ for (const threadId of threadIds) {
927
+ this.database.prepare("UPDATE threads SET updated_at = ?, revision = revision + 1 WHERE id = ?")
928
+ .run(now, threadId);
929
+ this.recordThreadRevision(threadId, "live_inputs_recovered", now);
930
+ }
931
+ });
932
+ }
665
933
  finishTurn(turnId, status, finalText, errorCode, errorMessage, runtime) {
666
934
  const turn = this.requireTurn(turnId);
667
935
  const existing = this.requireMessage(turn.assistant_message_id);
@@ -716,17 +984,20 @@ export class WebStore {
716
984
  .all(row.id);
717
985
  const storedParts = parseParts(row.parts_json);
718
986
  const quote = quoteFromParts(storedParts);
987
+ const liveInputStatus = liveInputStatusFromParts(storedParts);
719
988
  return {
720
989
  id: row.id,
721
990
  threadId: row.thread_id,
722
991
  ...(row.turn_id === null ? {} : { turnId: row.turn_id }),
723
992
  role: normalizeRole(row.role),
724
993
  ...(quote === undefined ? {} : { quote }),
725
- parts: storedParts.filter((part) => part.type !== "telemetry" || part.event !== QUOTE_TELEMETRY_EVENT),
994
+ parts: storedParts.filter((part) => part.type !== "telemetry"
995
+ || (part.event !== QUOTE_TELEMETRY_EVENT && part.event !== LIVE_INPUT_TELEMETRY_EVENT)),
726
996
  attachments: attachments.map((attachment) => toWebAttachment(mapStoredAttachment(attachment))),
727
997
  createdAt: row.created_at,
728
998
  updatedAt: row.updated_at,
729
999
  status: normalizeMessageStatus(row.status),
1000
+ ...(liveInputStatus === undefined ? {} : { liveInputStatus }),
730
1001
  };
731
1002
  }
732
1003
  latestRunState(threadId) {
@@ -784,6 +1055,16 @@ export class WebStore {
784
1055
  throw new WebConsoleError("message_not_found", "Message not found.", 404);
785
1056
  return this.mapMessage(row);
786
1057
  }
1058
+ getLiveInput(id) {
1059
+ return this.database.prepare("SELECT * FROM live_inputs WHERE id = ?")
1060
+ .get(id);
1061
+ }
1062
+ requireLiveInput(id) {
1063
+ const row = this.getLiveInput(id);
1064
+ if (row === undefined)
1065
+ throw new WebConsoleError("live_input_not_found", "Live input not found.", 404);
1066
+ return row;
1067
+ }
787
1068
  requireStoredAttachment(id) {
788
1069
  const attachment = this.getStoredAttachment(id);
789
1070
  if (attachment === undefined)
@@ -890,6 +1171,16 @@ function mapStoredAttachment(row) {
890
1171
  updatedAt: row.updated_at,
891
1172
  };
892
1173
  }
1174
+ function mapLiveInput(row) {
1175
+ return {
1176
+ id: row.id,
1177
+ threadId: row.thread_id,
1178
+ messageId: row.message_id,
1179
+ text: row.text,
1180
+ status: row.status,
1181
+ createdAt: row.created_at,
1182
+ };
1183
+ }
893
1184
  export function toWebAttachment(attachment) {
894
1185
  return {
895
1186
  id: attachment.id,
@@ -939,8 +1230,37 @@ function applyEvent(parts, event) {
939
1230
  });
940
1231
  return;
941
1232
  }
1233
+ if (event.type === "runtime_telemetry" && event.kind === "context_compaction") {
1234
+ upsertContextCompaction(parts, event);
1235
+ return;
1236
+ }
942
1237
  parts.push({ type: "telemetry", event: event.type, data: event });
943
1238
  }
1239
+ function contextCompactionOperationId(value) {
1240
+ if (value === null || typeof value !== "object" || Array.isArray(value))
1241
+ return undefined;
1242
+ const event = value;
1243
+ if (event.type !== "runtime_telemetry" || event.kind !== "context_compaction")
1244
+ return undefined;
1245
+ const data = event.data;
1246
+ if (data === null || typeof data !== "object" || Array.isArray(data))
1247
+ return undefined;
1248
+ const operationId = data.operationId;
1249
+ return typeof operationId === "string" && operationId.length > 0 ? operationId : undefined;
1250
+ }
1251
+ function upsertContextCompaction(parts, event) {
1252
+ const operationId = contextCompactionOperationId(event);
1253
+ const next = { type: "telemetry", event: event.type, data: event };
1254
+ if (operationId === undefined) {
1255
+ parts.push(next);
1256
+ return;
1257
+ }
1258
+ const index = parts.findIndex((part) => part.type === "telemetry" && contextCompactionOperationId(part.data) === operationId);
1259
+ if (index < 0)
1260
+ parts.push(next);
1261
+ else
1262
+ parts[index] = next;
1263
+ }
944
1264
  function existingToolName(parts, id) {
945
1265
  const existing = parts.find((part) => part.type === "tool-call" && part.toolCallId === id);
946
1266
  return existing?.type === "tool-call" ? existing.toolName : undefined;
@@ -1035,6 +1355,33 @@ function parseParts(value) {
1035
1355
  return parsed;
1036
1356
  }
1037
1357
  const QUOTE_TELEMETRY_EVENT = "quote";
1358
+ const LIVE_INPUT_TELEMETRY_EVENT = "live_input";
1359
+ function liveInputTelemetry(status) {
1360
+ return { type: "telemetry", event: LIVE_INPUT_TELEMETRY_EVENT, data: { status } };
1361
+ }
1362
+ function withoutLiveInputTelemetry(parts) {
1363
+ return parts.filter((part) => part.type !== "telemetry" || part.event !== LIVE_INPUT_TELEMETRY_EVENT);
1364
+ }
1365
+ function withLiveInputStatus(parts, status) {
1366
+ return [liveInputTelemetry(status), ...withoutLiveInputTelemetry(parts)];
1367
+ }
1368
+ function liveInputStatusFromParts(parts) {
1369
+ const markers = parts.filter((part) => part.type === "telemetry" && part.event === LIVE_INPUT_TELEMETRY_EVENT);
1370
+ if (markers.length === 0)
1371
+ return undefined;
1372
+ if (markers.length !== 1) {
1373
+ throw new WebConsoleError("storage_corrupt", "Persisted live-input metadata is duplicated.", 500);
1374
+ }
1375
+ const data = markers[0]?.data;
1376
+ if (typeof data !== "object" || data === null || Array.isArray(data)) {
1377
+ throw new WebConsoleError("storage_corrupt", "Persisted live-input metadata is invalid.", 500);
1378
+ }
1379
+ const status = data.status;
1380
+ if (status !== "pending" && status !== "applied" && status !== "queued" && status !== "cancelled") {
1381
+ throw new WebConsoleError("storage_corrupt", "Persisted live-input status is invalid.", 500);
1382
+ }
1383
+ return status;
1384
+ }
1038
1385
  function quoteFromParts(parts) {
1039
1386
  const markers = parts.filter((part) => part.type === "telemetry" && part.event === QUOTE_TELEMETRY_EVENT);
1040
1387
  if (markers.length === 0)