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