@bendyline/docblocks 2.7.6 → 2.7.8

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.
@@ -24,8 +24,7 @@ var RawIndexedDBFileSystemStore = class {
24
24
  this.closed = false;
25
25
  }
26
26
  async transaction(mode, operation) {
27
- const database = await this.getDatabase();
28
- const nativeTransaction = database.transaction(this.storeName, mode);
27
+ const nativeTransaction = await this.beginTransaction(mode);
29
28
  const completion = waitForTransaction(nativeTransaction);
30
29
  const transaction = new RawIndexedDBFileSystemTransaction(
31
30
  nativeTransaction.objectStore(this.storeName)
@@ -51,6 +50,30 @@ var RawIndexedDBFileSystemStore = class {
51
50
  this.database = null;
52
51
  this.opening = null;
53
52
  }
53
+ /**
54
+ * Start a native transaction, reconnecting once if the cached connection
55
+ * was closed underneath this store. Browsers close IndexedDB connections on
56
+ * their own (after the device sleeps, when site data is cleared, or when
57
+ * the storage backend restarts), and a closed connection rejects every
58
+ * later `transaction()` call with InvalidStateError ("The database
59
+ * connection is closing"). No work has run at that point, so reopening and
60
+ * starting again is safe.
61
+ */
62
+ async beginTransaction(mode) {
63
+ const database = await this.getDatabase();
64
+ try {
65
+ return database.transaction(this.storeName, mode);
66
+ } catch (error) {
67
+ if (!isClosedConnectionError(error)) throw error;
68
+ this.forgetDatabase(database);
69
+ const reopened = await this.getDatabase();
70
+ return reopened.transaction(this.storeName, mode);
71
+ }
72
+ }
73
+ forgetDatabase(database) {
74
+ if (this.database === database) this.database = null;
75
+ database.close();
76
+ }
54
77
  getDatabase() {
55
78
  if (this.closed) {
56
79
  return Promise.reject(new Error("The IndexedDB filesystem store is closed."));
@@ -67,10 +90,8 @@ var RawIndexedDBFileSystemStore = class {
67
90
  }
68
91
  this.database = database;
69
92
  this.opening = null;
70
- database.onversionchange = () => {
71
- database.close();
72
- if (this.database === database) this.database = null;
73
- };
93
+ database.onversionchange = () => this.forgetDatabase(database);
94
+ database.onclose = () => this.forgetDatabase(database);
74
95
  return database;
75
96
  }).catch((error) => {
76
97
  this.opening = null;
@@ -182,6 +203,9 @@ function openDatabase(databaseName, version, upgrade) {
182
203
  };
183
204
  });
184
205
  }
206
+ function isClosedConnectionError(error) {
207
+ return typeof error === "object" && error !== null && error.name === "InvalidStateError";
208
+ }
185
209
  function requestResult(request) {
186
210
  return new Promise((resolve, reject) => {
187
211
  request.onsuccess = () => resolve(request.result);
@@ -29,6 +29,7 @@ var NO_CAPABILITIES = Object.freeze({
29
29
  exportRememberedTargets: false,
30
30
  openSavedExports: false,
31
31
  externalResources: false,
32
+ externalRelativeMedia: false,
32
33
  openRequests: false,
33
34
  menuCommands: false,
34
35
  pinnedDocumentMirror: false,
@@ -107,6 +108,7 @@ function deriveHostCapabilities(host) {
107
108
  exportRememberedTargets: hasMethod(exports, "resolveTarget") && hasMethod(exports, "pickTarget"),
108
109
  openSavedExports: hasMethod(exports, "openSaved"),
109
110
  externalResources: hasMethod(host.external, "readText"),
111
+ externalRelativeMedia: hasMethod(host.external, "readRelativeMedia"),
110
112
  openRequests: hasMethod(host, "onOpenRequest"),
111
113
  menuCommands: hasMethod(host, "onMenuCommand"),
112
114
  pinnedDocumentMirror: hasMethod(host.menu, "setPinnedDocuments"),
@@ -123,7 +125,7 @@ function deriveHostCapabilities(host) {
123
125
  aiKnowledgeManagement: isRecord(host.ai) && hasMethod(host.ai.knowledge, "state") && hasMethod(host.ai.knowledge, "update"),
124
126
  aiWorkspaceIndex: hasMethod(host.ai, "ensureWorkspace"),
125
127
  aiSearch: hasMethod(host.ai, "search"),
126
- aiImages: hasMethod(host.ai, "generateImage"),
128
+ aiImages: isRecord(host.ai) && hasMethod(host.ai.images, "generate"),
127
129
  aiTranscription: hasMethod(host.ai, "transcribe"),
128
130
  aiSpeechSynthesis: hasMethod(host.ai, "synthesize"),
129
131
  speechInput: hasMethod(host.speech, "transcribe"),
@@ -153,6 +155,8 @@ var AI_WIRE_LIMITS = Object.freeze({
153
155
  knowledgeSources: 8,
154
156
  knowledgeTitles: 8,
155
157
  knowledgeTitleCharacters: 256,
158
+ /** A knowledge search; Gezel accepts up to 8,192 characters. */
159
+ knowledgeQueryCharacters: 8192,
156
160
  /** Passage texts sent with a knowledge report; Gezel caps a retrieval at 24,000 characters. */
157
161
  knowledgeEvidence: 8,
158
162
  knowledgeEvidenceCharacters: 24 * 1024,
@@ -412,7 +416,14 @@ function parseAiChatRequest(value) {
412
416
  if (!hasKeysWithin(
413
417
  value,
414
418
  ["messages", "purpose"],
415
- ["model", "temperature", "maxTokens", "responseFormat"]
419
+ [
420
+ "model",
421
+ "temperature",
422
+ "maxTokens",
423
+ "responseFormat",
424
+ "knowledgeQuery",
425
+ "knowledgeCitations"
426
+ ]
416
427
  )) {
417
428
  return null;
418
429
  }
@@ -423,11 +434,11 @@ function parseAiChatRequest(value) {
423
434
  const messages = [];
424
435
  let totalCharacters = 0;
425
436
  for (const entry of value.messages) {
426
- const message2 = parseAiChatMessage(entry);
427
- if (!message2) return null;
428
- totalCharacters += message2.content.length;
437
+ const message3 = parseAiChatMessage(entry);
438
+ if (!message3) return null;
439
+ totalCharacters += message3.content.length;
429
440
  if (totalCharacters > AI_WIRE_LIMITS.promptCharacters) return null;
430
- messages.push(message2);
441
+ messages.push(message3);
431
442
  }
432
443
  if (value.model !== void 0 && !isBoundedString(value.model, HOST_WIRE_LIMITS.identifierCharacters, 1)) {
433
444
  return null;
@@ -440,13 +451,21 @@ function parseAiChatRequest(value) {
440
451
  }
441
452
  const responseFormat = value.responseFormat === void 0 ? void 0 : parseAiResponseFormat(value.responseFormat);
442
453
  if (responseFormat === null) return null;
454
+ if (value.knowledgeQuery !== void 0 && !isBoundedString(value.knowledgeQuery, AI_WIRE_LIMITS.knowledgeQueryCharacters, 1)) {
455
+ return null;
456
+ }
457
+ if (value.knowledgeCitations !== void 0 && typeof value.knowledgeCitations !== "boolean") {
458
+ return null;
459
+ }
443
460
  return {
444
461
  messages,
445
462
  purpose: value.purpose,
446
463
  ...value.model === void 0 ? {} : { model: value.model },
447
464
  ...value.temperature === void 0 ? {} : { temperature: value.temperature },
448
465
  ...value.maxTokens === void 0 ? {} : { maxTokens: value.maxTokens },
449
- ...responseFormat === void 0 ? {} : { responseFormat }
466
+ ...responseFormat === void 0 ? {} : { responseFormat },
467
+ ...value.knowledgeQuery === void 0 ? {} : { knowledgeQuery: value.knowledgeQuery },
468
+ ...value.knowledgeCitations === void 0 ? {} : { knowledgeCitations: value.knowledgeCitations }
450
469
  };
451
470
  }
452
471
  function parseAiChatProgress(value) {
@@ -489,12 +508,8 @@ function parseAiChatKnowledge(value) {
489
508
  if (value.outcome !== "used" || !hasExactKeys(value, ["outcome", "sources", "evidence"])) {
490
509
  return null;
491
510
  }
492
- const evidence = parseTextList(
493
- value.evidence,
494
- AI_WIRE_LIMITS.knowledgeEvidence,
495
- AI_WIRE_LIMITS.knowledgeEvidenceCharacters
496
- );
497
- if (!evidence || evidence.length === 0) return null;
511
+ const evidence = parseAiChatEvidence(value.evidence);
512
+ if (!evidence) return null;
498
513
  if (!Array.isArray(value.sources) || value.sources.length === 0 || value.sources.length > AI_WIRE_LIMITS.knowledgeSources) {
499
514
  return null;
500
515
  }
@@ -508,6 +523,23 @@ function parseAiChatKnowledge(value) {
508
523
  }
509
524
  return { outcome: "used", sources, evidence };
510
525
  }
526
+ function parseAiChatEvidence(value) {
527
+ if (!Array.isArray(value) || value.length === 0 || value.length > AI_WIRE_LIMITS.knowledgeEvidence) {
528
+ return null;
529
+ }
530
+ const evidence = [];
531
+ let total = 0;
532
+ for (const entry of value) {
533
+ if (!isRecord2(entry) || !hasExactKeys(entry, ["title", "catalogId", "text"])) return null;
534
+ if (!isBoundedString(entry.title, AI_WIRE_LIMITS.knowledgeTitleCharacters)) return null;
535
+ if (!isBoundedString(entry.catalogId, HOST_WIRE_LIMITS.identifierCharacters, 1)) return null;
536
+ if (!isBoundedString(entry.text, AI_WIRE_LIMITS.knowledgeEvidenceCharacters, 1)) return null;
537
+ total += entry.text.length;
538
+ if (total > AI_WIRE_LIMITS.knowledgeEvidenceCharacters) return null;
539
+ evidence.push({ title: entry.title, catalogId: entry.catalogId, text: entry.text });
540
+ }
541
+ return evidence;
542
+ }
511
543
  function parseTextList(value, entries, characters) {
512
544
  if (!Array.isArray(value) || value.length > entries) return null;
513
545
  let total = 0;
@@ -521,7 +553,10 @@ function parseTextList(value, entries, characters) {
521
553
  return texts;
522
554
  }
523
555
  function parseAiFactCheckRequest(value) {
524
- if (!isRecord2(value) || !hasExactKeys(value, ["text", "evidence", "given"])) return null;
556
+ if (!isRecord2(value) || !hasExactKeys(value, ["text", "evidence", "given", "citations"])) {
557
+ return null;
558
+ }
559
+ if (typeof value.citations !== "boolean") return null;
525
560
  if (!isBoundedString(value.text, AI_WIRE_LIMITS.factCheckTextCharacters)) return null;
526
561
  if (!isBoundedString(value.given, AI_WIRE_LIMITS.factCheckGivenCharacters)) return null;
527
562
  const evidence = parseTextList(
@@ -529,11 +564,14 @@ function parseAiFactCheckRequest(value) {
529
564
  AI_WIRE_LIMITS.factCheckEvidence,
530
565
  AI_WIRE_LIMITS.factCheckEvidenceCharacters
531
566
  );
532
- return evidence ? { text: value.text, evidence, given: value.given } : null;
567
+ return evidence ? { text: value.text, evidence, given: value.given, citations: value.citations } : null;
533
568
  }
534
569
  function parseAiFactCheckSentence(value) {
535
- if (!isRecord2(value) || !hasExactKeys(value, ["text", "missing", "strong"])) return null;
570
+ if (!isRecord2(value) || !hasExactKeys(value, ["text", "problem", "missing", "strong"])) {
571
+ return null;
572
+ }
536
573
  if (!isBoundedString(value.text, AI_WIRE_LIMITS.factCheckSentenceCharacters, 1)) return null;
574
+ if (value.problem !== "unsourced" && value.problem !== "bad-citation") return null;
537
575
  if (typeof value.strong !== "boolean") return null;
538
576
  if (!Array.isArray(value.missing) || value.missing.length > AI_WIRE_LIMITS.factCheckDetails) {
539
577
  return null;
@@ -543,7 +581,7 @@ function parseAiFactCheckSentence(value) {
543
581
  if (!isBoundedString(detail, AI_WIRE_LIMITS.knowledgeTitleCharacters, 1)) return null;
544
582
  missing.push(detail);
545
583
  }
546
- return { text: value.text, missing, strong: value.strong };
584
+ return { text: value.text, problem: value.problem, missing, strong: value.strong };
547
585
  }
548
586
  function parseAiFactCheck(value) {
549
587
  if (!isRecord2(value) || !hasExactKeys(value, ["checked", "unsourced"])) return null;
@@ -719,6 +757,7 @@ var SPEECH_WIRE_LIMITS = Object.freeze({
719
757
  languageCharacters: 16,
720
758
  transcriptCharacters: 64 * 1024,
721
759
  segmentEntries: 4096,
760
+ segmentWordEntries: 1024,
722
761
  synthesizeTextCharacters: 2e4,
723
762
  chunkPcmBytes: 16 * 1024 * 1024,
724
763
  chunkWordEntries: 1024,
@@ -800,8 +839,11 @@ function parseSpeechModelKind(value) {
800
839
  }
801
840
  function parseSpeechTranscribeRequest(value) {
802
841
  if (!isRecord3(value)) return null;
803
- if (!hasKeysWithin2(value, ["audio", "mimeType"], ["prompt", "language"])) return null;
842
+ if (!hasKeysWithin2(value, ["audio", "mimeType"], ["prompt", "language", "timestamps"])) {
843
+ return null;
844
+ }
804
845
  if (value.mimeType !== "audio/wav") return null;
846
+ if (value.timestamps !== void 0 && typeof value.timestamps !== "boolean") return null;
805
847
  const audio = ownedBuffer(value.audio, SPEECH_WIRE_LIMITS.transcribeAudioBytes);
806
848
  if (!audio || !hasRiffWaveHeader(audio)) return null;
807
849
  if (value.prompt !== void 0 && !isBoundedString(value.prompt, SPEECH_WIRE_LIMITS.promptCharacters)) {
@@ -814,7 +856,8 @@ function parseSpeechTranscribeRequest(value) {
814
856
  audio,
815
857
  mimeType: "audio/wav",
816
858
  ...value.prompt === void 0 ? {} : { prompt: value.prompt },
817
- ...value.language === void 0 ? {} : { language: value.language }
859
+ ...value.language === void 0 ? {} : { language: value.language },
860
+ ...value.timestamps === true ? { timestamps: true } : {}
818
861
  };
819
862
  }
820
863
  function parseSpeechSynthesizeRequest(value) {
@@ -1031,13 +1074,29 @@ function parseSpeechPreferences(value) {
1031
1074
  speed: patch.speed
1032
1075
  };
1033
1076
  }
1034
- function parseSpeechSegment(value) {
1077
+ function parseSpeechWord(value) {
1035
1078
  if (!isRecord3(value) || !hasExactKeys2(value, ["start", "end", "text"])) return null;
1036
1079
  if (!isFiniteInRange2(value.start, 0, SPEECH_WIRE_LIMITS.durationCeiling)) return null;
1037
1080
  if (!isFiniteInRange2(value.end, value.start, SPEECH_WIRE_LIMITS.durationCeiling)) return null;
1038
1081
  if (!isBoundedString(value.text, SPEECH_WIRE_LIMITS.transcriptCharacters)) return null;
1039
1082
  return { start: value.start, end: value.end, text: value.text };
1040
1083
  }
1084
+ function parseSpeechSegment(value) {
1085
+ if (!isRecord3(value) || !hasKeysWithin2(value, ["start", "end", "text"], ["words"])) return null;
1086
+ const timed = parseSpeechWord({ start: value.start, end: value.end, text: value.text });
1087
+ if (!timed) return null;
1088
+ if (value.words === void 0) return timed;
1089
+ if (!Array.isArray(value.words) || value.words.length > SPEECH_WIRE_LIMITS.segmentWordEntries) {
1090
+ return null;
1091
+ }
1092
+ const words = [];
1093
+ for (const entry of value.words) {
1094
+ const word = parseSpeechWord(entry);
1095
+ if (!word) return null;
1096
+ words.push(word);
1097
+ }
1098
+ return { ...timed, words };
1099
+ }
1041
1100
  function parseSpeechTranscript(value) {
1042
1101
  if (!isRecord3(value)) return null;
1043
1102
  if (!hasExactKeys2(value, ["text", "language", "segments", "durationMs"])) return null;
@@ -1263,6 +1322,262 @@ function parseAiKnowledgeState(value) {
1263
1322
  return { catalogs, improvement };
1264
1323
  }
1265
1324
 
1325
+ // src/host/ai-images.ts
1326
+ var AI_IMAGE_ASPECTS = ["1:1", "4:3", "3:4", "16:9", "9:16"];
1327
+ var AI_IMAGE_FIT_VERDICTS = [
1328
+ "fits",
1329
+ "tight",
1330
+ "over-limit",
1331
+ "may-not-run",
1332
+ "wont-run"
1333
+ ];
1334
+ var AI_IMAGE_WIRE_LIMITS = Object.freeze({
1335
+ promptCharacters: 4e3,
1336
+ /** A PNG at the largest size a model renders. */
1337
+ imageBytes: 64 * 1024 * 1024,
1338
+ models: 256,
1339
+ pixels: 16384,
1340
+ verificationCodeCharacters: 16
1341
+ });
1342
+ var PNG_SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10];
1343
+ function record2(value) {
1344
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1345
+ }
1346
+ function exact2(value, keys) {
1347
+ return Object.keys(value).length === keys.length && keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
1348
+ }
1349
+ function within(value, required, optional) {
1350
+ const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
1351
+ return required.every((key) => Object.prototype.hasOwnProperty.call(value, key)) && Object.keys(value).every((key) => allowed.has(key));
1352
+ }
1353
+ var label2 = (value) => isBoundedString(value, HOST_WIRE_LIMITS.labelCharacters, 1);
1354
+ var message2 = (value) => isBoundedString(value, HOST_WIRE_LIMITS.messageCharacters);
1355
+ var nullableLabel = (value) => value === null || label2(value);
1356
+ var count2 = (value, maximum = Number.MAX_SAFE_INTEGER) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= maximum;
1357
+ var pixels = (value) => count2(value, AI_IMAGE_WIRE_LIMITS.pixels) && value > 0;
1358
+ function isAiImageModelId(value) {
1359
+ return isBoundedString(value, HOST_WIRE_LIMITS.identifierCharacters, 1);
1360
+ }
1361
+ function isAiImageAspect(value) {
1362
+ return typeof value === "string" && AI_IMAGE_ASPECTS.includes(value);
1363
+ }
1364
+ function parseAiImageAccess(value) {
1365
+ if (!record2(value)) return null;
1366
+ switch (value.kind) {
1367
+ case "ready":
1368
+ case "needs-approval":
1369
+ case "provider-outdated":
1370
+ return exact2(value, ["kind"]) ? { kind: value.kind } : null;
1371
+ case "awaiting-approval":
1372
+ if (!exact2(value, ["kind", "verificationCode"])) return null;
1373
+ if (value.verificationCode !== null && !isBoundedString(value.verificationCode, AI_IMAGE_WIRE_LIMITS.verificationCodeCharacters, 1))
1374
+ return null;
1375
+ return { kind: "awaiting-approval", verificationCode: value.verificationCode };
1376
+ case "unavailable":
1377
+ if (!exact2(value, ["kind", "reason", "message"]) || !message2(value.message)) return null;
1378
+ if (value.reason !== "not-connected" && value.reason !== "no-engine" && value.reason !== "graphics-driver" && value.reason !== "platform")
1379
+ return null;
1380
+ return { kind: "unavailable", reason: value.reason, message: value.message };
1381
+ default:
1382
+ return null;
1383
+ }
1384
+ }
1385
+ var MODEL_KEYS = [
1386
+ "id",
1387
+ "label",
1388
+ "description",
1389
+ "state",
1390
+ "downloadBytes",
1391
+ "shared",
1392
+ "license",
1393
+ "commercialUse",
1394
+ "rendersText",
1395
+ "promptStyle",
1396
+ "hardwareTier",
1397
+ "minRamGb",
1398
+ "fit"
1399
+ ];
1400
+ function parseAiImageModel(value) {
1401
+ if (!record2(value) || !exact2(value, MODEL_KEYS)) return null;
1402
+ if (!isAiImageModelId(value.id) || !label2(value.label)) return null;
1403
+ if (value.description !== null && !message2(value.description)) return null;
1404
+ if (value.state !== "installed" && value.state !== "download-required" && value.state !== "downloading")
1405
+ return null;
1406
+ if (value.downloadBytes !== null && !count2(value.downloadBytes)) return null;
1407
+ if (typeof value.shared !== "boolean" || typeof value.rendersText !== "boolean") return null;
1408
+ if (!nullableLabel(value.license)) return null;
1409
+ if (value.commercialUse !== null && typeof value.commercialUse !== "boolean") return null;
1410
+ if (value.promptStyle !== null && value.promptStyle !== "sentences" && value.promptStyle !== "tags")
1411
+ return null;
1412
+ if (value.hardwareTier !== null && value.hardwareTier !== "low" && value.hardwareTier !== "mid" && value.hardwareTier !== "high" && value.hardwareTier !== "workstation")
1413
+ return null;
1414
+ if (value.minRamGb !== null && !count2(value.minRamGb, 1e6)) return null;
1415
+ let fit = null;
1416
+ if (value.fit !== null) {
1417
+ const raw = value.fit;
1418
+ if (!record2(raw) || !exact2(raw, ["verdict", "reason"]) || typeof raw.verdict !== "string" || !AI_IMAGE_FIT_VERDICTS.includes(raw.verdict) || !message2(raw.reason))
1419
+ return null;
1420
+ fit = { verdict: raw.verdict, reason: raw.reason };
1421
+ }
1422
+ return {
1423
+ id: value.id,
1424
+ label: value.label,
1425
+ description: value.description,
1426
+ state: value.state,
1427
+ downloadBytes: value.downloadBytes,
1428
+ shared: value.shared,
1429
+ license: value.license,
1430
+ commercialUse: value.commercialUse,
1431
+ rendersText: value.rendersText,
1432
+ promptStyle: value.promptStyle,
1433
+ hardwareTier: value.hardwareTier,
1434
+ minRamGb: value.minRamGb,
1435
+ fit
1436
+ };
1437
+ }
1438
+ function parseAiImageModelList(value) {
1439
+ if (!Array.isArray(value) || value.length > AI_IMAGE_WIRE_LIMITS.models) return null;
1440
+ const models = [];
1441
+ const ids = /* @__PURE__ */ new Set();
1442
+ for (const raw of value) {
1443
+ const model = parseAiImageModel(raw);
1444
+ if (!model || ids.has(model.id)) return null;
1445
+ ids.add(model.id);
1446
+ models.push(model);
1447
+ }
1448
+ return models;
1449
+ }
1450
+ function parseAiImagePreferences(value) {
1451
+ if (!record2(value) || !exact2(value, ["model", "designModel", "aspect"])) return null;
1452
+ if (value.model !== null && !isAiImageModelId(value.model)) return null;
1453
+ if (value.designModel !== null && !isAiImageModelId(value.designModel)) return null;
1454
+ if (!isAiImageAspect(value.aspect)) return null;
1455
+ return { model: value.model, designModel: value.designModel, aspect: value.aspect };
1456
+ }
1457
+ function parseAiImagePreferencesPatch(value) {
1458
+ if (!record2(value) || !within(value, [], ["model", "designModel", "aspect"])) return null;
1459
+ const patch = {};
1460
+ if ("model" in value) {
1461
+ if (value.model !== null && !isAiImageModelId(value.model)) return null;
1462
+ patch.model = value.model;
1463
+ }
1464
+ if ("designModel" in value) {
1465
+ if (value.designModel !== null && !isAiImageModelId(value.designModel)) return null;
1466
+ patch.designModel = value.designModel;
1467
+ }
1468
+ if ("aspect" in value) {
1469
+ if (!isAiImageAspect(value.aspect)) return null;
1470
+ patch.aspect = value.aspect;
1471
+ }
1472
+ return patch;
1473
+ }
1474
+ function parseAiImageRequest(value) {
1475
+ if (!record2(value) || !within(value, ["prompt"], ["negativePrompt", "model", "aspect", "seed"]))
1476
+ return null;
1477
+ if (!isBoundedString(value.prompt, AI_IMAGE_WIRE_LIMITS.promptCharacters, 1)) return null;
1478
+ if (!value.prompt.trim()) return null;
1479
+ if (value.negativePrompt !== void 0 && !isBoundedString(value.negativePrompt, AI_IMAGE_WIRE_LIMITS.promptCharacters))
1480
+ return null;
1481
+ if (value.model !== void 0 && !isAiImageModelId(value.model)) return null;
1482
+ if (value.aspect !== void 0 && !isAiImageAspect(value.aspect)) return null;
1483
+ if (value.seed !== void 0 && !count2(value.seed, 2147483647)) return null;
1484
+ return {
1485
+ prompt: value.prompt,
1486
+ ...value.negativePrompt !== void 0 ? { negativePrompt: value.negativePrompt } : {},
1487
+ ...value.model !== void 0 ? { model: value.model } : {},
1488
+ ...value.aspect !== void 0 ? { aspect: value.aspect } : {},
1489
+ ...value.seed !== void 0 ? { seed: value.seed } : {}
1490
+ };
1491
+ }
1492
+ function ownedBuffer2(value, maximumBytes) {
1493
+ if (value instanceof ArrayBuffer) {
1494
+ return value.byteLength <= maximumBytes ? value.slice(0) : null;
1495
+ }
1496
+ if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
1497
+ if (value.byteLength > maximumBytes) return null;
1498
+ const copy = new Uint8Array(value.byteLength);
1499
+ copy.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
1500
+ return copy.buffer;
1501
+ }
1502
+ return null;
1503
+ }
1504
+ function isPng(buffer) {
1505
+ if (buffer.byteLength < 24) return false;
1506
+ const bytes = new Uint8Array(buffer, 0, PNG_SIGNATURE.length);
1507
+ return PNG_SIGNATURE.every((byte, index) => bytes[index] === byte);
1508
+ }
1509
+ function parseAiImageResult(value) {
1510
+ if (!record2(value) || !exact2(value, ["mimeType", "data", "width", "height", "seed", "model", "durationMs"]))
1511
+ return null;
1512
+ if (value.mimeType !== "image/png") return null;
1513
+ const data = ownedBuffer2(value.data, AI_IMAGE_WIRE_LIMITS.imageBytes);
1514
+ if (!data || !isPng(data)) return null;
1515
+ if (!pixels(value.width) || !pixels(value.height)) return null;
1516
+ if (!count2(value.seed) || !count2(value.durationMs) || !isAiImageModelId(value.model)) return null;
1517
+ return {
1518
+ mimeType: "image/png",
1519
+ data,
1520
+ width: value.width,
1521
+ height: value.height,
1522
+ seed: value.seed,
1523
+ model: value.model,
1524
+ durationMs: value.durationMs
1525
+ };
1526
+ }
1527
+ function parseAiImageProgress(value) {
1528
+ if (!record2(value) || !exact2(value, ["phase", "step", "totalSteps", "secondsPerStep"]))
1529
+ return null;
1530
+ if (value.phase !== "queued" && value.phase !== "loading-model" && value.phase !== "sampling" && value.phase !== "decoding")
1531
+ return null;
1532
+ if (!count2(value.step, 1e6) || !count2(value.totalSteps, 1e6)) return null;
1533
+ if (value.secondsPerStep !== null && !(typeof value.secondsPerStep === "number" && Number.isFinite(value.secondsPerStep) && value.secondsPerStep >= 0 && value.secondsPerStep <= 86400))
1534
+ return null;
1535
+ return {
1536
+ phase: value.phase,
1537
+ step: value.step,
1538
+ totalSteps: value.totalSteps,
1539
+ secondsPerStep: value.secondsPerStep
1540
+ };
1541
+ }
1542
+ function parseAiImageEvent(value) {
1543
+ if (!record2(value)) return null;
1544
+ if (value.kind === "progress") {
1545
+ if (!exact2(value, ["kind", "progress"])) return null;
1546
+ const progress = parseAiImageProgress(value.progress);
1547
+ return progress ? { kind: "progress", progress } : null;
1548
+ }
1549
+ if (value.kind === "done") {
1550
+ if (!exact2(value, ["kind", "image"])) return null;
1551
+ const image = parseAiImageResult(value.image);
1552
+ return image ? { kind: "done", image } : null;
1553
+ }
1554
+ if (value.kind === "error") {
1555
+ if (!exact2(value, ["kind", "error"])) return null;
1556
+ const error = parseAiError(value.error);
1557
+ return error ? { kind: "error", error } : null;
1558
+ }
1559
+ return null;
1560
+ }
1561
+ function parseAiImageInstallEvent(value) {
1562
+ if (!record2(value)) return null;
1563
+ if (value.kind === "progress") {
1564
+ if (!exact2(value, ["kind", "progress"])) return null;
1565
+ const progress = parseAiProgress(value.progress);
1566
+ return progress ? { kind: "progress", progress } : null;
1567
+ }
1568
+ if (value.kind === "done") {
1569
+ if (!exact2(value, ["kind", "model"])) return null;
1570
+ const model = parseAiImageModel(value.model);
1571
+ return model ? { kind: "done", model } : null;
1572
+ }
1573
+ if (value.kind === "error") {
1574
+ if (!exact2(value, ["kind", "error"])) return null;
1575
+ const error = parseAiError(value.error);
1576
+ return error ? { kind: "error", error } : null;
1577
+ }
1578
+ return null;
1579
+ }
1580
+
1266
1581
  // src/host/index.ts
1267
1582
  function rawHost() {
1268
1583
  if (typeof globalThis === "undefined") return null;
@@ -1344,6 +1659,20 @@ export {
1344
1659
  ELECTRON_FILE_SYSTEM_V2_CAPABILITIES,
1345
1660
  parseAiKnowledgeAction,
1346
1661
  parseAiKnowledgeState,
1662
+ AI_IMAGE_ASPECTS,
1663
+ AI_IMAGE_FIT_VERDICTS,
1664
+ AI_IMAGE_WIRE_LIMITS,
1665
+ isAiImageModelId,
1666
+ isAiImageAspect,
1667
+ parseAiImageAccess,
1668
+ parseAiImageModel,
1669
+ parseAiImageModelList,
1670
+ parseAiImagePreferences,
1671
+ parseAiImagePreferencesPatch,
1672
+ parseAiImageRequest,
1673
+ parseAiImageResult,
1674
+ parseAiImageEvent,
1675
+ parseAiImageInstallEvent,
1347
1676
  hasDocBlocksHost,
1348
1677
  getHostCapabilities,
1349
1678
  hostSupports,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  FILE_SYSTEM_TRANSFER_LIMITS,
3
3
  maybeGetDocBlocksHost
4
- } from "./chunk-P4VIU7MB.js";
4
+ } from "./chunk-WE5W7BRL.js";
5
5
  import {
6
6
  parseFileSystemCapabilities
7
7
  } from "./chunk-245W57QP.js";
@@ -200,6 +200,8 @@ interface DocumentSessionConflict {
200
200
  targetKey: string;
201
201
  localContent: string;
202
202
  localRevision: number;
203
+ /** Wall-clock time of this session's latest local edit, when it made one. */
204
+ localEditedAt?: number;
203
205
  externalContent: string | null;
204
206
  externalVersion: DocumentExternalVersion;
205
207
  }
@@ -287,6 +289,7 @@ declare class DocumentSession {
287
289
  private frozen;
288
290
  private error;
289
291
  private conflict;
292
+ private localEditedAt;
290
293
  private autoSaveTimer;
291
294
  private autoSaveRetryAttempt;
292
295
  private drainPromise;
@@ -9,7 +9,7 @@ import {
9
9
  DocumentSession,
10
10
  DocumentSessionConflictError,
11
11
  createFileSystemDocumentTarget
12
- } from "../chunk-FIEMYP2N.js";
12
+ } from "../chunk-D7VUEVAX.js";
13
13
  import "../chunk-XAMILHQP.js";
14
14
  import "../chunk-IYKGM3PV.js";
15
15
  import "../chunk-UTH3HYCZ.js";
@@ -4,7 +4,7 @@ export { isElectronHost } from '../host/index.js';
4
4
  import '../v2-DwxWjFC0.js';
5
5
  import '../workspace-path-CWVrcuPL.js';
6
6
  import '../filesystem-v2-CXdAPR27.js';
7
- import '../capabilities-DJ7X9EOt.js';
7
+ import '../capabilities-CH66e2pR.js';
8
8
 
9
9
  /** Compatibility entry point retaining the Electron backend's exact guarantees. */
10
10
  declare class ElectronFileSystemProviderV2 extends HostFileSystemProviderV2 {
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  ElectronFileSystemProvider,
3
3
  ElectronFileSystemProviderV2
4
- } from "../chunk-56QRV45P.js";
5
- import "../chunk-45XD424D.js";
4
+ } from "../chunk-LP6JHGBP.js";
5
+ import "../chunk-ZBRXXOW5.js";
6
6
  import {
7
7
  isElectronHost
8
- } from "../chunk-P4VIU7MB.js";
8
+ } from "../chunk-WE5W7BRL.js";
9
9
  import "../chunk-245W57QP.js";
10
10
  import "../chunk-V5SWLUZY.js";
11
11
  import "../chunk-73OOROXH.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  HostFileSystemProviderV2
3
- } from "../chunk-45XD424D.js";
3
+ } from "../chunk-ZBRXXOW5.js";
4
4
  import {
5
5
  ELECTRON_FILE_SYSTEM_V2_CAPABILITIES,
6
6
  maybeGetDocBlocksHost
7
- } from "../chunk-P4VIU7MB.js";
7
+ } from "../chunk-WE5W7BRL.js";
8
8
  import "../chunk-245W57QP.js";
9
9
  import "../chunk-V5SWLUZY.js";
10
10
  import "../chunk-73OOROXH.js";