@theokit/sdk 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/a2a/index.cjs +232 -171
  3. package/dist/a2a/index.cjs.map +1 -1
  4. package/dist/a2a/index.js +232 -171
  5. package/dist/a2a/index.js.map +1 -1
  6. package/dist/concurrency.cjs +86 -0
  7. package/dist/concurrency.cjs.map +1 -0
  8. package/dist/concurrency.d.cts +13 -0
  9. package/dist/concurrency.d.ts +13 -0
  10. package/dist/concurrency.js +83 -0
  11. package/dist/concurrency.js.map +1 -0
  12. package/dist/{cron-DFG9-W17.d.cts → cron-CSTqNZp9.d.cts} +1 -1
  13. package/dist/{cron-Bj8-Aq1O.d.ts → cron-Da6vF_2y.d.ts} +1 -1
  14. package/dist/cron.cjs +213 -169
  15. package/dist/cron.cjs.map +1 -1
  16. package/dist/cron.d.cts +1 -1
  17. package/dist/cron.d.ts +1 -1
  18. package/dist/cron.js +213 -169
  19. package/dist/cron.js.map +1 -1
  20. package/dist/{errors-DV9e0rcp.d.ts → errors--VP2qrGc.d.ts} +23 -1
  21. package/dist/{errors-ChqOmFH1.d.cts → errors-C9xkhNEF.d.cts} +23 -1
  22. package/dist/errors.cjs +17 -11
  23. package/dist/errors.cjs.map +1 -1
  24. package/dist/errors.d.cts +1 -1
  25. package/dist/errors.d.ts +22 -0
  26. package/dist/errors.js +17 -12
  27. package/dist/errors.js.map +1 -1
  28. package/dist/eval.cjs +213 -169
  29. package/dist/eval.cjs.map +1 -1
  30. package/dist/eval.js +213 -169
  31. package/dist/eval.js.map +1 -1
  32. package/dist/index.cjs +231 -171
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +161 -119
  35. package/dist/index.d.ts +161 -119
  36. package/dist/index.js +231 -173
  37. package/dist/index.js.map +1 -1
  38. package/dist/internal/default-retriable.d.ts +1 -0
  39. package/dist/internal/persistence/index.cjs +75 -0
  40. package/dist/internal/persistence/index.cjs.map +1 -1
  41. package/dist/internal/persistence/index.d.cts +2 -0
  42. package/dist/internal/persistence/index.d.ts +2 -0
  43. package/dist/internal/persistence/index.js +74 -1
  44. package/dist/internal/persistence/index.js.map +1 -1
  45. package/dist/internal/persistence/sqlite-open.d.cts +47 -0
  46. package/dist/internal/persistence/sqlite-open.d.ts +47 -0
  47. package/dist/internal/providers/register-plugin-providers.d.ts +22 -0
  48. package/dist/internal/runtime/concurrency/map-with-concurrency.d.ts +28 -0
  49. package/dist/internal/runtime/retry/with-retry.d.ts +40 -0
  50. package/dist/internal/security/index.cjs +1 -0
  51. package/dist/internal/security/index.cjs.map +1 -1
  52. package/dist/internal/security/index.js +1 -0
  53. package/dist/internal/security/index.js.map +1 -1
  54. package/dist/path-safety.cjs +15 -0
  55. package/dist/path-safety.cjs.map +1 -1
  56. package/dist/path-safety.d.cts +1 -1
  57. package/dist/path-safety.d.ts +1 -1
  58. package/dist/path-safety.js +15 -1
  59. package/dist/path-safety.js.map +1 -1
  60. package/dist/retry.cjs +85 -0
  61. package/dist/retry.cjs.map +1 -0
  62. package/dist/retry.d.cts +9 -0
  63. package/dist/retry.d.ts +9 -0
  64. package/dist/retry.js +83 -0
  65. package/dist/retry.js.map +1 -0
  66. package/dist/server/errors-envelope.cjs +14 -12
  67. package/dist/server/errors-envelope.cjs.map +1 -1
  68. package/dist/server/errors-envelope.js +14 -12
  69. package/dist/server/errors-envelope.js.map +1 -1
  70. package/dist/subscription/index.cjs.map +1 -1
  71. package/dist/subscription/index.js.map +1 -1
  72. package/dist/task-store.cjs.map +1 -1
  73. package/dist/task-store.js.map +1 -1
  74. package/dist/workflow.cjs.map +1 -1
  75. package/dist/workflow.js.map +1 -1
  76. package/package.json +21 -1
package/dist/cron.d.cts CHANGED
@@ -1,2 +1,2 @@
1
1
  import './run-DrwUpFxZ.cjs';
2
- export { I as Cron } from './cron-DFG9-W17.cjs';
2
+ export { I as Cron } from './cron-CSTqNZp9.cjs';
package/dist/cron.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  import './run-DrwUpFxZ.js';
2
- export { I as Cron } from './cron-Bj8-Aq1O.js';
2
+ export { I as Cron } from './cron-Da6vF_2y.js';
package/dist/cron.js CHANGED
@@ -20,6 +20,24 @@ var __export = (target, all) => {
20
20
  __defProp(target, name, { get: all[name], enumerable: true });
21
21
  };
22
22
 
23
+ // src/internal/default-retriable.ts
24
+ function defaultRetriableForCode(code) {
25
+ switch (code) {
26
+ case "rate_limit":
27
+ case "timeout":
28
+ case "server_error":
29
+ case "network":
30
+ case "provider_unreachable":
31
+ return true;
32
+ default:
33
+ return false;
34
+ }
35
+ }
36
+ var init_default_retriable = __esm({
37
+ "src/internal/default-retriable.ts"() {
38
+ }
39
+ });
40
+
23
41
  // src/internal/security/redact.ts
24
42
  function readEnvOnce() {
25
43
  const raw = process.env.THEOKIT_REDACT_SECRETS;
@@ -170,7 +188,8 @@ __export(errors_exports, {
170
188
  UnsupportedBudgetOperationError: () => UnsupportedBudgetOperationError,
171
189
  UnsupportedRunOperationError: () => UnsupportedRunOperationError,
172
190
  UnsupportedTaskOperationError: () => UnsupportedTaskOperationError,
173
- coerceToKnownAgentRunErrorCode: () => coerceToKnownAgentRunErrorCode
191
+ coerceToKnownAgentRunErrorCode: () => coerceToKnownAgentRunErrorCode,
192
+ isTransientError: () => isTransientError
174
193
  });
175
194
  function coerceToKnownAgentRunErrorCode(code) {
176
195
  if (code !== void 0 && KNOWN_AGENT_RUN_ERROR_CODES.has(code)) {
@@ -202,21 +221,13 @@ function safeStringify(value) {
202
221
  return String(value);
203
222
  }
204
223
  }
205
- function defaultRetriableForCode(code) {
206
- switch (code) {
207
- case "rate_limit":
208
- case "timeout":
209
- case "server_error":
210
- case "network":
211
- case "provider_unreachable":
212
- return true;
213
- default:
214
- return false;
215
- }
224
+ function isTransientError(err) {
225
+ return err instanceof TheokitAgentError && err.isRetryable === true;
216
226
  }
217
227
  var KNOWN_AGENT_RUN_ERROR_CODES, TheokitAgentError, AuthenticationError, RateLimitError, ConfigurationError, IntegrationNotConnectedError, NetworkError, UnknownAgentError, AgentRunError, UnsupportedRunOperationError, CredentialPoolExhaustedError, MemoryAdapterError, InvalidTaskIdError, TaskNotFoundError, UnsupportedTaskOperationError, BudgetExceededError, AgentDisposedError, UnsupportedBudgetOperationError;
218
228
  var init_errors = __esm({
219
229
  "src/errors.ts"() {
230
+ init_default_retriable();
220
231
  init_redact();
221
232
  KNOWN_AGENT_RUN_ERROR_CODES = /* @__PURE__ */ new Set([
222
233
  "rate_limit",
@@ -815,6 +826,49 @@ var init_async_local_storage = __esm({
815
826
  }
816
827
  });
817
828
 
829
+ // src/internal/runtime/concurrency/async-semaphore.ts
830
+ function createSemaphore(permits) {
831
+ if (!Number.isInteger(permits) || permits < 1) {
832
+ throw new ConfigurationError(
833
+ `async-semaphore: permits must be a positive integer, got ${permits}`,
834
+ { code: "invalid_concurrency" }
835
+ );
836
+ }
837
+ let active = 0;
838
+ const queue = [];
839
+ function tryGrant() {
840
+ if (active < permits && queue.length > 0) {
841
+ const resolve3 = queue.shift();
842
+ if (resolve3 !== void 0) {
843
+ active += 1;
844
+ resolve3();
845
+ }
846
+ }
847
+ }
848
+ return {
849
+ inFlight: () => active,
850
+ pending: () => queue.length + active,
851
+ async acquire() {
852
+ await new Promise((resolve3) => {
853
+ queue.push(resolve3);
854
+ tryGrant();
855
+ });
856
+ let released = false;
857
+ return () => {
858
+ if (released) return;
859
+ released = true;
860
+ active -= 1;
861
+ tryGrant();
862
+ };
863
+ }
864
+ };
865
+ }
866
+ var init_async_semaphore = __esm({
867
+ "src/internal/runtime/concurrency/async-semaphore.ts"() {
868
+ init_errors();
869
+ }
870
+ });
871
+
818
872
  // src/internal/llm/credential-pool-types.ts
819
873
  var COOLDOWN_MS, DEFAULT_COOLDOWN_MS;
820
874
  var init_credential_pool_types = __esm({
@@ -1466,49 +1520,6 @@ var init_task = __esm({
1466
1520
  }
1467
1521
  });
1468
1522
 
1469
- // src/internal/runtime/concurrency/async-semaphore.ts
1470
- function createSemaphore(permits) {
1471
- if (!Number.isInteger(permits) || permits < 1) {
1472
- throw new ConfigurationError(
1473
- `async-semaphore: permits must be a positive integer, got ${permits}`,
1474
- { code: "invalid_concurrency" }
1475
- );
1476
- }
1477
- let active = 0;
1478
- const queue = [];
1479
- function tryGrant() {
1480
- if (active < permits && queue.length > 0) {
1481
- const resolve3 = queue.shift();
1482
- if (resolve3 !== void 0) {
1483
- active += 1;
1484
- resolve3();
1485
- }
1486
- }
1487
- }
1488
- return {
1489
- inFlight: () => active,
1490
- pending: () => queue.length + active,
1491
- async acquire() {
1492
- await new Promise((resolve3) => {
1493
- queue.push(resolve3);
1494
- tryGrant();
1495
- });
1496
- let released = false;
1497
- return () => {
1498
- if (released) return;
1499
- released = true;
1500
- active -= 1;
1501
- tryGrant();
1502
- };
1503
- }
1504
- };
1505
- }
1506
- var init_async_semaphore = __esm({
1507
- "src/internal/runtime/concurrency/async-semaphore.ts"() {
1508
- init_errors();
1509
- }
1510
- });
1511
-
1512
1523
  // src/internal/task/ring-buffer.ts
1513
1524
  var RingBuffer;
1514
1525
  var init_ring_buffer = __esm({
@@ -3084,6 +3095,19 @@ function sanitizeIdentifier(input, options) {
3084
3095
  }
3085
3096
  return input.toLowerCase();
3086
3097
  }
3098
+ function safeFilenameForId(id, options) {
3099
+ if (id.length === 0) {
3100
+ throw new ConfigurationError("Filename id must be a non-empty string", {
3101
+ code: "invalid_filename_id"
3102
+ });
3103
+ }
3104
+ const maxLen = options?.maxLen;
3105
+ const lower = id.toLowerCase();
3106
+ if (lower.length <= maxLen && IDENTIFIER_PATTERN.test(lower)) {
3107
+ return lower;
3108
+ }
3109
+ return `h-${createHash("sha256").update(id).digest("hex").slice(0, 16)}`;
3110
+ }
3087
3111
 
3088
3112
  // src/internal/runtime/config/default-model.ts
3089
3113
  var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
@@ -5796,10 +5820,7 @@ function sessionsDir(cwd) {
5796
5820
  return join(memoryDir(cwd), "sessions");
5797
5821
  }
5798
5822
  function sessionSummaryPath(cwd, runId) {
5799
- return join(sessionsDir(cwd), `${sanitizeRunId(runId)}.md`);
5800
- }
5801
- function sanitizeRunId(runId) {
5802
- return runId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 128);
5823
+ return join(sessionsDir(cwd), `${safeFilenameForId(runId, { maxLen: 128 })}.md`);
5803
5824
  }
5804
5825
  function truncate(text) {
5805
5826
  if (text.length <= MAX_TURN_CHARS) return text;
@@ -8416,6 +8437,27 @@ async function emitTextDeltaCallback(inputs, text) {
8416
8437
  // src/internal/agent-loop/tool-dispatch.ts
8417
8438
  init_async_local_storage();
8418
8439
 
8440
+ // src/internal/runtime/concurrency/map-with-concurrency.ts
8441
+ init_async_semaphore();
8442
+ var NEVER_ABORT = new AbortController().signal;
8443
+ async function mapWithConcurrency(items, concurrency, fn, options) {
8444
+ const semaphore = createSemaphore(concurrency);
8445
+ const signal = NEVER_ABORT;
8446
+ return Promise.all(
8447
+ items.map(async (item, index) => {
8448
+ const release = await semaphore.acquire();
8449
+ try {
8450
+ if (signal.aborted) {
8451
+ throw signal.reason instanceof Error ? signal.reason : new Error("mapWithConcurrency: aborted");
8452
+ }
8453
+ return await fn(item, index, signal);
8454
+ } finally {
8455
+ release();
8456
+ }
8457
+ })
8458
+ );
8459
+ }
8460
+
8419
8461
  // src/internal/tool-dispatch/repair-middleware.ts
8420
8462
  var DECIMAL_RE = /^-?\d+(\.\d+)?$/;
8421
8463
  function repairToolCall(raw, registry) {
@@ -8590,38 +8632,12 @@ ${result.stderr}`.trim();
8590
8632
  // src/internal/agent-loop/tool-dispatch.ts
8591
8633
  async function dispatchTools(inputs, tools, toolCalls, events) {
8592
8634
  const maxConcurrent = inputs.maxConcurrentTools ?? 4;
8593
- return boundedParallel(
8594
- maxConcurrent,
8635
+ return mapWithConcurrency(
8595
8636
  toolCalls,
8637
+ maxConcurrent,
8596
8638
  (call) => dispatchSingleCall(inputs, tools, call, events)
8597
8639
  );
8598
8640
  }
8599
- async function boundedParallel(max, items, fn) {
8600
- let running = 0;
8601
- const queue = [];
8602
- async function acquire() {
8603
- if (running < max) {
8604
- running++;
8605
- return;
8606
- }
8607
- await new Promise((resolve3) => queue.push(resolve3));
8608
- running++;
8609
- }
8610
- function release() {
8611
- running--;
8612
- if (queue.length > 0) queue.shift()();
8613
- }
8614
- return Promise.all(
8615
- items.map(async (item) => {
8616
- await acquire();
8617
- try {
8618
- return await fn(item);
8619
- } finally {
8620
- release();
8621
- }
8622
- })
8623
- );
8624
- }
8625
8641
  async function dispatchSingleCall(inputs, tools, call, events) {
8626
8642
  const { call: workingCall, repairs } = applyRepairAndExtractCall(tools, call);
8627
8643
  const callId = generateCallId();
@@ -11555,6 +11571,16 @@ function resolveMcpCwd(configCwd) {
11555
11571
  return safePathJoin(process.cwd(), configCwd);
11556
11572
  }
11557
11573
 
11574
+ // src/internal/providers/register-plugin-providers.ts
11575
+ function registerPluginProviderProfiles(entries) {
11576
+ let registered4 = 0;
11577
+ for (const entry of entries) {
11578
+ registerProvider(entry.profile);
11579
+ registered4 += 1;
11580
+ }
11581
+ return registered4;
11582
+ }
11583
+
11558
11584
  // src/internal/tool-registry/personality-filter.ts
11559
11585
  init_hooks_source();
11560
11586
  function applyPersonalityFilter(exposedTools, whitelist, opts) {
@@ -11631,12 +11657,27 @@ function createRealLocalRun(options) {
11631
11657
  registerRun(handle);
11632
11658
  return handle;
11633
11659
  }
11634
- function buildLoopInputs(options, runId, userText) {
11660
+ var pluginProvidersAnnounced = false;
11661
+ function resolveRunProvider(options) {
11635
11662
  registerBuiltins();
11663
+ const profiles = options.pluginManager?.aggregated.providerProfiles ?? [];
11664
+ const registered4 = registerPluginProviderProfiles(profiles);
11665
+ if (registered4 > 0 && !pluginProvidersAnnounced) {
11666
+ pluginProvidersAnnounced = true;
11667
+ const names = profiles.map((e) => e.profile.name).join(", ");
11668
+ process.stderr.write(
11669
+ `[theokit-sdk] registered ${registered4} plugin provider profile(s): ${names}
11670
+ `
11671
+ );
11672
+ }
11636
11673
  const parsedModel = parseModelId(options.model?.id);
11637
11674
  const inferredProvider = parsedModel.provider !== void 0 && getProviderProfile(parsedModel.provider) !== void 0 ? parsedModel.provider : void 0;
11638
11675
  const primary = options.agentOptions.providers?.routes?.[0]?.provider ?? inferredProvider ?? detectPrimaryProvider();
11639
11676
  const effectiveModelId = inferredProvider !== void 0 ? parsedModel.name : options.model?.id ?? "claude-sonnet-4-6";
11677
+ return { primary, effectiveModelId };
11678
+ }
11679
+ function buildLoopInputs(options, runId, userText) {
11680
+ const { primary, effectiveModelId } = resolveRunProvider(options);
11640
11681
  const fallback = options.agentOptions.providers?.fallback;
11641
11682
  const apiKeys = options.agentOptions.providers?.apiKeys;
11642
11683
  const credentialPoolStrategy = options.agentOptions.providers?.credentialPoolStrategy;
@@ -12254,7 +12295,7 @@ async function embedTexts(input) {
12254
12295
  pending
12255
12296
  });
12256
12297
  }
12257
- await runBatches(input, pending, results);
12298
+ await embedInBoundedBatches(input, pending, results);
12258
12299
  return results.map((v) => v ?? new Array(dimension).fill(0));
12259
12300
  }
12260
12301
  function classifyEntry(args) {
@@ -12273,45 +12314,34 @@ function classifyEntry(args) {
12273
12314
  args.pending.push({ index: args.index, text: args.text, key });
12274
12315
  }
12275
12316
  var MAX_CONCURRENT_BATCHES = 3;
12276
- async function runBatches(input, pending, results) {
12317
+ async function embedInBoundedBatches(input, pending, results) {
12277
12318
  const batches = [];
12278
12319
  for (let offset = 0; offset < pending.length; offset += MAX_BATCH) {
12279
12320
  batches.push(pending.slice(offset, offset + MAX_BATCH));
12280
12321
  }
12281
- let running = 0;
12282
- const queue = [];
12283
- await Promise.all(batches.map((batch) => processBatch(input, batch, results, acquire, release)));
12284
- async function acquire() {
12285
- if (running >= MAX_CONCURRENT_BATCHES) await new Promise((r) => queue.push(r));
12286
- running++;
12287
- }
12288
- function release() {
12289
- running--;
12290
- if (queue.length > 0) queue.shift()();
12291
- }
12322
+ await mapWithConcurrency(
12323
+ batches,
12324
+ MAX_CONCURRENT_BATCHES,
12325
+ (batch) => processBatch(input, batch, results)
12326
+ );
12292
12327
  }
12293
- async function processBatch(input, batch, results, acquire, release) {
12294
- await acquire();
12295
- try {
12296
- const vectors = await embedBatch({
12297
- apiKey: input.apiKey,
12298
- baseUrl: input.baseUrl,
12299
- embeddingsPath: input.embeddingsPath,
12300
- model: input.model,
12301
- inputs: batch.map((b) => b.text),
12302
- fetchImpl: input.fetchImpl,
12303
- stats: input.stats,
12304
- providerId: input.providerId
12305
- });
12306
- for (let j = 0; j < batch.length; j++) {
12307
- const slot = batch[j];
12308
- const vector = vectors[j];
12309
- if (slot === void 0 || vector === void 0) continue;
12310
- results[slot.index] = vector;
12311
- input.cache.set(slot.key, vector);
12312
- }
12313
- } finally {
12314
- release();
12328
+ async function processBatch(input, batch, results) {
12329
+ const vectors = await embedBatch({
12330
+ apiKey: input.apiKey,
12331
+ baseUrl: input.baseUrl,
12332
+ embeddingsPath: input.embeddingsPath,
12333
+ model: input.model,
12334
+ inputs: batch.map((b) => b.text),
12335
+ fetchImpl: input.fetchImpl,
12336
+ stats: input.stats,
12337
+ providerId: input.providerId
12338
+ });
12339
+ for (let j = 0; j < batch.length; j++) {
12340
+ const slot = batch[j];
12341
+ const vector = vectors[j];
12342
+ if (slot === void 0 || vector === void 0) continue;
12343
+ results[slot.index] = vector;
12344
+ input.cache.set(slot.key, vector);
12315
12345
  }
12316
12346
  }
12317
12347
  async function embedBatch(opts) {
@@ -12744,7 +12774,7 @@ function sanitizeFts5Query(query) {
12744
12774
  return text.trim();
12745
12775
  }
12746
12776
 
12747
- // src/internal/memory/index-db.ts
12777
+ // src/internal/persistence/sqlite-open.ts
12748
12778
  init_errors();
12749
12779
 
12750
12780
  // src/internal/persistence/sqlite-wal.ts
@@ -12772,6 +12802,57 @@ function logFallback(label, reason) {
12772
12802
  );
12773
12803
  }
12774
12804
 
12805
+ // src/internal/persistence/sqlite-open.ts
12806
+ async function openSqliteResilient(options) {
12807
+ await mkdir(dirname(options.filePath), { recursive: true });
12808
+ try {
12809
+ return await openConcrete(options);
12810
+ } catch (cause) {
12811
+ if (options.recoverCorrupt !== false && isCorruptionError(cause)) {
12812
+ await renameAside(options.filePath, options.label ?? "sqlite");
12813
+ return await openConcrete(options);
12814
+ }
12815
+ throw cause;
12816
+ }
12817
+ }
12818
+ async function openConcrete(options) {
12819
+ const db = await loadDriver(options.filePath);
12820
+ applyWalWithFallback(db, options.label ?? "sqlite");
12821
+ await options.onOpen?.(db);
12822
+ return db;
12823
+ }
12824
+ async function loadDriver(filePath) {
12825
+ try {
12826
+ const mod = await import('better-sqlite3');
12827
+ const Ctor = mod.default ?? mod;
12828
+ if (typeof Ctor !== "function") {
12829
+ throw new Error(`better-sqlite3 export is not a constructor (got ${typeof Ctor})`);
12830
+ }
12831
+ return new Ctor(filePath);
12832
+ } catch (cause) {
12833
+ const message = cause instanceof Error ? cause.message : String(cause);
12834
+ throw new ConfigurationError(
12835
+ `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
12836
+ { code: "sqlite_driver_unavailable", cause }
12837
+ );
12838
+ }
12839
+ }
12840
+ function isCorruptionError(cause) {
12841
+ if (!(cause instanceof Error)) return false;
12842
+ const msg = cause.message.toLowerCase();
12843
+ return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
12844
+ }
12845
+ async function renameAside(filePath, label) {
12846
+ const asidePath = `${filePath}.corrupt-${Date.now()}`;
12847
+ await rename(filePath, asidePath).catch(() => void 0);
12848
+ await rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
12849
+ await rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
12850
+ process.stderr.write(
12851
+ `[theokit-sdk] ${label} database corrupt; renamed aside to ${asidePath} and rebuilt schema
12852
+ `
12853
+ );
12854
+ }
12855
+
12775
12856
  // src/internal/memory/index-schema.ts
12776
12857
  var SCHEMA_STATEMENTS = [
12777
12858
  `CREATE TABLE IF NOT EXISTS files (
@@ -12815,52 +12896,15 @@ var PRAGMA_STATEMENTS = [
12815
12896
 
12816
12897
  // src/internal/memory/index-db.ts
12817
12898
  async function openMemoryDb(opts) {
12818
- await mkdir(dirname(opts.filePath), { recursive: true });
12819
- try {
12820
- return await openConcrete(opts.filePath);
12821
- } catch (cause) {
12822
- if (opts.recoverCorrupt !== false && isCorruptionError(cause)) {
12823
- await renameAside(opts.filePath);
12824
- return await openConcrete(opts.filePath);
12899
+ return openSqliteResilient({
12900
+ filePath: opts.filePath,
12901
+ label: "memory-index",
12902
+ recoverCorrupt: opts.recoverCorrupt,
12903
+ onOpen: (db) => {
12904
+ for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
12905
+ for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
12825
12906
  }
12826
- throw cause;
12827
- }
12828
- }
12829
- async function openConcrete(filePath) {
12830
- const db = await loadDriver(filePath);
12831
- applyWalWithFallback(db, "memory-index");
12832
- for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
12833
- for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
12834
- return db;
12835
- }
12836
- async function loadDriver(filePath) {
12837
- try {
12838
- const mod = await import('better-sqlite3');
12839
- const Ctor = mod.default ?? mod;
12840
- const db = new Ctor(filePath);
12841
- return db;
12842
- } catch (cause) {
12843
- const message = cause instanceof Error ? cause.message : String(cause);
12844
- throw new ConfigurationError(
12845
- `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
12846
- { code: "sqlite_driver_unavailable", cause }
12847
- );
12848
- }
12849
- }
12850
- function isCorruptionError(cause) {
12851
- if (!(cause instanceof Error)) return false;
12852
- const msg = cause.message.toLowerCase();
12853
- return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
12854
- }
12855
- async function renameAside(filePath) {
12856
- const asidePath = `${filePath}.corrupt-${Date.now()}`;
12857
- await rename(filePath, asidePath).catch(() => void 0);
12858
- await rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
12859
- await rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
12860
- process.stderr.write(
12861
- `[theokit-sdk] memory index corrupt; renamed aside to ${asidePath} and rebuilt schema
12862
- `
12863
- );
12907
+ });
12864
12908
  }
12865
12909
  function defaultIndexPath(cwd) {
12866
12910
  return join(cwd, ".theokit", "memory", ".index", "memory.sqlite");