@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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7d53632: Custom LLM providers via the public Plugin protocol (plan `dev-friendly-custom-provider`).
8
+
9
+ - **Added** `defineProvider(profile, opts?)` — canonical factory (mirrors `defineTool`/`definePlugin`, Inviolable Rule 9) that wraps a data-only `ProviderProfile` into a `kind: "model-provider"` `Plugin`. Register any OpenAI-/Anthropic-compatible endpoint (Groq, Together, Fireworks, DeepInfra, a private gateway) with `Agent.create({ model: { id: "myprov/model" }, plugins: [defineProvider(profile)] })`, routed via the `provider/model` id prefix. Exported from `@theokit/sdk`.
10
+ - **Fixed** half-wired `kind: "model-provider"` plugin path: `PluginManager` aggregated provider profiles but nothing registered them, so `getProviderProfile`/`resolveProviderChain` never saw a plugin-contributed provider — a programmatic `model-provider` plugin was silently dropped. The local-agent run now registers plugin-contributed profiles before provider-chain resolution, so custom providers actually route.
11
+ - **Docs** new "Custom providers (`defineProvider`)" section in `docs.md` (field reference + `apiMode` table) and a worked `examples/custom-provider/`.
12
+
13
+ - 872c89e: M0 Foundation — expose already-existing internal primitives as public surfaces (plan `m0-foundation-expose-primitives`), so agent/code-assistant builders reuse battle-tested plumbing instead of re-implementing it.
14
+
15
+ - `isTransientError(err)` — public retryability predicate delegating to `TheokitAgentError.isRetryable` (T1.1).
16
+ - `safeFilenameForId(id, { maxLen })` — total id→filename helper via `@theokit/sdk/path-safety` (passthrough when safe, deterministic sha256 token otherwise); `sanitizeRunId` migrated to it (T2.1).
17
+ - `@theokit/sdk/concurrency` — public `createSemaphore` + new ordered, fail-fast `mapWithConcurrency`; two internal pooling clones deduplicated onto it (T3.1).
18
+ - `@theokit/sdk/retry` — generic `withRetry(fn, options)` (exponential backoff + full jitter, injectable sleep/rng, default `isRetryable = isTransientError`) (T4.1).
19
+ - `openSqliteResilient` (`@theokit/sdk/internal/persistence`, semver-exempt) — shared driver-load + WAL + corruption-recovery; both memory `index-db` copies deduplicated onto it (T5.1).
20
+
3
21
  ## 2.0.1
4
22
 
5
23
  ### Patch Changes
@@ -132,6 +132,24 @@ var init_agent_builder = __esm({
132
132
  }
133
133
  });
134
134
 
135
+ // src/internal/default-retriable.ts
136
+ function defaultRetriableForCode(code) {
137
+ switch (code) {
138
+ case "rate_limit":
139
+ case "timeout":
140
+ case "server_error":
141
+ case "network":
142
+ case "provider_unreachable":
143
+ return true;
144
+ default:
145
+ return false;
146
+ }
147
+ }
148
+ var init_default_retriable = __esm({
149
+ "src/internal/default-retriable.ts"() {
150
+ }
151
+ });
152
+
135
153
  // src/internal/security/redact.ts
136
154
  function readEnvOnce() {
137
155
  const raw = process.env.THEOKIT_REDACT_SECRETS;
@@ -282,7 +300,8 @@ __export(errors_exports, {
282
300
  UnsupportedBudgetOperationError: () => UnsupportedBudgetOperationError,
283
301
  UnsupportedRunOperationError: () => UnsupportedRunOperationError,
284
302
  UnsupportedTaskOperationError: () => UnsupportedTaskOperationError,
285
- coerceToKnownAgentRunErrorCode: () => coerceToKnownAgentRunErrorCode
303
+ coerceToKnownAgentRunErrorCode: () => coerceToKnownAgentRunErrorCode,
304
+ isTransientError: () => isTransientError
286
305
  });
287
306
  function coerceToKnownAgentRunErrorCode(code) {
288
307
  if (code !== void 0 && KNOWN_AGENT_RUN_ERROR_CODES.has(code)) {
@@ -314,21 +333,13 @@ function safeStringify(value) {
314
333
  return String(value);
315
334
  }
316
335
  }
317
- function defaultRetriableForCode(code) {
318
- switch (code) {
319
- case "rate_limit":
320
- case "timeout":
321
- case "server_error":
322
- case "network":
323
- case "provider_unreachable":
324
- return true;
325
- default:
326
- return false;
327
- }
336
+ function isTransientError(err) {
337
+ return err instanceof TheokitAgentError && err.isRetryable === true;
328
338
  }
329
339
  var KNOWN_AGENT_RUN_ERROR_CODES, TheokitAgentError, AuthenticationError, RateLimitError, ConfigurationError, IntegrationNotConnectedError, NetworkError, UnknownAgentError, AgentRunError, UnsupportedRunOperationError, CredentialPoolExhaustedError, MemoryAdapterError, InvalidTaskIdError, TaskNotFoundError, UnsupportedTaskOperationError, BudgetExceededError, AgentDisposedError, UnsupportedBudgetOperationError;
330
340
  var init_errors = __esm({
331
341
  "src/errors.ts"() {
342
+ init_default_retriable();
332
343
  init_redact();
333
344
  KNOWN_AGENT_RUN_ERROR_CODES = /* @__PURE__ */ new Set([
334
345
  "rate_limit",
@@ -1027,6 +1038,19 @@ function sanitizeIdentifier(input, options) {
1027
1038
  }
1028
1039
  return input.toLowerCase();
1029
1040
  }
1041
+ function safeFilenameForId(id, options) {
1042
+ if (id.length === 0) {
1043
+ throw new ConfigurationError("Filename id must be a non-empty string", {
1044
+ code: "invalid_filename_id"
1045
+ });
1046
+ }
1047
+ const maxLen = options?.maxLen;
1048
+ const lower = id.toLowerCase();
1049
+ if (lower.length <= maxLen && IDENTIFIER_PATTERN.test(lower)) {
1050
+ return lower;
1051
+ }
1052
+ return `h-${crypto.createHash("sha256").update(id).digest("hex").slice(0, 16)}`;
1053
+ }
1030
1054
  var PathTraversalError, IDENTIFIER_PATTERN;
1031
1055
  var init_path_guard = __esm({
1032
1056
  "src/internal/security/path-guard.ts"() {
@@ -4243,10 +4267,7 @@ function sessionsDir(cwd) {
4243
4267
  return path.join(memoryDir(cwd), "sessions");
4244
4268
  }
4245
4269
  function sessionSummaryPath(cwd, runId) {
4246
- return path.join(sessionsDir(cwd), `${sanitizeRunId(runId)}.md`);
4247
- }
4248
- function sanitizeRunId(runId) {
4249
- return runId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 128);
4270
+ return path.join(sessionsDir(cwd), `${safeFilenameForId(runId, { maxLen: 128 })}.md`);
4250
4271
  }
4251
4272
  function truncate(text) {
4252
4273
  if (text.length <= MAX_TURN_CHARS) return text;
@@ -4282,6 +4303,7 @@ var MAX_TURN_CHARS;
4282
4303
  var init_session_summary_writer = __esm({
4283
4304
  "src/internal/memory/storage/session-summary-writer.ts"() {
4284
4305
  init_atomic_write();
4306
+ init_path_guard();
4285
4307
  init_types();
4286
4308
  init_markdown_store();
4287
4309
  MAX_TURN_CHARS = 2e3;
@@ -7112,6 +7134,75 @@ var init_async_local_storage = __esm({
7112
7134
  }
7113
7135
  });
7114
7136
 
7137
+ // src/internal/runtime/concurrency/async-semaphore.ts
7138
+ function createSemaphore(permits) {
7139
+ if (!Number.isInteger(permits) || permits < 1) {
7140
+ throw new ConfigurationError(
7141
+ `async-semaphore: permits must be a positive integer, got ${permits}`,
7142
+ { code: "invalid_concurrency" }
7143
+ );
7144
+ }
7145
+ let active = 0;
7146
+ const queue = [];
7147
+ function tryGrant() {
7148
+ if (active < permits && queue.length > 0) {
7149
+ const resolve3 = queue.shift();
7150
+ if (resolve3 !== void 0) {
7151
+ active += 1;
7152
+ resolve3();
7153
+ }
7154
+ }
7155
+ }
7156
+ return {
7157
+ inFlight: () => active,
7158
+ pending: () => queue.length + active,
7159
+ async acquire() {
7160
+ await new Promise((resolve3) => {
7161
+ queue.push(resolve3);
7162
+ tryGrant();
7163
+ });
7164
+ let released = false;
7165
+ return () => {
7166
+ if (released) return;
7167
+ released = true;
7168
+ active -= 1;
7169
+ tryGrant();
7170
+ };
7171
+ }
7172
+ };
7173
+ }
7174
+ var init_async_semaphore = __esm({
7175
+ "src/internal/runtime/concurrency/async-semaphore.ts"() {
7176
+ init_errors();
7177
+ }
7178
+ });
7179
+
7180
+ // src/internal/runtime/concurrency/map-with-concurrency.ts
7181
+ async function mapWithConcurrency(items, concurrency, fn, options) {
7182
+ const semaphore = createSemaphore(concurrency);
7183
+ const signal = NEVER_ABORT;
7184
+ return Promise.all(
7185
+ items.map(async (item, index) => {
7186
+ const release = await semaphore.acquire();
7187
+ try {
7188
+ if (signal.aborted) {
7189
+ throw signal.reason instanceof Error ? signal.reason : new Error("mapWithConcurrency: aborted");
7190
+ }
7191
+ return await fn(item, index, signal);
7192
+ } finally {
7193
+ release();
7194
+ }
7195
+ })
7196
+ );
7197
+ }
7198
+ var NEVER_ABORT;
7199
+ var init_map_with_concurrency = __esm({
7200
+ "src/internal/runtime/concurrency/map-with-concurrency.ts"() {
7201
+ init_async_semaphore();
7202
+ NEVER_ABORT = new AbortController().signal;
7203
+ }
7204
+ });
7205
+
7115
7206
  // src/internal/tool-dispatch/repair-middleware.ts
7116
7207
  function repairToolCall(raw, registry) {
7117
7208
  const repairs = [];
@@ -7301,38 +7392,12 @@ var init_tool_executors = __esm({
7301
7392
  // src/internal/agent-loop/tool-dispatch.ts
7302
7393
  async function dispatchTools(inputs, tools, toolCalls, events) {
7303
7394
  const maxConcurrent = inputs.maxConcurrentTools ?? 4;
7304
- return boundedParallel(
7305
- maxConcurrent,
7395
+ return mapWithConcurrency(
7306
7396
  toolCalls,
7397
+ maxConcurrent,
7307
7398
  (call) => dispatchSingleCall(inputs, tools, call, events)
7308
7399
  );
7309
7400
  }
7310
- async function boundedParallel(max, items, fn) {
7311
- let running = 0;
7312
- const queue = [];
7313
- async function acquire() {
7314
- if (running < max) {
7315
- running++;
7316
- return;
7317
- }
7318
- await new Promise((resolve3) => queue.push(resolve3));
7319
- running++;
7320
- }
7321
- function release() {
7322
- running--;
7323
- if (queue.length > 0) queue.shift()();
7324
- }
7325
- return Promise.all(
7326
- items.map(async (item) => {
7327
- await acquire();
7328
- try {
7329
- return await fn(item);
7330
- } finally {
7331
- release();
7332
- }
7333
- })
7334
- );
7335
- }
7336
7401
  async function dispatchSingleCall(inputs, tools, call, events) {
7337
7402
  const { call: workingCall, repairs } = applyRepairAndExtractCall(tools, call);
7338
7403
  const callId = generateCallId();
@@ -7556,6 +7621,7 @@ var init_tool_dispatch = __esm({
7556
7621
  "src/internal/agent-loop/tool-dispatch.ts"() {
7557
7622
  init_ids();
7558
7623
  init_async_local_storage();
7624
+ init_map_with_concurrency();
7559
7625
  init_repair_middleware();
7560
7626
  init_tool_executors();
7561
7627
  }
@@ -10827,6 +10893,21 @@ var init_client = __esm({
10827
10893
  }
10828
10894
  });
10829
10895
 
10896
+ // src/internal/providers/register-plugin-providers.ts
10897
+ function registerPluginProviderProfiles(entries) {
10898
+ let registered4 = 0;
10899
+ for (const entry of entries) {
10900
+ registerProvider(entry.profile);
10901
+ registered4 += 1;
10902
+ }
10903
+ return registered4;
10904
+ }
10905
+ var init_register_plugin_providers = __esm({
10906
+ "src/internal/providers/register-plugin-providers.ts"() {
10907
+ init_registry();
10908
+ }
10909
+ });
10910
+
10830
10911
  // src/internal/tool-registry/personality-filter.ts
10831
10912
  function applyPersonalityFilter(exposedTools, whitelist, opts) {
10832
10913
  if (whitelist === void 0) return exposedTools;
@@ -10907,12 +10988,26 @@ function createRealLocalRun(options) {
10907
10988
  registerRun(handle);
10908
10989
  return handle;
10909
10990
  }
10910
- function buildLoopInputs(options, runId, userText) {
10991
+ function resolveRunProvider(options) {
10911
10992
  registerBuiltins();
10993
+ const profiles = options.pluginManager?.aggregated.providerProfiles ?? [];
10994
+ const registered4 = registerPluginProviderProfiles(profiles);
10995
+ if (registered4 > 0 && !pluginProvidersAnnounced) {
10996
+ pluginProvidersAnnounced = true;
10997
+ const names = profiles.map((e) => e.profile.name).join(", ");
10998
+ process.stderr.write(
10999
+ `[theokit-sdk] registered ${registered4} plugin provider profile(s): ${names}
11000
+ `
11001
+ );
11002
+ }
10912
11003
  const parsedModel = parseModelId(options.model?.id);
10913
11004
  const inferredProvider = parsedModel.provider !== void 0 && getProviderProfile(parsedModel.provider) !== void 0 ? parsedModel.provider : void 0;
10914
11005
  const primary = options.agentOptions.providers?.routes?.[0]?.provider ?? inferredProvider ?? detectPrimaryProvider();
10915
11006
  const effectiveModelId = inferredProvider !== void 0 ? parsedModel.name : options.model?.id ?? "claude-sonnet-4-6";
11007
+ return { primary, effectiveModelId };
11008
+ }
11009
+ function buildLoopInputs(options, runId, userText) {
11010
+ const { primary, effectiveModelId } = resolveRunProvider(options);
10916
11011
  const fallback = options.agentOptions.providers?.fallback;
10917
11012
  const apiKeys = options.agentOptions.providers?.apiKeys;
10918
11013
  const credentialPoolStrategy = options.agentOptions.providers?.credentialPoolStrategy;
@@ -11006,7 +11101,7 @@ function buildMcpMap(options) {
11006
11101
  }
11007
11102
  return map;
11008
11103
  }
11009
- var RealLocalRun;
11104
+ var pluginProvidersAnnounced, RealLocalRun;
11010
11105
  var init_real_local_run = __esm({
11011
11106
  "src/internal/runtime/local-agent/real-local-run.ts"() {
11012
11107
  init_loop();
@@ -11015,10 +11110,12 @@ var init_real_local_run = __esm({
11015
11110
  init_router();
11016
11111
  init_client();
11017
11112
  init_providers();
11113
+ init_register_plugin_providers();
11018
11114
  init_tracer();
11019
11115
  init_personality_filter();
11020
11116
  init_fixture_run_base();
11021
11117
  init_run_registry();
11118
+ pluginProvidersAnnounced = false;
11022
11119
  RealLocalRun = class extends FixtureRunBase {
11023
11120
  buildInputs;
11024
11121
  constructor(options, buildInputs) {
@@ -11570,7 +11667,7 @@ async function embedTexts(input) {
11570
11667
  pending
11571
11668
  });
11572
11669
  }
11573
- await runBatches(input, pending, results);
11670
+ await embedInBoundedBatches(input, pending, results);
11574
11671
  return results.map((v) => v ?? new Array(dimension).fill(0));
11575
11672
  }
11576
11673
  function classifyEntry(args) {
@@ -11588,45 +11685,34 @@ function classifyEntry(args) {
11588
11685
  args.stats.cacheMisses += 1;
11589
11686
  args.pending.push({ index: args.index, text: args.text, key });
11590
11687
  }
11591
- async function runBatches(input, pending, results) {
11688
+ async function embedInBoundedBatches(input, pending, results) {
11592
11689
  const batches = [];
11593
11690
  for (let offset = 0; offset < pending.length; offset += MAX_BATCH) {
11594
11691
  batches.push(pending.slice(offset, offset + MAX_BATCH));
11595
11692
  }
11596
- let running = 0;
11597
- const queue = [];
11598
- await Promise.all(batches.map((batch) => processBatch(input, batch, results, acquire, release)));
11599
- async function acquire() {
11600
- if (running >= MAX_CONCURRENT_BATCHES) await new Promise((r) => queue.push(r));
11601
- running++;
11602
- }
11603
- function release() {
11604
- running--;
11605
- if (queue.length > 0) queue.shift()();
11606
- }
11693
+ await mapWithConcurrency(
11694
+ batches,
11695
+ MAX_CONCURRENT_BATCHES,
11696
+ (batch) => processBatch(input, batch, results)
11697
+ );
11607
11698
  }
11608
- async function processBatch(input, batch, results, acquire, release) {
11609
- await acquire();
11610
- try {
11611
- const vectors = await embedBatch({
11612
- apiKey: input.apiKey,
11613
- baseUrl: input.baseUrl,
11614
- embeddingsPath: input.embeddingsPath,
11615
- model: input.model,
11616
- inputs: batch.map((b) => b.text),
11617
- fetchImpl: input.fetchImpl,
11618
- stats: input.stats,
11619
- providerId: input.providerId
11620
- });
11621
- for (let j = 0; j < batch.length; j++) {
11622
- const slot = batch[j];
11623
- const vector = vectors[j];
11624
- if (slot === void 0 || vector === void 0) continue;
11625
- results[slot.index] = vector;
11626
- input.cache.set(slot.key, vector);
11627
- }
11628
- } finally {
11629
- release();
11699
+ async function processBatch(input, batch, results) {
11700
+ const vectors = await embedBatch({
11701
+ apiKey: input.apiKey,
11702
+ baseUrl: input.baseUrl,
11703
+ embeddingsPath: input.embeddingsPath,
11704
+ model: input.model,
11705
+ inputs: batch.map((b) => b.text),
11706
+ fetchImpl: input.fetchImpl,
11707
+ stats: input.stats,
11708
+ providerId: input.providerId
11709
+ });
11710
+ for (let j = 0; j < batch.length; j++) {
11711
+ const slot = batch[j];
11712
+ const vector = vectors[j];
11713
+ if (slot === void 0 || vector === void 0) continue;
11714
+ results[slot.index] = vector;
11715
+ input.cache.set(slot.key, vector);
11630
11716
  }
11631
11717
  }
11632
11718
  async function embedBatch(opts) {
@@ -11700,6 +11786,7 @@ var init_openai_compatible2 = __esm({
11700
11786
  "src/internal/memory/adapters/openai-compatible.ts"() {
11701
11787
  init_errors();
11702
11788
  init_openai_compatible();
11789
+ init_map_with_concurrency();
11703
11790
  init_embedding_cache();
11704
11791
  MAX_BATCH = 100;
11705
11792
  MAX_RETRIES = 2;
@@ -12184,6 +12271,61 @@ var init_sqlite_wal = __esm({
12184
12271
  warnedLabels = /* @__PURE__ */ new Set();
12185
12272
  }
12186
12273
  });
12274
+ async function openSqliteResilient(options) {
12275
+ await promises.mkdir(path.dirname(options.filePath), { recursive: true });
12276
+ try {
12277
+ return await openConcrete(options);
12278
+ } catch (cause) {
12279
+ if (options.recoverCorrupt !== false && isCorruptionError(cause)) {
12280
+ await renameAside(options.filePath, options.label ?? "sqlite");
12281
+ return await openConcrete(options);
12282
+ }
12283
+ throw cause;
12284
+ }
12285
+ }
12286
+ async function openConcrete(options) {
12287
+ const db = await loadDriver(options.filePath);
12288
+ applyWalWithFallback(db, options.label ?? "sqlite");
12289
+ await options.onOpen?.(db);
12290
+ return db;
12291
+ }
12292
+ async function loadDriver(filePath) {
12293
+ try {
12294
+ const mod = await import('better-sqlite3');
12295
+ const Ctor = mod.default ?? mod;
12296
+ if (typeof Ctor !== "function") {
12297
+ throw new Error(`better-sqlite3 export is not a constructor (got ${typeof Ctor})`);
12298
+ }
12299
+ return new Ctor(filePath);
12300
+ } catch (cause) {
12301
+ const message = cause instanceof Error ? cause.message : String(cause);
12302
+ throw new ConfigurationError(
12303
+ `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
12304
+ { code: "sqlite_driver_unavailable", cause }
12305
+ );
12306
+ }
12307
+ }
12308
+ function isCorruptionError(cause) {
12309
+ if (!(cause instanceof Error)) return false;
12310
+ const msg = cause.message.toLowerCase();
12311
+ return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
12312
+ }
12313
+ async function renameAside(filePath, label) {
12314
+ const asidePath = `${filePath}.corrupt-${Date.now()}`;
12315
+ await promises.rename(filePath, asidePath).catch(() => void 0);
12316
+ await promises.rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
12317
+ await promises.rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
12318
+ process.stderr.write(
12319
+ `[theokit-sdk] ${label} database corrupt; renamed aside to ${asidePath} and rebuilt schema
12320
+ `
12321
+ );
12322
+ }
12323
+ var init_sqlite_open = __esm({
12324
+ "src/internal/persistence/sqlite-open.ts"() {
12325
+ init_errors();
12326
+ init_sqlite_wal();
12327
+ }
12328
+ });
12187
12329
 
12188
12330
  // src/internal/memory/index-schema.ts
12189
12331
  var SCHEMA_STATEMENTS, PRAGMA_STATEMENTS;
@@ -12231,60 +12373,22 @@ var init_index_schema = __esm({
12231
12373
  }
12232
12374
  });
12233
12375
  async function openMemoryDb(opts) {
12234
- await promises.mkdir(path.dirname(opts.filePath), { recursive: true });
12235
- try {
12236
- return await openConcrete(opts.filePath);
12237
- } catch (cause) {
12238
- if (opts.recoverCorrupt !== false && isCorruptionError(cause)) {
12239
- await renameAside(opts.filePath);
12240
- return await openConcrete(opts.filePath);
12376
+ return openSqliteResilient({
12377
+ filePath: opts.filePath,
12378
+ label: "memory-index",
12379
+ recoverCorrupt: opts.recoverCorrupt,
12380
+ onOpen: (db) => {
12381
+ for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
12382
+ for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
12241
12383
  }
12242
- throw cause;
12243
- }
12244
- }
12245
- async function openConcrete(filePath) {
12246
- const db = await loadDriver(filePath);
12247
- applyWalWithFallback(db, "memory-index");
12248
- for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
12249
- for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
12250
- return db;
12251
- }
12252
- async function loadDriver(filePath) {
12253
- try {
12254
- const mod = await import('better-sqlite3');
12255
- const Ctor = mod.default ?? mod;
12256
- const db = new Ctor(filePath);
12257
- return db;
12258
- } catch (cause) {
12259
- const message = cause instanceof Error ? cause.message : String(cause);
12260
- throw new ConfigurationError(
12261
- `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
12262
- { code: "sqlite_driver_unavailable", cause }
12263
- );
12264
- }
12265
- }
12266
- function isCorruptionError(cause) {
12267
- if (!(cause instanceof Error)) return false;
12268
- const msg = cause.message.toLowerCase();
12269
- return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
12270
- }
12271
- async function renameAside(filePath) {
12272
- const asidePath = `${filePath}.corrupt-${Date.now()}`;
12273
- await promises.rename(filePath, asidePath).catch(() => void 0);
12274
- await promises.rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
12275
- await promises.rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
12276
- process.stderr.write(
12277
- `[theokit-sdk] memory index corrupt; renamed aside to ${asidePath} and rebuilt schema
12278
- `
12279
- );
12384
+ });
12280
12385
  }
12281
12386
  function defaultIndexPath(cwd) {
12282
12387
  return path.join(cwd, ".theokit", "memory", ".index", "memory.sqlite");
12283
12388
  }
12284
12389
  var init_index_db = __esm({
12285
12390
  "src/internal/memory/index-db.ts"() {
12286
- init_errors();
12287
- init_sqlite_wal();
12391
+ init_sqlite_open();
12288
12392
  init_index_schema();
12289
12393
  }
12290
12394
  });
@@ -14347,49 +14451,6 @@ var init_task = __esm({
14347
14451
  }
14348
14452
  });
14349
14453
 
14350
- // src/internal/runtime/concurrency/async-semaphore.ts
14351
- function createSemaphore(permits) {
14352
- if (!Number.isInteger(permits) || permits < 1) {
14353
- throw new ConfigurationError(
14354
- `async-semaphore: permits must be a positive integer, got ${permits}`,
14355
- { code: "invalid_concurrency" }
14356
- );
14357
- }
14358
- let active = 0;
14359
- const queue = [];
14360
- function tryGrant() {
14361
- if (active < permits && queue.length > 0) {
14362
- const resolve3 = queue.shift();
14363
- if (resolve3 !== void 0) {
14364
- active += 1;
14365
- resolve3();
14366
- }
14367
- }
14368
- }
14369
- return {
14370
- inFlight: () => active,
14371
- pending: () => queue.length + active,
14372
- async acquire() {
14373
- await new Promise((resolve3) => {
14374
- queue.push(resolve3);
14375
- tryGrant();
14376
- });
14377
- let released = false;
14378
- return () => {
14379
- if (released) return;
14380
- released = true;
14381
- active -= 1;
14382
- tryGrant();
14383
- };
14384
- }
14385
- };
14386
- }
14387
- var init_async_semaphore = __esm({
14388
- "src/internal/runtime/concurrency/async-semaphore.ts"() {
14389
- init_errors();
14390
- }
14391
- });
14392
-
14393
14454
  // src/internal/task/ring-buffer.ts
14394
14455
  var RingBuffer;
14395
14456
  var init_ring_buffer = __esm({