token-goat 2.9.28 → 2.9.30

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 (134) hide show
  1. package/README.md +36 -40
  2. package/SECURITY.md +28 -10
  3. package/THIRD_PARTY_NOTICES.md +40 -3
  4. package/dist/native/linux-arm64/tg-hook +0 -0
  5. package/dist/native/linux-x64/tg-hook +0 -0
  6. package/dist/ort-wasm-simd-threaded.mjs +59 -0
  7. package/dist/token-goat-chunk-3CHUNJ2O.mjs +53 -0
  8. package/dist/token-goat-chunk-3Z5S4KGM.mjs +918 -0
  9. package/dist/token-goat-chunk-4FVIAHSI.mjs +211 -0
  10. package/dist/{token-goat-chunk-66LODAUT.mjs → token-goat-chunk-4JJS54TR.mjs} +4 -3
  11. package/dist/token-goat-chunk-4LCCNRTG.mjs +313 -0
  12. package/dist/token-goat-chunk-54K3EUV3.mjs +35 -0
  13. package/dist/token-goat-chunk-56EIMS3E.mjs +159 -0
  14. package/dist/token-goat-chunk-5BCIJXMO.mjs +609 -0
  15. package/dist/token-goat-chunk-5KDMCMF2.mjs +362 -0
  16. package/dist/token-goat-chunk-5RG5SBL6.mjs +221 -0
  17. package/dist/{token-goat-chunk-RRNZMM3A.mjs → token-goat-chunk-5Y6ZYL6U.mjs} +21 -2
  18. package/dist/{token-goat-chunk-NILVK7XM.mjs → token-goat-chunk-64FBPDTK.mjs} +12 -6
  19. package/dist/{token-goat-chunk-LEG3JNJ6.mjs → token-goat-chunk-664RTTUV.mjs} +31 -15
  20. package/dist/token-goat-chunk-67TQJ76B.mjs +57 -0
  21. package/dist/token-goat-chunk-6QMAS7AB.mjs +42 -0
  22. package/dist/token-goat-chunk-7BXSNBY5.mjs +253 -0
  23. package/dist/{token-goat-chunk-F7QZRIWM.mjs → token-goat-chunk-7F2UBPD3.mjs} +42 -35
  24. package/dist/{token-goat-chunk-CC2PYJM7.mjs → token-goat-chunk-7IN2MKDS.mjs} +718 -595
  25. package/dist/token-goat-chunk-7NADEDKJ.mjs +124 -0
  26. package/dist/{token-goat-chunk-6SGLIPQB.mjs → token-goat-chunk-A3CK6N5Z.mjs} +621 -72
  27. package/dist/{token-goat-chunk-HXNAC4ZF.mjs → token-goat-chunk-AFLYLHDH.mjs} +48 -33
  28. package/dist/token-goat-chunk-AM5V6GSX.mjs +367 -0
  29. package/dist/{token-goat-chunk-I7CB3X4K.mjs → token-goat-chunk-APGZXSPS.mjs} +44 -148
  30. package/dist/{token-goat-chunk-BJBRGPBX.mjs → token-goat-chunk-ARDNEC2U.mjs} +11 -7
  31. package/dist/token-goat-chunk-ATCPY7EQ.mjs +134 -0
  32. package/dist/token-goat-chunk-B2RZZCSS.mjs +354 -0
  33. package/dist/{token-goat-chunk-QM76S6YS.mjs → token-goat-chunk-B45VMIIH.mjs} +6 -5
  34. package/dist/token-goat-chunk-BPGCUMR7.mjs +30 -0
  35. package/dist/{token-goat-chunk-GGLEE35R.mjs → token-goat-chunk-C63JQV5K.mjs} +49 -10
  36. package/dist/{token-goat-chunk-RJH64KQH.mjs → token-goat-chunk-COBQGL5S.mjs} +2661 -2440
  37. package/dist/token-goat-chunk-COTXZHTS.mjs +39 -0
  38. package/dist/token-goat-chunk-CYCEYAEO.mjs +265 -0
  39. package/dist/token-goat-chunk-CZ3LUXYZ.mjs +4115 -0
  40. package/dist/token-goat-chunk-DZX2YDPT.mjs +15 -0
  41. package/dist/token-goat-chunk-E3OQX2XT.mjs +824 -0
  42. package/dist/token-goat-chunk-EFHMMIGP.mjs +68 -0
  43. package/dist/token-goat-chunk-ETBHVDB6.mjs +328 -0
  44. package/dist/token-goat-chunk-EVMZJ27Z.mjs +147 -0
  45. package/dist/token-goat-chunk-EXKGNHEG.mjs +2096 -0
  46. package/dist/{token-goat-chunk-6Z5ULNE5.mjs → token-goat-chunk-FBNQNSJK.mjs} +2490 -4957
  47. package/dist/{token-goat-chunk-NMTKNYGF.mjs → token-goat-chunk-FNFBKLRD.mjs} +74 -9
  48. package/dist/{token-goat-chunk-XI2VFAV6.mjs → token-goat-chunk-FZ3NEW5O.mjs} +33 -19
  49. package/dist/token-goat-chunk-G5PJ7AGM.mjs +36 -0
  50. package/dist/{token-goat-chunk-VE27HPIF.mjs → token-goat-chunk-GFBSN7BD.mjs} +30 -398
  51. package/dist/{token-goat-chunk-L45ZQRT7.mjs → token-goat-chunk-GOLEOXZP.mjs} +780 -786
  52. package/dist/token-goat-chunk-H2K3QP6H.mjs +24 -0
  53. package/dist/token-goat-chunk-H2PHHGM5.mjs +188 -0
  54. package/dist/token-goat-chunk-HAVAOB3S.mjs +87 -0
  55. package/dist/token-goat-chunk-HG45AF2B.mjs +2228 -0
  56. package/dist/{token-goat-chunk-CBMY2FGI.mjs → token-goat-chunk-HOJLCGDP.mjs} +17113 -15554
  57. package/dist/{token-goat-chunk-HUDC5GS5.mjs → token-goat-chunk-HYBFA5X3.mjs} +120 -19
  58. package/dist/token-goat-chunk-IVALLT3S.mjs +48 -0
  59. package/dist/token-goat-chunk-IVE6U7KV.mjs +53 -0
  60. package/dist/{token-goat-chunk-UBUKHVYW.mjs → token-goat-chunk-IVKIZWAB.mjs} +3 -3
  61. package/dist/{token-goat-chunk-4JMWPWCS.mjs → token-goat-chunk-JB6QN6VF.mjs} +6 -4
  62. package/dist/{token-goat-chunk-Q4S3UQGO.mjs → token-goat-chunk-JDHE26RP.mjs} +10 -3
  63. package/dist/{token-goat-chunk-7R2XYKC5.mjs → token-goat-chunk-JDU3EXSU.mjs} +14 -10
  64. package/dist/token-goat-chunk-JDWCRD6M.mjs +253 -0
  65. package/dist/{token-goat-chunk-XBESNWEX.mjs → token-goat-chunk-JN4XMLSD.mjs} +7 -4
  66. package/dist/token-goat-chunk-JTQQGXZH.mjs +733 -0
  67. package/dist/token-goat-chunk-KKUFXAPD.mjs +25 -0
  68. package/dist/token-goat-chunk-KQNZDCBD.mjs +47 -0
  69. package/dist/{token-goat-chunk-4RN64ZND.mjs → token-goat-chunk-KWPNJBDH.mjs} +28 -3
  70. package/dist/token-goat-chunk-LOBGHXPB.mjs +242 -0
  71. package/dist/token-goat-chunk-MKPF4CL5.mjs +37 -0
  72. package/dist/token-goat-chunk-MNWKPRAL.mjs +13 -0
  73. package/dist/{token-goat-chunk-YWSP6IU7.mjs → token-goat-chunk-N2NMER56.mjs} +25 -15
  74. package/dist/token-goat-chunk-NYKZLXMK.mjs +218 -0
  75. package/dist/token-goat-chunk-OJBNYTJP.mjs +3087 -0
  76. package/dist/token-goat-chunk-P4QMM4WK.mjs +295 -0
  77. package/dist/token-goat-chunk-PD5VKH46.mjs +184 -0
  78. package/dist/token-goat-chunk-PEPTHRFF.mjs +129 -0
  79. package/dist/{token-goat-chunk-VWZKZPSR.mjs → token-goat-chunk-PU7V3FD6.mjs} +8752 -11098
  80. package/dist/token-goat-chunk-PY533T7F.mjs +46 -0
  81. package/dist/token-goat-chunk-Q5VTGRH7.mjs +761 -0
  82. package/dist/{token-goat-chunk-IHKZ3QNA.mjs → token-goat-chunk-QCLDAQTS.mjs} +33 -19
  83. package/dist/{token-goat-chunk-LKSXAMJB.mjs → token-goat-chunk-QSBRTGM2.mjs} +9 -2
  84. package/dist/{token-goat-chunk-4STDIGYC.mjs → token-goat-chunk-RC35RBUL.mjs} +7 -5
  85. package/dist/{token-goat-chunk-D5ICEPSB.mjs → token-goat-chunk-RHXKCZX3.mjs} +887 -507
  86. package/dist/{token-goat-chunk-RSS3TLXU.mjs → token-goat-chunk-RZRTDST7.mjs} +17 -7
  87. package/dist/{token-goat-chunk-MAMQPC43.mjs → token-goat-chunk-SJHWVNDQ.mjs} +15 -21
  88. package/dist/token-goat-chunk-SMVRN2HU.mjs +188 -0
  89. package/dist/{token-goat-chunk-EL7M26WP.mjs → token-goat-chunk-SQSSNQPE.mjs} +2100 -1442
  90. package/dist/{token-goat-chunk-OZRSREKR.mjs → token-goat-chunk-T4FK7VOV.mjs} +3 -3
  91. package/dist/token-goat-chunk-U4N3BN2W.mjs +92 -0
  92. package/dist/token-goat-chunk-UA5V6P3P.mjs +122 -0
  93. package/dist/{token-goat-chunk-XKY4VKJ4.mjs → token-goat-chunk-UQ5UK2T7.mjs} +15 -4
  94. package/dist/token-goat-chunk-USILZ3T3.mjs +53 -0
  95. package/dist/token-goat-chunk-VES7EL7O.mjs +205 -0
  96. package/dist/{token-goat-chunk-M56BWPBN.mjs → token-goat-chunk-VL3YIFHZ.mjs} +550 -49
  97. package/dist/{token-goat-chunk-IZXKVT4A.mjs → token-goat-chunk-VPBBTSJR.mjs} +362 -128
  98. package/dist/{token-goat-chunk-F2ARFHLJ.mjs → token-goat-chunk-W3QCJS6G.mjs} +10 -2
  99. package/dist/{token-goat-chunk-FSTWAOW6.mjs → token-goat-chunk-WY3L4DVT.mjs} +11 -5
  100. package/dist/{token-goat-chunk-OSUFN2FV.mjs → token-goat-chunk-XB5KKOCG.mjs} +5 -25
  101. package/dist/token-goat-chunk-XKI4SEMX.mjs +66 -0
  102. package/dist/token-goat-chunk-XU4P6Z24.mjs +133 -0
  103. package/dist/{token-goat-chunk-6CCQDAYY.mjs → token-goat-chunk-Y776AENC.mjs} +138 -33
  104. package/dist/token-goat-chunk-YARZFCZS.mjs +359 -0
  105. package/dist/{token-goat-chunk-EEIDFMEM.mjs → token-goat-chunk-YZAAZU2S.mjs} +12 -3
  106. package/dist/{token-goat-chunk-E3K3BTEQ.mjs → token-goat-chunk-Z44YO3QZ.mjs} +12 -6
  107. package/dist/token-goat-chunk-Z57YODA2.mjs +35 -0
  108. package/dist/{token-goat-chunk-DA43OE7L.mjs → token-goat-chunk-ZCDTCVIS.mjs} +49 -30
  109. package/dist/token-goat-chunk-ZEJVMUQH.mjs +48 -0
  110. package/dist/{token-goat-chunk-BGHSSJUA.mjs → token-goat-chunk-ZFOBKUWH.mjs} +1 -1
  111. package/dist/token-goat-chunk-ZMBL6UVV.mjs +110 -0
  112. package/dist/{token-goat-chunk-SJEKA7Y2.mjs → token-goat-chunk-ZQSJMO2R.mjs} +381 -201
  113. package/dist/token-goat-hook-client.cjs +539 -0
  114. package/dist/token-goat-hook-client.mjs +26 -0
  115. package/dist/token-goat-hook.mjs +48 -28
  116. package/dist/token-goat.core.mjs +10 -45
  117. package/docs/C4_RUNTIME_ARCHITECTURE.md +3 -3
  118. package/docs/cli.md +82 -47
  119. package/docs/install.md +58 -23
  120. package/docs/security.md +7 -7
  121. package/package.json +17 -7
  122. package/scripts/verify-native-dist.mjs +386 -0
  123. package/dist/token-goat-chunk-4JJBFT7V.mjs +0 -20
  124. package/dist/token-goat-chunk-5NNYIUGV.mjs +0 -143
  125. package/dist/token-goat-chunk-6DQNNFJ6.mjs +0 -294
  126. package/dist/token-goat-chunk-7MAFDRVR.mjs +0 -47
  127. package/dist/token-goat-chunk-BCN2YQC6.mjs +0 -32
  128. package/dist/token-goat-chunk-D7GHQKQD.mjs +0 -30
  129. package/dist/token-goat-chunk-GMOUBOX4.mjs +0 -386
  130. package/dist/token-goat-chunk-IFORDBGC.mjs +0 -192
  131. package/dist/token-goat-chunk-JXK2HQXS.mjs +0 -318
  132. package/dist/token-goat-chunk-L7SDVGBT.mjs +0 -430
  133. package/dist/token-goat-chunk-MMLMALWK.mjs +0 -1381
  134. package/dist/token-goat-chunk-MVSWBNMD.mjs +0 -1985
@@ -1,24 +1,28 @@
1
1
  import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
- import {
4
- registerReset
5
- } from "./token-goat-chunk-EEIDFMEM.mjs";
6
3
  import {
7
4
  Database
8
- } from "./token-goat-chunk-OSUFN2FV.mjs";
5
+ } from "./token-goat-chunk-XB5KKOCG.mjs";
6
+ import {
7
+ registerReset
8
+ } from "./token-goat-chunk-YZAAZU2S.mjs";
9
9
  import {
10
- SYMBOL_BODY_CHAR_CAP,
11
10
  countContentLines,
12
- dataDir,
13
11
  ensureDirSync,
14
- escapeRegExp,
12
+ escapeRegExp
13
+ } from "./token-goat-chunk-GOLEOXZP.mjs";
14
+ import {
15
15
  foldCase,
16
16
  foldPath,
17
17
  sleepSync
18
- } from "./token-goat-chunk-L45ZQRT7.mjs";
18
+ } from "./token-goat-chunk-AM5V6GSX.mjs";
19
19
  import {
20
20
  safeJoin
21
- } from "./token-goat-chunk-NMTKNYGF.mjs";
21
+ } from "./token-goat-chunk-FNFBKLRD.mjs";
22
+ import {
23
+ SYMBOL_BODY_CHAR_CAP,
24
+ dataDir
25
+ } from "./token-goat-chunk-VES7EL7O.mjs";
22
26
  import {
23
27
  init_define_import_meta_env
24
28
  } from "./token-goat-chunk-A37V4PBF.mjs";
@@ -389,6 +393,13 @@ function scalaCharLiteralLength(line, index) {
389
393
  return line[index + 1] !== void 0 && line[index + 1] !== "'" && line[index + 2] === "'" ? 3 : 0;
390
394
  }
391
395
  function stripStringLiterals(line, opts = {}) {
396
+ const stringFrame = (idx, delim) => {
397
+ const quote = line[idx];
398
+ const prev = line[idx - 1] ?? "";
399
+ const bareBraceHole = quote === '"' && (idx > 0 && prev === "$" || idx > 1 && prev === "@" && line[idx - 2] === "$" || idx > 1 && prev === "$" && line[idx - 2] === "@");
400
+ const plain = opts.interpolatorPrefix === true && !/[A-Za-z0-9_]/.test(prev) || opts.singleQuoteHoles === true && prev === "r" && !/[A-Za-z0-9_$]/.test(line[idx - 2] ?? "");
401
+ return { kind: "string", quote: delim, bareBraceHole, plain };
402
+ };
392
403
  const openDelim = (idx) => {
393
404
  const q = line[idx];
394
405
  if (opts.tripleQuotes && line[idx + 1] === q && line[idx + 2] === q) return q + q + q;
@@ -413,9 +424,8 @@ function stripStringLiterals(line, opts = {}) {
413
424
  continue;
414
425
  }
415
426
  if (ch === '"' || ch === "'") {
416
- const bareBraceHole = ch === '"' && i > 0 && line[i - 1] === "$";
417
427
  const delim = openDelim(i);
418
- stack.push({ kind: "string", quote: delim, bareBraceHole });
428
+ stack.push(stringFrame(i, delim));
419
429
  out += delim;
420
430
  i += delim.length;
421
431
  continue;
@@ -425,15 +435,35 @@ function stripStringLiterals(line, opts = {}) {
425
435
  continue;
426
436
  }
427
437
  if (top.kind === "hole") {
438
+ if (top.paren === true && (ch === "(" || ch === ")")) {
439
+ if (ch === "(") {
440
+ top.depth++;
441
+ out += ch;
442
+ } else {
443
+ if (top.depth > 0) {
444
+ top.depth--;
445
+ out += ch;
446
+ } else {
447
+ stack.pop();
448
+ out += " ";
449
+ }
450
+ }
451
+ i++;
452
+ continue;
453
+ }
454
+ if (top.paren === true && (ch === "{" || ch === "}")) {
455
+ out += ch;
456
+ i++;
457
+ continue;
458
+ }
428
459
  if (opts.symbolLiterals === true && ch === "'" && scalaCharLiteralLength(line, i) === 0) {
429
460
  out += ch;
430
461
  i++;
431
462
  continue;
432
463
  }
433
464
  if (ch === '"' || ch === "'") {
434
- const bareBraceHole = ch === '"' && i > 0 && line[i - 1] === "$";
435
465
  const delim = openDelim(i);
436
- stack.push({ kind: "string", quote: delim, bareBraceHole });
466
+ stack.push(stringFrame(i, delim));
437
467
  out += delim;
438
468
  i += delim.length;
439
469
  continue;
@@ -458,6 +488,12 @@ function stripStringLiterals(line, opts = {}) {
458
488
  i++;
459
489
  continue;
460
490
  }
491
+ if (opts.swiftInterpolation === true && top.quote === '"' && ch === "\\" && line[i + 1] === "(") {
492
+ stack.push({ kind: "hole", depth: 0, paren: true });
493
+ out += " ";
494
+ i += 2;
495
+ continue;
496
+ }
461
497
  if (ch === "\\" && i + 1 < line.length && line[i + 1] !== "\n") {
462
498
  out += " ";
463
499
  i += 2;
@@ -469,7 +505,7 @@ function stripStringLiterals(line, opts = {}) {
469
505
  i += top.quote.length;
470
506
  continue;
471
507
  }
472
- if (top.quote === '"') {
508
+ if (!top.plain && (top.quote === '"' || opts.singleQuoteHoles === true && top.quote[0] === "'")) {
473
509
  if (top.bareBraceHole && ch === "{") {
474
510
  if (line[i + 1] === "{") {
475
511
  out += " ";
@@ -1180,12 +1216,21 @@ function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex
1180
1216
  const rRaw = opts?.rRawStrings === true;
1181
1217
  const tripleDelims = tripleQuoteDelimiters(opts);
1182
1218
  const lineString = opts?.lineStringPrefix;
1219
+ const interpolation = opts?.interpolation;
1220
+ let interpolated = false;
1183
1221
  let depth = 0;
1184
1222
  let quote = null;
1185
1223
  let verbatim = false;
1186
1224
  for (let i = openBraceIndex; i < content.length; i++) {
1187
1225
  const ch = content[i];
1188
1226
  if (quote !== null) {
1227
+ if (interpolated && interpolation !== void 0) {
1228
+ const past = skipInterpolationAt(content, i, content.length, interpolation);
1229
+ if (past !== null) {
1230
+ i = past - 1;
1231
+ continue;
1232
+ }
1233
+ }
1189
1234
  if (verbatim) {
1190
1235
  if (ch === quote) {
1191
1236
  if (content[i + 1] === quote) {
@@ -1248,6 +1293,7 @@ function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex
1248
1293
  if (ch === '"' || ch === "'" || backtick && ch === "`") {
1249
1294
  quote = ch;
1250
1295
  verbatim = escapes === "csharp" && ch === '"' && opensCsharpVerbatimString(content, i);
1296
+ interpolated = interpolation !== void 0 && opensInterpolatedString(content, i, interpolation);
1251
1297
  continue;
1252
1298
  }
1253
1299
  if (ch === "{") depth++;
@@ -1281,6 +1327,7 @@ function assignBraceBlockSpans(symbols, content, opts = {}) {
1281
1327
  rawStringQuotes: opts.rawStringQuotes ?? false,
1282
1328
  tripleSingleQuote: opts.tripleSingleQuote ?? false,
1283
1329
  tripleQuoteRunClose: opts.tripleQuoteRunClose ?? "last",
1330
+ ...opts.interpolation === void 0 ? {} : { interpolation: opts.interpolation },
1284
1331
  ...blockComment === void 0 ? {} : { blockComment, nestedBlockComments },
1285
1332
  ...lineStringPrefix === void 0 ? {} : { lineStringPrefix },
1286
1333
  ...opts.lineCommentExceptions === void 0 ? {} : { lineCommentExceptions: opts.lineCommentExceptions }
@@ -1288,6 +1335,18 @@ function assignBraceBlockSpans(symbols, content, opts = {}) {
1288
1335
  return symbols.map((sym) => {
1289
1336
  if (sym.lineEnd !== sym.lineStart) return sym;
1290
1337
  const nextStart = starts.find((s) => s > sym.lineStart);
1338
+ if (opts.expressionBodies === true) {
1339
+ const arrowLast = Math.min(nextStart !== void 0 ? nextStart - 1 : totalLines, sym.lineStart + BRACE_SEARCH_MAX_LINES);
1340
+ const arrowEnd = findArrowBodyEndLine(scanContent, lineIndex, sym.lineStart, arrowLast);
1341
+ if (arrowEnd !== null) {
1342
+ return arrowEnd <= sym.lineStart ? withWholeLineBody(sym, lines) : { ...sym, lineEnd: arrowEnd, body: lines.slice(sym.lineStart - 1, arrowEnd).join("\n") };
1343
+ }
1344
+ } else if (opts.expressionBodies === "kotlin") {
1345
+ const eqEnd = findKotlinEqualsBodyEndLine(scanContent, lineIndex, sym.lineStart, nextStart !== void 0 ? nextStart - 1 : totalLines);
1346
+ if (eqEnd !== null) {
1347
+ return eqEnd <= sym.lineStart ? withWholeLineBody(sym, lines) : { ...sym, lineEnd: eqEnd, body: lines.slice(sym.lineStart - 1, eqEnd).join("\n") };
1348
+ }
1349
+ }
1291
1350
  const lastSearchLine = Math.min(nextStart !== void 0 ? nextStart - 1 : totalLines, sym.lineStart + BRACE_SEARCH_MAX_LINES);
1292
1351
  const openIndex = findBlockOpenBrace(scanContent, lineIndex, sym.lineStart, lastSearchLine, lineCommentPrefix, scanOpts);
1293
1352
  if (openIndex === null) return sym;
@@ -1299,11 +1358,304 @@ function assignBraceBlockSpans(symbols, content, opts = {}) {
1299
1358
  lineCommentPrefix,
1300
1359
  scanOpts
1301
1360
  );
1302
- if (endLine <= sym.lineStart) return sym;
1361
+ if (endLine === sym.lineStart) return withWholeLineBody(sym, lines);
1362
+ if (endLine < sym.lineStart) return sym;
1303
1363
  return { ...sym, lineEnd: endLine, body: lines.slice(sym.lineStart - 1, endLine).join("\n") };
1304
1364
  });
1305
1365
  }
1366
+ function withWholeLineBody(sym, lines) {
1367
+ const line = lines[sym.lineStart - 1];
1368
+ if (line === void 0 || sym.body.includes("{") || !line.includes("{")) return sym;
1369
+ return { ...sym, body: line.trimEnd() };
1370
+ }
1306
1371
  var BRACE_SEARCH_MAX_LINES = 10;
1372
+ function skipCsharpInterpolationHole(content, i, to) {
1373
+ let depth = 0;
1374
+ for (let k = i; k < to; k++) {
1375
+ const lit = skipCsharpLiteral(content, k, to);
1376
+ if (lit !== null) {
1377
+ k = lit - 1;
1378
+ continue;
1379
+ }
1380
+ const ch = content[k];
1381
+ if (ch === "{") depth++;
1382
+ else if (ch === "}" && --depth === 0) return k + 1;
1383
+ }
1384
+ return to;
1385
+ }
1386
+ function skipCsharpLiteral(content, i, to) {
1387
+ const first = content[i];
1388
+ if (first === "'") {
1389
+ if (content[i + 1] === "\\") {
1390
+ const close = content.indexOf("'", i + 3);
1391
+ return close === -1 || close >= to ? null : close + 1;
1392
+ }
1393
+ return content[i + 2] === "'" ? i + 3 : null;
1394
+ }
1395
+ let j = i;
1396
+ while ((content[j] === "$" || content[j] === "@") && j - i < 3) j++;
1397
+ if (content[j] !== '"') return null;
1398
+ const prefix = content.slice(i, j);
1399
+ if (j > i && /[A-Za-z0-9_]/.test(content[i - 1] ?? "")) return null;
1400
+ const interpolated = prefix.includes("$");
1401
+ const verbatim = prefix.includes("@");
1402
+ const run = quoteRunLength(content, j);
1403
+ if (run >= 3) {
1404
+ const end = skipRawStringQuotes(content, j + run, run);
1405
+ return end === -1 ? to : end;
1406
+ }
1407
+ for (let k = j + 1; k < to; k++) {
1408
+ const ch = content[k];
1409
+ if (ch === void 0) break;
1410
+ if (verbatim) {
1411
+ if (ch === '"') {
1412
+ if (content[k + 1] === '"') {
1413
+ k++;
1414
+ continue;
1415
+ }
1416
+ return k + 1;
1417
+ }
1418
+ } else if (ch === "\\") {
1419
+ k++;
1420
+ continue;
1421
+ } else if (ch === '"') return k + 1;
1422
+ if (interpolated) {
1423
+ if (ch === "{") {
1424
+ if (content[k + 1] === "{") {
1425
+ k++;
1426
+ continue;
1427
+ }
1428
+ k = skipCsharpInterpolationHole(content, k, to) - 1;
1429
+ }
1430
+ }
1431
+ }
1432
+ return to;
1433
+ }
1434
+ function findArrowBodyEndLine(content, lineIndex, startLine, lastSearchLine) {
1435
+ const from = lineIndex[startLine - 1];
1436
+ if (from === void 0) return null;
1437
+ const to = lineIndex[lastSearchLine] ?? content.length;
1438
+ let depth = 0;
1439
+ let seenArrow = false;
1440
+ for (let i = from; i < to; i++) {
1441
+ const ch = content[i];
1442
+ if (ch === void 0) break;
1443
+ if (ch === "/" && content[i + 1] === "/") {
1444
+ while (i + 1 < to && content[i + 1] !== "\n") i++;
1445
+ continue;
1446
+ }
1447
+ if (ch === "/" && content[i + 1] === "*") {
1448
+ const end = content.indexOf("*/", i + 2);
1449
+ if (end === -1) return null;
1450
+ i = end + 1;
1451
+ continue;
1452
+ }
1453
+ const lit = skipCsharpLiteral(content, i, to);
1454
+ if (lit !== null) {
1455
+ i = lit - 1;
1456
+ continue;
1457
+ }
1458
+ if (ch === "(" || ch === "[") depth++;
1459
+ else if (ch === ")" || ch === "]") {
1460
+ if (depth > 0) depth--;
1461
+ } else if (ch === "=" && content[i + 1] === ">" && depth === 0) {
1462
+ seenArrow = true;
1463
+ i++;
1464
+ } else if (ch === "{") {
1465
+ if (!seenArrow && depth === 0) return null;
1466
+ depth++;
1467
+ } else if (ch === "}") {
1468
+ if (depth === 0) return null;
1469
+ depth--;
1470
+ } else if (ch === ";" && depth === 0) {
1471
+ if (!seenArrow) return null;
1472
+ let line = startLine;
1473
+ for (let k = from; k < i; k++) if (content[k] === "\n") line++;
1474
+ return line;
1475
+ }
1476
+ }
1477
+ return null;
1478
+ }
1479
+ function skipKotlinTemplateHole(content, i, to) {
1480
+ let depth = 0;
1481
+ for (let k = i; k < to; k++) {
1482
+ const lit = skipKotlinLiteral(content, k, to);
1483
+ if (lit !== null) {
1484
+ k = lit - 1;
1485
+ continue;
1486
+ }
1487
+ const ch = content[k];
1488
+ if (ch === "{") depth++;
1489
+ else if (ch === "}" && --depth === 0) return k + 1;
1490
+ }
1491
+ return to;
1492
+ }
1493
+ function skipKotlinLiteral(content, i, to) {
1494
+ const first = content[i];
1495
+ if (first === "'") {
1496
+ for (let k = i + 1; k < to && content[k] !== "\n"; k++) {
1497
+ if (content[k] === "\\") {
1498
+ k++;
1499
+ continue;
1500
+ }
1501
+ if (content[k] === "'") return k + 1;
1502
+ }
1503
+ return null;
1504
+ }
1505
+ if (first !== '"') return null;
1506
+ const triple = content.startsWith('"""', i);
1507
+ for (let k = i + (triple ? 3 : 1); k < to; k++) {
1508
+ const ch = content[k];
1509
+ if (triple) {
1510
+ if (ch === '"' && content.startsWith('"""', k)) {
1511
+ k += 3;
1512
+ while (content[k] === '"') k++;
1513
+ return k;
1514
+ }
1515
+ } else if (ch === "\\") {
1516
+ k++;
1517
+ continue;
1518
+ } else if (ch === '"') return k + 1;
1519
+ else if (ch === "\n") return k;
1520
+ if (ch === "$" && content[k + 1] === "{") k = skipKotlinTemplateHole(content, k + 1, to) - 1;
1521
+ }
1522
+ return to;
1523
+ }
1524
+ function opensInterpolatedString(content, i, lang) {
1525
+ const q = content[i];
1526
+ const prev = content[i - 1] ?? "";
1527
+ if (lang === "csharp") return q === '"' && (prev === "$" || prev === "@" && content[i - 2] === "$");
1528
+ if (lang === "scala") return q === '"' && /[A-Za-z0-9_]/.test(prev);
1529
+ if (lang === "dart") return !(prev === "r" && !/[A-Za-z0-9_$]/.test(content[i - 2] ?? ""));
1530
+ return q === '"';
1531
+ }
1532
+ function skipInterpolationAt(content, i, to, lang) {
1533
+ const ch = content[i];
1534
+ if (lang === "swift") return ch === "\\" && content[i + 1] === "(" ? skipInterpolationHole(content, i + 1, to, lang) : null;
1535
+ if (lang === "csharp") {
1536
+ if (ch !== "{") return null;
1537
+ return content[i + 1] === "{" ? i + 2 : skipInterpolationHole(content, i, to, lang);
1538
+ }
1539
+ return ch === "$" && content[i + 1] === "{" ? skipInterpolationHole(content, i + 1, to, lang) : null;
1540
+ }
1541
+ function skipInterpolationHole(content, i, to, lang) {
1542
+ const open = content[i];
1543
+ const close = open === "(" ? ")" : "}";
1544
+ let depth = 0;
1545
+ for (let k = i; k < to; k++) {
1546
+ const lit = skipNestedLiteral(content, k, to, lang);
1547
+ if (lit !== null) {
1548
+ k = lit - 1;
1549
+ continue;
1550
+ }
1551
+ const ch = content[k];
1552
+ if (ch === open) depth++;
1553
+ else if (ch === close && --depth === 0) return k + 1;
1554
+ }
1555
+ return to;
1556
+ }
1557
+ function skipNestedLiteral(content, i, to, lang) {
1558
+ const ch = content[i];
1559
+ if (lang === "csharp") return skipCsharpLiteral(content, i, to);
1560
+ if (lang === "kotlin") return skipKotlinLiteral(content, i, to);
1561
+ if (ch === "'" && lang === "scala") {
1562
+ const len = scalaCharLiteralLength(content, i);
1563
+ return len > 0 ? i + len : null;
1564
+ }
1565
+ if (ch !== '"' && !(ch === "'" && lang === "dart")) return null;
1566
+ return skipInterpolatedString(content, i, to, lang, opensInterpolatedString(content, i, lang));
1567
+ }
1568
+ function skipInterpolatedString(content, i, to, lang, interpolated) {
1569
+ const q = content[i];
1570
+ if (content.startsWith(q + q + q, i)) {
1571
+ const end = content.indexOf(q + q + q, i + 3);
1572
+ return end === -1 || end >= to ? to : end + 3;
1573
+ }
1574
+ for (let k = i + 1; k < to; k++) {
1575
+ const ch = content[k];
1576
+ if (ch === "\n") return k;
1577
+ if (interpolated) {
1578
+ const past = skipInterpolationAt(content, k, to, lang);
1579
+ if (past !== null) {
1580
+ k = past - 1;
1581
+ continue;
1582
+ }
1583
+ }
1584
+ if (ch === "\\") {
1585
+ k++;
1586
+ continue;
1587
+ }
1588
+ if (ch === q) return k + 1;
1589
+ }
1590
+ return to;
1591
+ }
1592
+ function kotlinBodyContinues(content, lastSig, ifParenClosedAt, nextLineFrom) {
1593
+ const c = content[lastSig];
1594
+ const prev = content[lastSig - 1];
1595
+ if (lastSig === ifParenClosedAt) return true;
1596
+ if (c !== void 0 && "=*/%&|,.:".includes(c)) return true;
1597
+ if ((c === "+" || c === "-") && prev !== c) return true;
1598
+ if (c === ">" && prev === "-") return true;
1599
+ if (/(?:^|[^A-Za-z0-9_])else$/.test(content.slice(Math.max(0, lastSig - 4), lastSig + 1))) return true;
1600
+ const rest = content.slice(nextLineFrom);
1601
+ const next = /^(?:[ \t\r]*(?:\/\/[^\n]*)?\n)*[ \t]*(\.|\?\.|\?:|&&|\|\||else\b|as\b|catch\b|finally\b)/.exec(rest);
1602
+ return next !== null;
1603
+ }
1604
+ function findKotlinEqualsBodyEndLine(content, lineIndex, startLine, lastLine) {
1605
+ const from = lineIndex[startLine - 1];
1606
+ if (from === void 0) return null;
1607
+ const to = lineIndex[lastLine] ?? content.length;
1608
+ let depth = 0;
1609
+ let seenEq = false;
1610
+ let line = startLine;
1611
+ let lastSig = -1;
1612
+ let ifParenClosedAt = -1;
1613
+ const ifParens = [];
1614
+ for (let i = from; i < to; i++) {
1615
+ const ch = content[i];
1616
+ if (ch === void 0) break;
1617
+ if (ch === "\n") {
1618
+ if (depth === 0) {
1619
+ if (!seenEq) return null;
1620
+ if (!kotlinBodyContinues(content, lastSig, ifParenClosedAt, i + 1)) return line;
1621
+ }
1622
+ line++;
1623
+ continue;
1624
+ }
1625
+ if (ch === " " || ch === " " || ch === "\r") continue;
1626
+ if (ch === "/" && content[i + 1] === "/") {
1627
+ while (i + 1 < to && content[i + 1] !== "\n") i++;
1628
+ continue;
1629
+ }
1630
+ if (ch === "/" && content[i + 1] === "*") {
1631
+ const end = content.indexOf("*/", i + 2);
1632
+ if (end === -1) return null;
1633
+ for (let k = i; k < end; k++) if (content[k] === "\n") line++;
1634
+ i = end + 1;
1635
+ continue;
1636
+ }
1637
+ const lit = skipKotlinLiteral(content, i, to);
1638
+ if (lit !== null) {
1639
+ for (let k = i; k < lit; k++) if (content[k] === "\n") line++;
1640
+ lastSig = lit - 1;
1641
+ i = lit - 1;
1642
+ continue;
1643
+ }
1644
+ lastSig = i;
1645
+ if (ch === "(" || ch === "[" || ch === "{") {
1646
+ if (ch === "{" && !seenEq && depth === 0) return null;
1647
+ ifParens.push(ch === "(" && /\bif\s*$/.test(content.slice(Math.max(from, i - 8), i)));
1648
+ depth++;
1649
+ } else if (ch === ")" || ch === "]" || ch === "}") {
1650
+ if (depth === 0) return null;
1651
+ depth--;
1652
+ if (ifParens.pop() === true) ifParenClosedAt = i;
1653
+ } else if (ch === "=" && !seenEq && depth === 0 && !"=!<>".includes(content[i - 1] ?? " ") && content[i + 1] !== "=") {
1654
+ seenEq = true;
1655
+ }
1656
+ }
1657
+ return seenEq && depth === 0 ? line : null;
1658
+ }
1307
1659
  function maskMultilineStrings(content, lang) {
1308
1660
  let state = null;
1309
1661
  const parts = [];
@@ -1333,6 +1685,8 @@ function findBlockOpenBrace(content, lineIndex, startLine, lastSearchLine, lineC
1333
1685
  const from = lineIndex[startLine - 1];
1334
1686
  if (from === void 0) return null;
1335
1687
  const to = lineIndex[lastSearchLine] ?? content.length;
1688
+ const interpolation = opts?.interpolation;
1689
+ let interpolated = false;
1336
1690
  let quote = null;
1337
1691
  let parenDepth = 0;
1338
1692
  let linesSeen = 0;
@@ -1342,6 +1696,13 @@ function findBlockOpenBrace(content, lineIndex, startLine, lastSearchLine, lineC
1342
1696
  const ch = content[i];
1343
1697
  if (ch === void 0) break;
1344
1698
  if (quote !== null) {
1699
+ if (interpolated && interpolation !== void 0) {
1700
+ const past = skipInterpolationAt(content, i, to, interpolation);
1701
+ if (past !== null) {
1702
+ i = past - 1;
1703
+ continue;
1704
+ }
1705
+ }
1345
1706
  if (verbatim) {
1346
1707
  if (ch === quote) {
1347
1708
  if (content[i + 1] === quote) {
@@ -1408,6 +1769,7 @@ function findBlockOpenBrace(content, lineIndex, startLine, lastSearchLine, lineC
1408
1769
  if (ch === '"' || ch === "'") {
1409
1770
  quote = ch;
1410
1771
  verbatim = stringEscapes === "csharp" && ch === '"' && opensCsharpVerbatimString(content, i);
1772
+ interpolated = interpolation !== void 0 && opensInterpolatedString(content, i, interpolation);
1411
1773
  continue;
1412
1774
  }
1413
1775
  if (ch === "(" || ch === "[") parenDepth++;
@@ -1972,7 +2334,7 @@ CREATE INDEX IF NOT EXISTS idx_symbols_file ON symbols(file_path);
1972
2334
  CREATE INDEX IF NOT EXISTS idx_symbols_name_kind ON symbols(name, kind);
1973
2335
  CREATE INDEX IF NOT EXISTS idx_symbols_file_folded ON symbols(TG_LOWER(file_path));
1974
2336
  CREATE INDEX IF NOT EXISTS idx_symbols_file_name_folded ON symbols(TG_LOWER(file_path), name);
1975
- -- Partial index backing checkSymbolBodySize (cli_doctor.ts), which every SessionStart hook runs.
2337
+ -- Partial index backing checkSymbolBodySize (symbol_body_probe.ts), which every SessionStart hook runs.
1976
2338
  -- Its predicate cannot be served by any index above, so the check had to read the whole symbols
1977
2339
  -- table -- 226 MB / 231324 rows here, 229 ms per session start, and the early-exit LIMIT 1 never
1978
2340
  -- fires on a healthy index because there is nothing to find. Indexing the *violating* rows only
@@ -1980,7 +2342,7 @@ CREATE INDEX IF NOT EXISTS idx_symbols_file_name_folded ON symbols(TG_LOWER(file
1980
2342
  -- ms, 4 KB on disk, and no measurable insert cost (-0.2%, within noise, over 40000 real rows),
1981
2343
  -- because SQLite evaluates the predicate and skips the b-tree write for every row under the cap.
1982
2344
  -- SQLite uses a partial index only where the query's WHERE implies the index's, so the probe in
1983
- -- cli_doctor.ts spells its comparison the same way and against the same constant. That makes the
2345
+ -- symbol_body_probe.ts spells its comparison the same way and against the same constant. That makes the
1984
2346
  -- threshold part of the stored schema -- see SYMBOL_BODY_CHAR_CAP in constants.ts for what
1985
2347
  -- changing it requires. A query with a lower threshold correctly gets a full scan instead, so no
1986
2348
  -- other reader can be served stale rows by this index.
@@ -2076,7 +2438,11 @@ CREATE INDEX IF NOT EXISTS idx_cache_recall_type ON cache_recall(cache_type);
2076
2438
  -- picked and averaging fabrications into a measured rate is how 174 of bash_redirect's 698 rows
2077
2439
  -- came to hold its efficacy down to 0.86% when the observed figure was 1.15%. calls_remaining is
2078
2440
  -- the countdown of subsequent tool-use events still eligible to resolve this row before it is
2079
- -- considered timed out. displayed=0 marks a detection that never reached the agent -- auto-
2441
+ -- considered timed out. A displayed, observable row with resolved=0 is pending: nothing has
2442
+ -- scored it yet. Nothing sweeps these on a clock, so a session that ends mid-window leaves its
2443
+ -- rows pending for good, and efficacy and suppression count only resolved rows -- counting a
2444
+ -- pending one as not acted on is what muted bash_redirect at 1 in 11 when its scored rows stood
2445
+ -- at 1 in 6. displayed=0 marks a detection that never reached the agent -- auto-
2080
2446
  -- suppressed, or declined by a hint's own net-benefit gate -- recorded so the ledger can answer
2081
2447
  -- how often a hint fired at all, which it previously could not: a suppressed detection left no
2082
2448
  -- trace, so a fully-muted category was indistinguishable from one that had stopped triggering.
@@ -2313,7 +2679,7 @@ function runMigrations(conn, fromVersion, toVersion) {
2313
2679
  }
2314
2680
  }
2315
2681
  var WAL_SWITCH_DEADLINE_MS = 15e3;
2316
- function enableWalWithRetry(conn, budgetMs = WAL_SWITCH_DEADLINE_MS) {
2682
+ function enableWalWithRetry(conn, budgetMs = WAL_SWITCH_DEADLINE_MS, stopOn) {
2317
2683
  const deadline = Date.now() + budgetMs;
2318
2684
  let lastError;
2319
2685
  for (; ; ) {
@@ -2323,55 +2689,147 @@ function enableWalWithRetry(conn, budgetMs = WAL_SWITCH_DEADLINE_MS) {
2323
2689
  lastError = new Error(`got: ${String(mode)}`);
2324
2690
  } catch (e) {
2325
2691
  lastError = e;
2692
+ if (isDamagedDatabaseError(e) || stopOn?.(e) === true) throw walSwitchError(e);
2326
2693
  }
2327
2694
  try {
2328
2695
  if (String(conn.pragma("journal_mode", { simple: true })).toLowerCase() === "wal") return;
2329
2696
  } catch {
2330
2697
  }
2331
- if (Date.now() >= deadline) {
2332
- throw new Error(`db: failed to enable WAL mode (${lastError instanceof Error ? lastError.message : String(lastError)})`);
2333
- }
2698
+ if (Date.now() >= deadline) throw walSwitchError(lastError);
2334
2699
  sleepSync(25);
2335
2700
  }
2336
2701
  }
2337
- function initConnection(conn) {
2702
+ function isDamagedDatabaseError(e) {
2703
+ const code = e?.code;
2704
+ return typeof code === "string" && /^SQLITE_(?:NOTADB|CORRUPT)/.test(code);
2705
+ }
2706
+ function walSwitchError(lastError) {
2707
+ return new Error(`db: failed to enable WAL mode (${lastError instanceof Error ? lastError.message : String(lastError)})`, { cause: lastError });
2708
+ }
2709
+ function initConnection(conn, walStopOn) {
2338
2710
  conn.pragma("busy_timeout = 15000");
2339
- enableWalWithRetry(conn);
2711
+ enableWalWithRetry(conn, WAL_SWITCH_DEADLINE_MS, walStopOn);
2340
2712
  conn.pragma("synchronous = NORMAL");
2341
2713
  conn.pragma("cache_size = -32000");
2342
2714
  conn.pragma("temp_store = MEMORY");
2343
2715
  conn.pragma("mmap_size = 134217728");
2344
- conn.function(
2345
- "TG_LOWER",
2346
- { deterministic: true },
2347
- (value) => value === null ? null : foldCase(String(value))
2348
- );
2716
+ registerTgLower(conn);
2349
2717
  const storedVersion = Number(conn.pragma("user_version", { simple: true }));
2350
- if (storedVersion > SCHEMA_VERSION) {
2351
- throw new Error(
2352
- `db: index schema version ${storedVersion} is newer than this token-goat build supports (expected ${SCHEMA_VERSION}). Update token-goat, or delete the stale index database and let it rebuild.`
2353
- );
2354
- }
2718
+ if (storedVersion > SCHEMA_VERSION) throw newerSchemaError(storedVersion);
2355
2719
  conn.exec(SCHEMA_SQL);
2356
2720
  try {
2357
2721
  conn.exec(FTS_SQL);
2358
2722
  } catch {
2359
2723
  }
2360
- try {
2361
- const sqliteVec = _require("sqlite-vec");
2362
- sqliteVec.load(conn);
2363
- conn.exec(
2364
- `CREATE VIRTUAL TABLE IF NOT EXISTS chunk_vectors USING vec0(
2724
+ if (loadSqliteVec(conn)) {
2725
+ try {
2726
+ conn.exec(
2727
+ `CREATE VIRTUAL TABLE IF NOT EXISTS chunk_vectors USING vec0(
2365
2728
  embedding float[384]
2366
2729
  );`
2367
- );
2368
- } catch {
2730
+ );
2731
+ } catch {
2732
+ }
2369
2733
  }
2370
2734
  if (storedVersion < SCHEMA_VERSION) {
2371
2735
  runMigrations(conn, storedVersion, SCHEMA_VERSION);
2372
2736
  conn.pragma(`user_version = ${SCHEMA_VERSION}`);
2373
2737
  }
2374
2738
  }
2739
+ function newerSchemaError(storedVersion) {
2740
+ return new Error(
2741
+ `db: index schema version ${storedVersion} is newer than this token-goat build supports (expected ${SCHEMA_VERSION}). Update token-goat, or delete the stale index database and let it rebuild.`
2742
+ );
2743
+ }
2744
+ function registerTgLower(conn) {
2745
+ conn.function(
2746
+ "TG_LOWER",
2747
+ { deterministic: true },
2748
+ (value) => value === null ? null : foldCase(String(value))
2749
+ );
2750
+ }
2751
+ function loadSqliteVec(conn) {
2752
+ try {
2753
+ const sqliteVec = _require("sqlite-vec");
2754
+ sqliteVec.load(conn);
2755
+ return true;
2756
+ } catch {
2757
+ return false;
2758
+ }
2759
+ }
2760
+ var _readOnlyFallback;
2761
+ var _readOnlyConnections = /* @__PURE__ */ new Map();
2762
+ function allowReadOnlyIndex(onFirstUse) {
2763
+ _readOnlyFallback = { onFirstUse, used: false };
2764
+ }
2765
+ function isReadOnlyDb(dbPath) {
2766
+ if (_readOnlyFallback === void 0) return false;
2767
+ try {
2768
+ getDb(dbPath);
2769
+ } catch {
2770
+ return false;
2771
+ }
2772
+ return _readOnlyConnections.has(connectionKey(dbPath).key);
2773
+ }
2774
+ var WRITE_ACCESS_SQLITE_CODE_RE = /^SQLITE_(?:CANTOPEN|READONLY|PERM|IOERR_ACCESS|IOERR_SHMOPEN)/;
2775
+ var WRITE_ACCESS_FS_CODES = /* @__PURE__ */ new Set(["EACCES", "EPERM", "EROFS"]);
2776
+ function isWriteAccessError(e) {
2777
+ for (let cur = e, depth = 0; cur instanceof Error && depth < 4; cur = cur.cause, depth++) {
2778
+ const code = cur.code;
2779
+ if (typeof code === "string" && (WRITE_ACCESS_SQLITE_CODE_RE.test(code) || WRITE_ACCESS_FS_CODES.has(code))) return true;
2780
+ }
2781
+ return false;
2782
+ }
2783
+ function writeRefusal(resolved) {
2784
+ let fd;
2785
+ try {
2786
+ fd = fs2.openSync(resolved, "r+");
2787
+ } catch (e) {
2788
+ return isWriteAccessError(e) ? e : void 0;
2789
+ }
2790
+ fs2.closeSync(fd);
2791
+ return void 0;
2792
+ }
2793
+ function openReadOnlyAt(resolved, immutable) {
2794
+ const conn = new Database(resolved, { readonly: true, fileMustExist: true, immutable });
2795
+ try {
2796
+ conn.pragma("busy_timeout = 15000");
2797
+ return { conn, storedVersion: Number(conn.pragma("user_version", { simple: true })) };
2798
+ } catch (e) {
2799
+ try {
2800
+ conn.close();
2801
+ } catch {
2802
+ }
2803
+ throw e;
2804
+ }
2805
+ }
2806
+ function openIndexReadOnly(resolved) {
2807
+ let opened;
2808
+ try {
2809
+ opened = openReadOnlyAt(resolved, false);
2810
+ } catch (e) {
2811
+ if (!isWriteAccessError(e)) throw e;
2812
+ opened = openReadOnlyAt(resolved, true);
2813
+ }
2814
+ const { conn, storedVersion } = opened;
2815
+ try {
2816
+ if (storedVersion > SCHEMA_VERSION) throw newerSchemaError(storedVersion);
2817
+ if (storedVersion < SCHEMA_VERSION) {
2818
+ throw new Error(
2819
+ `db: index schema version ${storedVersion} predates this token-goat build (expected ${SCHEMA_VERSION}) and cannot be upgraded without write access. Run token-goat once where its data directory is writable.`
2820
+ );
2821
+ }
2822
+ registerTgLower(conn);
2823
+ loadSqliteVec(conn);
2824
+ } catch (e) {
2825
+ try {
2826
+ conn.close();
2827
+ } catch {
2828
+ }
2829
+ throw e;
2830
+ }
2831
+ return conn;
2832
+ }
2375
2833
  function resolveDbPath(dbPath) {
2376
2834
  if (path2.isAbsolute(dbPath)) return dbPath;
2377
2835
  if (dbPath.includes("/") || dbPath.includes("\\")) return path2.resolve(dbPath);
@@ -2385,35 +2843,75 @@ function getDb(dbPath) {
2385
2843
  const { resolved, key } = connectionKey(dbPath);
2386
2844
  const existing = _connections.get(key);
2387
2845
  if (existing !== void 0) return existing;
2846
+ const fallback = _readOnlyFallback;
2847
+ if (fallback !== void 0) {
2848
+ const readOnly = _readOnlyConnections.get(key);
2849
+ if (readOnly !== void 0) return readOnly;
2850
+ const refused = writeRefusal(resolved);
2851
+ if (refused !== void 0) return openReadOnlyFallback(resolved, key, fallback, refused);
2852
+ }
2388
2853
  const dir = path2.dirname(resolved);
2389
2854
  try {
2390
2855
  ensureDirSync(dir);
2391
2856
  } catch (e) {
2392
2857
  if (e.code !== "EEXIST" || !fs2.existsSync(dir)) throw e;
2393
2858
  }
2394
- const conn = new Database(resolved);
2859
+ let conn;
2395
2860
  try {
2396
- initConnection(conn);
2861
+ conn = new Database(resolved);
2862
+ initConnection(conn, fallback !== void 0 ? isWriteAccessError : void 0);
2397
2863
  } catch (e) {
2398
2864
  try {
2399
- conn.close();
2865
+ conn?.close();
2400
2866
  } catch {
2401
2867
  }
2868
+ if (fallback !== void 0 && isWriteAccessError(e)) return openReadOnlyFallback(resolved, key, fallback, e);
2402
2869
  throw e;
2403
2870
  }
2404
2871
  _connections.set(key, conn);
2405
2872
  return conn;
2406
2873
  }
2407
- function closeAllDbs() {
2408
- for (const conn of _connections.values()) {
2874
+ function openReadOnlyFallback(resolved, key, fallback, writeError) {
2875
+ let conn;
2876
+ try {
2877
+ conn = openIndexReadOnly(resolved);
2878
+ } catch (e) {
2879
+ throw isWriteAccessError(e) || !fs2.existsSync(resolved) ? writeError : e;
2880
+ }
2881
+ _readOnlyConnections.set(key, conn);
2882
+ if (!fallback.used) {
2883
+ fallback.used = true;
2884
+ fallback.onFirstUse();
2885
+ }
2886
+ return conn;
2887
+ }
2888
+ function closeDb(dbPath) {
2889
+ const { key } = connectionKey(dbPath);
2890
+ for (const cache of [_connections, _readOnlyConnections]) {
2891
+ const conn = cache.get(key);
2892
+ if (conn === void 0) continue;
2409
2893
  try {
2410
2894
  conn.close();
2411
2895
  } catch {
2412
2896
  }
2897
+ cache.delete(key);
2898
+ }
2899
+ }
2900
+ function closeAllDbs() {
2901
+ for (const cache of [_connections, _readOnlyConnections]) {
2902
+ for (const conn of cache.values()) {
2903
+ try {
2904
+ conn.close();
2905
+ } catch {
2906
+ }
2907
+ }
2908
+ cache.clear();
2413
2909
  }
2414
- _connections.clear();
2415
2910
  }
2416
- registerReset(closeAllDbs);
2911
+ registerReset(closeAllDbs, { perRequest: true });
2912
+ registerReset(() => {
2913
+ _readOnlyFallback = void 0;
2914
+ }, { perRequest: true });
2417
2915
 
2418
2916
  export {
2419
2917
  precedingDocComment,
@@ -2470,5 +2968,8 @@ export {
2470
2968
  unsupportedLanguageName,
2471
2969
  isDotenvPath,
2472
2970
  redactIfDotenv,
2473
- getDb
2971
+ allowReadOnlyIndex,
2972
+ isReadOnlyDb,
2973
+ getDb,
2974
+ closeDb
2474
2975
  };