@theokit/sdk 2.0.1 → 2.2.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 (81) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/a2a/index.cjs +261 -174
  3. package/dist/a2a/index.cjs.map +1 -1
  4. package/dist/a2a/index.js +261 -174
  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-Bj8-Aq1O.d.ts → cron-Aksw2Hy4.d.ts} +10 -2
  13. package/dist/{cron-DFG9-W17.d.cts → cron-JSPSFczQ.d.cts} +10 -2
  14. package/dist/cron.cjs +244 -172
  15. package/dist/cron.cjs.map +1 -1
  16. package/dist/cron.d.cts +2 -2
  17. package/dist/cron.d.ts +2 -2
  18. package/dist/cron.js +244 -172
  19. package/dist/cron.js.map +1 -1
  20. package/dist/{errors-ChqOmFH1.d.cts → errors-Bcw_Pakm.d.ts} +24 -2
  21. package/dist/{errors-DV9e0rcp.d.ts → errors-Vhg6ZV4o.d.cts} +24 -2
  22. package/dist/errors.cjs +17 -11
  23. package/dist/errors.cjs.map +1 -1
  24. package/dist/errors.d.cts +2 -2
  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 +244 -172
  29. package/dist/eval.cjs.map +1 -1
  30. package/dist/eval.js +244 -172
  31. package/dist/eval.js.map +1 -1
  32. package/dist/index.cjs +262 -174
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +163 -121
  35. package/dist/index.d.ts +163 -121
  36. package/dist/index.js +262 -176
  37. package/dist/index.js.map +1 -1
  38. package/dist/internal/agent-loop/loop-types.d.ts +6 -0
  39. package/dist/internal/default-retriable.d.ts +1 -0
  40. package/dist/internal/persistence/index.cjs +75 -0
  41. package/dist/internal/persistence/index.cjs.map +1 -1
  42. package/dist/internal/persistence/index.d.cts +2 -0
  43. package/dist/internal/persistence/index.d.ts +2 -0
  44. package/dist/internal/persistence/index.js +74 -1
  45. package/dist/internal/persistence/index.js.map +1 -1
  46. package/dist/internal/persistence/sqlite-open.d.cts +47 -0
  47. package/dist/internal/persistence/sqlite-open.d.ts +47 -0
  48. package/dist/internal/providers/register-plugin-providers.d.ts +22 -0
  49. package/dist/internal/runtime/budget/budget-tracker.d.ts +8 -0
  50. package/dist/internal/runtime/concurrency/map-with-concurrency.d.ts +28 -0
  51. package/dist/internal/runtime/retry/with-retry.d.ts +40 -0
  52. package/dist/internal/security/index.cjs +1 -0
  53. package/dist/internal/security/index.cjs.map +1 -1
  54. package/dist/internal/security/index.js +1 -0
  55. package/dist/internal/security/index.js.map +1 -1
  56. package/dist/path-safety.cjs +15 -0
  57. package/dist/path-safety.cjs.map +1 -1
  58. package/dist/path-safety.d.cts +1 -1
  59. package/dist/path-safety.d.ts +1 -1
  60. package/dist/path-safety.js +15 -1
  61. package/dist/path-safety.js.map +1 -1
  62. package/dist/retry.cjs +85 -0
  63. package/dist/retry.cjs.map +1 -0
  64. package/dist/retry.d.cts +9 -0
  65. package/dist/retry.d.ts +9 -0
  66. package/dist/retry.js +83 -0
  67. package/dist/retry.js.map +1 -0
  68. package/dist/{run-DrwUpFxZ.d.cts → run-ekGKZlmg.d.cts} +20 -0
  69. package/dist/{run-DrwUpFxZ.d.ts → run-ekGKZlmg.d.ts} +20 -0
  70. package/dist/server/errors-envelope.cjs +14 -12
  71. package/dist/server/errors-envelope.cjs.map +1 -1
  72. package/dist/server/errors-envelope.js +14 -12
  73. package/dist/server/errors-envelope.js.map +1 -1
  74. package/dist/subscription/index.cjs.map +1 -1
  75. package/dist/subscription/index.js.map +1 -1
  76. package/dist/task-store.cjs.map +1 -1
  77. package/dist/task-store.js.map +1 -1
  78. package/dist/types/run.d.ts +20 -0
  79. package/dist/workflow.cjs.map +1 -1
  80. package/dist/workflow.js.map +1 -1
  81. 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";
@@ -4288,8 +4312,7 @@ var FixtureRunBase = class {
4288
4312
  if (status === "error" && this.script.errorDetail !== void 0) {
4289
4313
  base.error = this.script.errorDetail;
4290
4314
  }
4291
- if (this.script.usage !== void 0) base.usage = this.script.usage;
4292
- if (this.script.cost !== void 0) base.cost = this.script.cost;
4315
+ applyScriptMetrics(base, this.script);
4293
4316
  return this.extendRunResult(applyExtraRunFields(base, this.script));
4294
4317
  }
4295
4318
  /** Subclasses override to attach runtime-specific fields (e.g. cloud git info). */
@@ -4323,6 +4346,11 @@ function makeNotifier() {
4323
4346
  });
4324
4347
  return { promise, resolve: resolve3 };
4325
4348
  }
4349
+ function applyScriptMetrics(base, script) {
4350
+ if (script.usage !== void 0) base.usage = script.usage;
4351
+ if (script.cost !== void 0) base.cost = script.cost;
4352
+ if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
4353
+ }
4326
4354
 
4327
4355
  // src/internal/runtime/cloud/cloud-run.ts
4328
4356
  function createCloudRun(options) {
@@ -5791,10 +5819,7 @@ function sessionsDir(cwd) {
5791
5819
  return join(memoryDir(cwd), "sessions");
5792
5820
  }
5793
5821
  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);
5822
+ return join(sessionsDir(cwd), `${safeFilenameForId(runId, { maxLen: 128 })}.md`);
5798
5823
  }
5799
5824
  function truncate(text) {
5800
5825
  if (text.length <= MAX_TURN_CHARS) return text;
@@ -7958,6 +7983,9 @@ var LocalRun = class extends FixtureRunBase {
7958
7983
  }
7959
7984
  };
7960
7985
 
7986
+ // src/internal/runtime/local-agent/real-local-run.ts
7987
+ init_errors();
7988
+
7961
7989
  // src/internal/runtime/budget/budget.ts
7962
7990
  var IterationBudget = class {
7963
7991
  #remaining;
@@ -8411,6 +8439,27 @@ async function emitTextDeltaCallback(inputs, text) {
8411
8439
  // src/internal/agent-loop/tool-dispatch.ts
8412
8440
  init_async_local_storage();
8413
8441
 
8442
+ // src/internal/runtime/concurrency/map-with-concurrency.ts
8443
+ init_async_semaphore();
8444
+ var NEVER_ABORT = new AbortController().signal;
8445
+ async function mapWithConcurrency(items, concurrency, fn, options) {
8446
+ const semaphore = createSemaphore(concurrency);
8447
+ const signal = NEVER_ABORT;
8448
+ return Promise.all(
8449
+ items.map(async (item, index) => {
8450
+ const release = await semaphore.acquire();
8451
+ try {
8452
+ if (signal.aborted) {
8453
+ throw signal.reason instanceof Error ? signal.reason : new Error("mapWithConcurrency: aborted");
8454
+ }
8455
+ return await fn(item, index, signal);
8456
+ } finally {
8457
+ release();
8458
+ }
8459
+ })
8460
+ );
8461
+ }
8462
+
8414
8463
  // src/internal/tool-dispatch/repair-middleware.ts
8415
8464
  var DECIMAL_RE = /^-?\d+(\.\d+)?$/;
8416
8465
  function repairToolCall(raw, registry) {
@@ -8585,38 +8634,12 @@ ${result.stderr}`.trim();
8585
8634
  // src/internal/agent-loop/tool-dispatch.ts
8586
8635
  async function dispatchTools(inputs, tools, toolCalls, events) {
8587
8636
  const maxConcurrent = inputs.maxConcurrentTools ?? 4;
8588
- return boundedParallel(
8589
- maxConcurrent,
8637
+ return mapWithConcurrency(
8590
8638
  toolCalls,
8639
+ maxConcurrent,
8591
8640
  (call) => dispatchSingleCall(inputs, tools, call, events)
8592
8641
  );
8593
8642
  }
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
8643
  async function dispatchSingleCall(inputs, tools, call, events) {
8621
8644
  const { call: workingCall, repairs } = applyRepairAndExtractCall(tools, call);
8622
8645
  const callId = generateCallId();
@@ -9187,6 +9210,7 @@ async function runAgentLoop(inputs) {
9187
9210
  const ctx = await initLoopContext(inputs);
9188
9211
  ctxRef = ctx;
9189
9212
  const budget = inputs.budget ?? new IterationBudget({ maxIterations: inputs.maxIterations ?? 8 });
9213
+ let lastTurnDecision;
9190
9214
  while (budget.shouldContinue()) {
9191
9215
  if (inputs.budgetTracker !== void 0) {
9192
9216
  const decision2 = evaluateBudgetGate(inputs.budgetTracker);
@@ -9195,18 +9219,26 @@ async function runAgentLoop(inputs) {
9195
9219
  if (decision2.detail !== void 0) {
9196
9220
  ctx.error = { message: decision2.detail, code: decision2.reason ?? "budget" };
9197
9221
  }
9222
+ if (decision2.reason === "iteration_limit") {
9223
+ ctx.stoppedAtIterationLimit = true;
9224
+ }
9198
9225
  break;
9199
9226
  }
9200
9227
  }
9201
9228
  const usingGrace = budget.remaining <= 0 && !budget.graceCallUsed;
9202
9229
  if (usingGrace) budget.useGraceCall();
9203
9230
  const decision = await runIteration(inputs, ctx);
9231
+ lastTurnDecision = decision;
9204
9232
  if (decision === "done") break;
9205
9233
  if (decision === "error") {
9206
9234
  ctx.finalStatus = "error";
9207
9235
  break;
9208
9236
  }
9209
9237
  budget.consume();
9238
+ inputs.budgetTracker?.nextIteration?.();
9239
+ }
9240
+ if (lastTurnDecision === "continue" && budget.shouldContinue() === false) {
9241
+ ctx.stoppedAtIterationLimit = true;
9210
9242
  }
9211
9243
  if (budget.shouldContinue() === false && ctx.finalStatus === "finished" && ctx.finalText === "") {
9212
9244
  ctx.finalStatus = "error";
@@ -9237,7 +9269,8 @@ async function runAgentLoop(inputs) {
9237
9269
  conversation: ctx.conversation,
9238
9270
  ...usage !== void 0 ? { usage } : {},
9239
9271
  ...cost !== void 0 ? { cost } : {},
9240
- ...ctx.error !== void 0 ? { error: ctx.error } : {}
9272
+ ...ctx.error !== void 0 ? { error: ctx.error } : {},
9273
+ ...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {}
9241
9274
  };
9242
9275
  } finally {
9243
9276
  if (ctxRef !== void 0 && ctxRef.memoryProviderHandle !== void 0 && inputs.memoryProvider !== void 0) {
@@ -11550,6 +11583,16 @@ function resolveMcpCwd(configCwd) {
11550
11583
  return safePathJoin(process.cwd(), configCwd);
11551
11584
  }
11552
11585
 
11586
+ // src/internal/providers/register-plugin-providers.ts
11587
+ function registerPluginProviderProfiles(entries) {
11588
+ let registered4 = 0;
11589
+ for (const entry of entries) {
11590
+ registerProvider(entry.profile);
11591
+ registered4 += 1;
11592
+ }
11593
+ return registered4;
11594
+ }
11595
+
11553
11596
  // src/internal/tool-registry/personality-filter.ts
11554
11597
  init_hooks_source();
11555
11598
  function applyPersonalityFilter(exposedTools, whitelist, opts) {
@@ -11626,12 +11669,34 @@ function createRealLocalRun(options) {
11626
11669
  registerRun(handle);
11627
11670
  return handle;
11628
11671
  }
11629
- function buildLoopInputs(options, runId, userText) {
11672
+ var pluginProvidersAnnounced = false;
11673
+ function resolveRunProvider(options) {
11630
11674
  registerBuiltins();
11675
+ const profiles = options.pluginManager?.aggregated.providerProfiles ?? [];
11676
+ const registered4 = registerPluginProviderProfiles(profiles);
11677
+ if (registered4 > 0 && !pluginProvidersAnnounced) {
11678
+ pluginProvidersAnnounced = true;
11679
+ const names = profiles.map((e) => e.profile.name).join(", ");
11680
+ process.stderr.write(
11681
+ `[theokit-sdk] registered ${registered4} plugin provider profile(s): ${names}
11682
+ `
11683
+ );
11684
+ }
11631
11685
  const parsedModel = parseModelId(options.model?.id);
11632
11686
  const inferredProvider = parsedModel.provider !== void 0 && getProviderProfile(parsedModel.provider) !== void 0 ? parsedModel.provider : void 0;
11633
11687
  const primary = options.agentOptions.providers?.routes?.[0]?.provider ?? inferredProvider ?? detectPrimaryProvider();
11634
11688
  const effectiveModelId = inferredProvider !== void 0 ? parsedModel.name : options.model?.id ?? "claude-sonnet-4-6";
11689
+ return { primary, effectiveModelId };
11690
+ }
11691
+ function buildLoopInputs(options, runId, userText) {
11692
+ const maxIterations = options.sendOptions.maxIterations;
11693
+ if (maxIterations !== void 0 && (!Number.isInteger(maxIterations) || maxIterations < 1)) {
11694
+ throw new ConfigurationError(
11695
+ `SendOptions.maxIterations must be a positive integer, got ${maxIterations}`,
11696
+ { code: "invalid_max_iterations" }
11697
+ );
11698
+ }
11699
+ const { primary, effectiveModelId } = resolveRunProvider(options);
11635
11700
  const fallback = options.agentOptions.providers?.fallback;
11636
11701
  const apiKeys = options.agentOptions.providers?.apiKeys;
11637
11702
  const credentialPoolStrategy = options.agentOptions.providers?.credentialPoolStrategy;
@@ -11669,6 +11734,9 @@ function buildLoopInputs(options, runId, userText) {
11669
11734
  // D318 — forward SendOptions.signal to the agent loop so streamLlmTurn
11670
11735
  // can attach it to the LLM `fetch({ signal })` call.
11671
11736
  ...options.sendOptions.signal !== void 0 ? { signal: options.sendOptions.signal } : {},
11737
+ // M1-2: per-send iteration ceiling (validated above). The loop reads
11738
+ // inputs.maxIterations (default 8 when unset).
11739
+ ...maxIterations !== void 0 ? { maxIterations } : {},
11672
11740
  // D315-D317 — tool lifecycle hooks (cost tracking + audit + retry/alert)
11673
11741
  ...options.agentOptions.onToolStart !== void 0 ? { onToolStart: options.agentOptions.onToolStart } : {},
11674
11742
  ...options.agentOptions.onToolEnd !== void 0 ? { onToolEnd: options.agentOptions.onToolEnd } : {},
@@ -11800,6 +11868,7 @@ var RealLocalRun = class extends FixtureRunBase {
11800
11868
  if (output.result.length > 0) this.script.result = output.result;
11801
11869
  if (output.usage !== void 0) this.script.usage = output.usage;
11802
11870
  if (output.cost !== void 0) this.script.cost = output.cost;
11871
+ if (output.stoppedAtIterationLimit === true) this.script.stoppedAtIterationLimit = true;
11803
11872
  if (output.error !== void 0 && this.script.errorDetail === void 0) {
11804
11873
  this.script.errorDetail = {
11805
11874
  message: output.error.message,
@@ -12249,7 +12318,7 @@ async function embedTexts(input) {
12249
12318
  pending
12250
12319
  });
12251
12320
  }
12252
- await runBatches(input, pending, results);
12321
+ await embedInBoundedBatches(input, pending, results);
12253
12322
  return results.map((v) => v ?? new Array(dimension).fill(0));
12254
12323
  }
12255
12324
  function classifyEntry(args) {
@@ -12268,45 +12337,34 @@ function classifyEntry(args) {
12268
12337
  args.pending.push({ index: args.index, text: args.text, key });
12269
12338
  }
12270
12339
  var MAX_CONCURRENT_BATCHES = 3;
12271
- async function runBatches(input, pending, results) {
12340
+ async function embedInBoundedBatches(input, pending, results) {
12272
12341
  const batches = [];
12273
12342
  for (let offset = 0; offset < pending.length; offset += MAX_BATCH) {
12274
12343
  batches.push(pending.slice(offset, offset + MAX_BATCH));
12275
12344
  }
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
- }
12345
+ await mapWithConcurrency(
12346
+ batches,
12347
+ MAX_CONCURRENT_BATCHES,
12348
+ (batch) => processBatch(input, batch, results)
12349
+ );
12287
12350
  }
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();
12351
+ async function processBatch(input, batch, results) {
12352
+ const vectors = await embedBatch({
12353
+ apiKey: input.apiKey,
12354
+ baseUrl: input.baseUrl,
12355
+ embeddingsPath: input.embeddingsPath,
12356
+ model: input.model,
12357
+ inputs: batch.map((b) => b.text),
12358
+ fetchImpl: input.fetchImpl,
12359
+ stats: input.stats,
12360
+ providerId: input.providerId
12361
+ });
12362
+ for (let j = 0; j < batch.length; j++) {
12363
+ const slot = batch[j];
12364
+ const vector = vectors[j];
12365
+ if (slot === void 0 || vector === void 0) continue;
12366
+ results[slot.index] = vector;
12367
+ input.cache.set(slot.key, vector);
12310
12368
  }
12311
12369
  }
12312
12370
  async function embedBatch(opts) {
@@ -12739,7 +12797,7 @@ function sanitizeFts5Query(query) {
12739
12797
  return text.trim();
12740
12798
  }
12741
12799
 
12742
- // src/internal/memory/index-db.ts
12800
+ // src/internal/persistence/sqlite-open.ts
12743
12801
  init_errors();
12744
12802
 
12745
12803
  // src/internal/persistence/sqlite-wal.ts
@@ -12767,6 +12825,57 @@ function logFallback(label, reason) {
12767
12825
  );
12768
12826
  }
12769
12827
 
12828
+ // src/internal/persistence/sqlite-open.ts
12829
+ async function openSqliteResilient(options) {
12830
+ await mkdir(dirname(options.filePath), { recursive: true });
12831
+ try {
12832
+ return await openConcrete(options);
12833
+ } catch (cause) {
12834
+ if (options.recoverCorrupt !== false && isCorruptionError(cause)) {
12835
+ await renameAside(options.filePath, options.label ?? "sqlite");
12836
+ return await openConcrete(options);
12837
+ }
12838
+ throw cause;
12839
+ }
12840
+ }
12841
+ async function openConcrete(options) {
12842
+ const db = await loadDriver(options.filePath);
12843
+ applyWalWithFallback(db, options.label ?? "sqlite");
12844
+ await options.onOpen?.(db);
12845
+ return db;
12846
+ }
12847
+ async function loadDriver(filePath) {
12848
+ try {
12849
+ const mod = await import('better-sqlite3');
12850
+ const Ctor = mod.default ?? mod;
12851
+ if (typeof Ctor !== "function") {
12852
+ throw new Error(`better-sqlite3 export is not a constructor (got ${typeof Ctor})`);
12853
+ }
12854
+ return new Ctor(filePath);
12855
+ } catch (cause) {
12856
+ const message = cause instanceof Error ? cause.message : String(cause);
12857
+ throw new ConfigurationError(
12858
+ `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
12859
+ { code: "sqlite_driver_unavailable", cause }
12860
+ );
12861
+ }
12862
+ }
12863
+ function isCorruptionError(cause) {
12864
+ if (!(cause instanceof Error)) return false;
12865
+ const msg = cause.message.toLowerCase();
12866
+ return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
12867
+ }
12868
+ async function renameAside(filePath, label) {
12869
+ const asidePath = `${filePath}.corrupt-${Date.now()}`;
12870
+ await rename(filePath, asidePath).catch(() => void 0);
12871
+ await rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
12872
+ await rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
12873
+ process.stderr.write(
12874
+ `[theokit-sdk] ${label} database corrupt; renamed aside to ${asidePath} and rebuilt schema
12875
+ `
12876
+ );
12877
+ }
12878
+
12770
12879
  // src/internal/memory/index-schema.ts
12771
12880
  var SCHEMA_STATEMENTS = [
12772
12881
  `CREATE TABLE IF NOT EXISTS files (
@@ -12810,52 +12919,15 @@ var PRAGMA_STATEMENTS = [
12810
12919
 
12811
12920
  // src/internal/memory/index-db.ts
12812
12921
  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);
12922
+ return openSqliteResilient({
12923
+ filePath: opts.filePath,
12924
+ label: "memory-index",
12925
+ recoverCorrupt: opts.recoverCorrupt,
12926
+ onOpen: (db) => {
12927
+ for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
12928
+ for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
12820
12929
  }
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
- );
12930
+ });
12859
12931
  }
12860
12932
  function defaultIndexPath(cwd) {
12861
12933
  return join(cwd, ".theokit", "memory", ".index", "memory.sqlite");