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