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