@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/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";
@@ -4296,8 +4320,7 @@ var FixtureRunBase = class {
4296
4320
  if (status === "error" && this.script.errorDetail !== void 0) {
4297
4321
  base.error = this.script.errorDetail;
4298
4322
  }
4299
- if (this.script.usage !== void 0) base.usage = this.script.usage;
4300
- if (this.script.cost !== void 0) base.cost = this.script.cost;
4323
+ applyScriptMetrics(base, this.script);
4301
4324
  return this.extendRunResult(applyExtraRunFields(base, this.script));
4302
4325
  }
4303
4326
  /** Subclasses override to attach runtime-specific fields (e.g. cloud git info). */
@@ -4331,6 +4354,11 @@ function makeNotifier() {
4331
4354
  });
4332
4355
  return { promise, resolve: resolve3 };
4333
4356
  }
4357
+ function applyScriptMetrics(base, script) {
4358
+ if (script.usage !== void 0) base.usage = script.usage;
4359
+ if (script.cost !== void 0) base.cost = script.cost;
4360
+ if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
4361
+ }
4334
4362
 
4335
4363
  // src/internal/runtime/cloud/cloud-run.ts
4336
4364
  function createCloudRun(options) {
@@ -5799,10 +5827,7 @@ function sessionsDir(cwd) {
5799
5827
  return path.join(memoryDir(cwd), "sessions");
5800
5828
  }
5801
5829
  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);
5830
+ return path.join(sessionsDir(cwd), `${safeFilenameForId(runId, { maxLen: 128 })}.md`);
5806
5831
  }
5807
5832
  function truncate(text) {
5808
5833
  if (text.length <= MAX_TURN_CHARS) return text;
@@ -7966,6 +7991,9 @@ var LocalRun = class extends FixtureRunBase {
7966
7991
  }
7967
7992
  };
7968
7993
 
7994
+ // src/internal/runtime/local-agent/real-local-run.ts
7995
+ init_errors();
7996
+
7969
7997
  // src/internal/runtime/budget/budget.ts
7970
7998
  var IterationBudget = class {
7971
7999
  #remaining;
@@ -8419,6 +8447,27 @@ async function emitTextDeltaCallback(inputs, text) {
8419
8447
  // src/internal/agent-loop/tool-dispatch.ts
8420
8448
  init_async_local_storage();
8421
8449
 
8450
+ // src/internal/runtime/concurrency/map-with-concurrency.ts
8451
+ init_async_semaphore();
8452
+ var NEVER_ABORT = new AbortController().signal;
8453
+ async function mapWithConcurrency(items, concurrency, fn, options) {
8454
+ const semaphore = createSemaphore(concurrency);
8455
+ const signal = NEVER_ABORT;
8456
+ return Promise.all(
8457
+ items.map(async (item, index) => {
8458
+ const release = await semaphore.acquire();
8459
+ try {
8460
+ if (signal.aborted) {
8461
+ throw signal.reason instanceof Error ? signal.reason : new Error("mapWithConcurrency: aborted");
8462
+ }
8463
+ return await fn(item, index, signal);
8464
+ } finally {
8465
+ release();
8466
+ }
8467
+ })
8468
+ );
8469
+ }
8470
+
8422
8471
  // src/internal/tool-dispatch/repair-middleware.ts
8423
8472
  var DECIMAL_RE = /^-?\d+(\.\d+)?$/;
8424
8473
  function repairToolCall(raw, registry) {
@@ -8593,38 +8642,12 @@ ${result.stderr}`.trim();
8593
8642
  // src/internal/agent-loop/tool-dispatch.ts
8594
8643
  async function dispatchTools(inputs, tools, toolCalls, events) {
8595
8644
  const maxConcurrent = inputs.maxConcurrentTools ?? 4;
8596
- return boundedParallel(
8597
- maxConcurrent,
8645
+ return mapWithConcurrency(
8598
8646
  toolCalls,
8647
+ maxConcurrent,
8599
8648
  (call) => dispatchSingleCall(inputs, tools, call, events)
8600
8649
  );
8601
8650
  }
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
8651
  async function dispatchSingleCall(inputs, tools, call, events) {
8629
8652
  const { call: workingCall, repairs } = applyRepairAndExtractCall(tools, call);
8630
8653
  const callId = generateCallId();
@@ -9195,6 +9218,7 @@ async function runAgentLoop(inputs) {
9195
9218
  const ctx = await initLoopContext(inputs);
9196
9219
  ctxRef = ctx;
9197
9220
  const budget = inputs.budget ?? new IterationBudget({ maxIterations: inputs.maxIterations ?? 8 });
9221
+ let lastTurnDecision;
9198
9222
  while (budget.shouldContinue()) {
9199
9223
  if (inputs.budgetTracker !== void 0) {
9200
9224
  const decision2 = evaluateBudgetGate(inputs.budgetTracker);
@@ -9203,18 +9227,26 @@ async function runAgentLoop(inputs) {
9203
9227
  if (decision2.detail !== void 0) {
9204
9228
  ctx.error = { message: decision2.detail, code: decision2.reason ?? "budget" };
9205
9229
  }
9230
+ if (decision2.reason === "iteration_limit") {
9231
+ ctx.stoppedAtIterationLimit = true;
9232
+ }
9206
9233
  break;
9207
9234
  }
9208
9235
  }
9209
9236
  const usingGrace = budget.remaining <= 0 && !budget.graceCallUsed;
9210
9237
  if (usingGrace) budget.useGraceCall();
9211
9238
  const decision = await runIteration(inputs, ctx);
9239
+ lastTurnDecision = decision;
9212
9240
  if (decision === "done") break;
9213
9241
  if (decision === "error") {
9214
9242
  ctx.finalStatus = "error";
9215
9243
  break;
9216
9244
  }
9217
9245
  budget.consume();
9246
+ inputs.budgetTracker?.nextIteration?.();
9247
+ }
9248
+ if (lastTurnDecision === "continue" && budget.shouldContinue() === false) {
9249
+ ctx.stoppedAtIterationLimit = true;
9218
9250
  }
9219
9251
  if (budget.shouldContinue() === false && ctx.finalStatus === "finished" && ctx.finalText === "") {
9220
9252
  ctx.finalStatus = "error";
@@ -9245,7 +9277,8 @@ async function runAgentLoop(inputs) {
9245
9277
  conversation: ctx.conversation,
9246
9278
  ...usage !== void 0 ? { usage } : {},
9247
9279
  ...cost !== void 0 ? { cost } : {},
9248
- ...ctx.error !== void 0 ? { error: ctx.error } : {}
9280
+ ...ctx.error !== void 0 ? { error: ctx.error } : {},
9281
+ ...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {}
9249
9282
  };
9250
9283
  } finally {
9251
9284
  if (ctxRef !== void 0 && ctxRef.memoryProviderHandle !== void 0 && inputs.memoryProvider !== void 0) {
@@ -11558,6 +11591,16 @@ function resolveMcpCwd(configCwd) {
11558
11591
  return safePathJoin(process.cwd(), configCwd);
11559
11592
  }
11560
11593
 
11594
+ // src/internal/providers/register-plugin-providers.ts
11595
+ function registerPluginProviderProfiles(entries) {
11596
+ let registered4 = 0;
11597
+ for (const entry of entries) {
11598
+ registerProvider(entry.profile);
11599
+ registered4 += 1;
11600
+ }
11601
+ return registered4;
11602
+ }
11603
+
11561
11604
  // src/internal/tool-registry/personality-filter.ts
11562
11605
  init_hooks_source();
11563
11606
  function applyPersonalityFilter(exposedTools, whitelist, opts) {
@@ -11634,12 +11677,34 @@ function createRealLocalRun(options) {
11634
11677
  registerRun(handle);
11635
11678
  return handle;
11636
11679
  }
11637
- function buildLoopInputs(options, runId, userText) {
11680
+ var pluginProvidersAnnounced = false;
11681
+ function resolveRunProvider(options) {
11638
11682
  registerBuiltins();
11683
+ const profiles = options.pluginManager?.aggregated.providerProfiles ?? [];
11684
+ const registered4 = registerPluginProviderProfiles(profiles);
11685
+ if (registered4 > 0 && !pluginProvidersAnnounced) {
11686
+ pluginProvidersAnnounced = true;
11687
+ const names = profiles.map((e) => e.profile.name).join(", ");
11688
+ process.stderr.write(
11689
+ `[theokit-sdk] registered ${registered4} plugin provider profile(s): ${names}
11690
+ `
11691
+ );
11692
+ }
11639
11693
  const parsedModel = parseModelId(options.model?.id);
11640
11694
  const inferredProvider = parsedModel.provider !== void 0 && getProviderProfile(parsedModel.provider) !== void 0 ? parsedModel.provider : void 0;
11641
11695
  const primary = options.agentOptions.providers?.routes?.[0]?.provider ?? inferredProvider ?? detectPrimaryProvider();
11642
11696
  const effectiveModelId = inferredProvider !== void 0 ? parsedModel.name : options.model?.id ?? "claude-sonnet-4-6";
11697
+ return { primary, effectiveModelId };
11698
+ }
11699
+ function buildLoopInputs(options, runId, userText) {
11700
+ const maxIterations = options.sendOptions.maxIterations;
11701
+ if (maxIterations !== void 0 && (!Number.isInteger(maxIterations) || maxIterations < 1)) {
11702
+ throw new ConfigurationError(
11703
+ `SendOptions.maxIterations must be a positive integer, got ${maxIterations}`,
11704
+ { code: "invalid_max_iterations" }
11705
+ );
11706
+ }
11707
+ const { primary, effectiveModelId } = resolveRunProvider(options);
11643
11708
  const fallback = options.agentOptions.providers?.fallback;
11644
11709
  const apiKeys = options.agentOptions.providers?.apiKeys;
11645
11710
  const credentialPoolStrategy = options.agentOptions.providers?.credentialPoolStrategy;
@@ -11677,6 +11742,9 @@ function buildLoopInputs(options, runId, userText) {
11677
11742
  // D318 — forward SendOptions.signal to the agent loop so streamLlmTurn
11678
11743
  // can attach it to the LLM `fetch({ signal })` call.
11679
11744
  ...options.sendOptions.signal !== void 0 ? { signal: options.sendOptions.signal } : {},
11745
+ // M1-2: per-send iteration ceiling (validated above). The loop reads
11746
+ // inputs.maxIterations (default 8 when unset).
11747
+ ...maxIterations !== void 0 ? { maxIterations } : {},
11680
11748
  // D315-D317 — tool lifecycle hooks (cost tracking + audit + retry/alert)
11681
11749
  ...options.agentOptions.onToolStart !== void 0 ? { onToolStart: options.agentOptions.onToolStart } : {},
11682
11750
  ...options.agentOptions.onToolEnd !== void 0 ? { onToolEnd: options.agentOptions.onToolEnd } : {},
@@ -11808,6 +11876,7 @@ var RealLocalRun = class extends FixtureRunBase {
11808
11876
  if (output.result.length > 0) this.script.result = output.result;
11809
11877
  if (output.usage !== void 0) this.script.usage = output.usage;
11810
11878
  if (output.cost !== void 0) this.script.cost = output.cost;
11879
+ if (output.stoppedAtIterationLimit === true) this.script.stoppedAtIterationLimit = true;
11811
11880
  if (output.error !== void 0 && this.script.errorDetail === void 0) {
11812
11881
  this.script.errorDetail = {
11813
11882
  message: output.error.message,
@@ -12257,7 +12326,7 @@ async function embedTexts(input) {
12257
12326
  pending
12258
12327
  });
12259
12328
  }
12260
- await runBatches(input, pending, results);
12329
+ await embedInBoundedBatches(input, pending, results);
12261
12330
  return results.map((v) => v ?? new Array(dimension).fill(0));
12262
12331
  }
12263
12332
  function classifyEntry(args) {
@@ -12276,45 +12345,34 @@ function classifyEntry(args) {
12276
12345
  args.pending.push({ index: args.index, text: args.text, key });
12277
12346
  }
12278
12347
  var MAX_CONCURRENT_BATCHES = 3;
12279
- async function runBatches(input, pending, results) {
12348
+ async function embedInBoundedBatches(input, pending, results) {
12280
12349
  const batches = [];
12281
12350
  for (let offset = 0; offset < pending.length; offset += MAX_BATCH) {
12282
12351
  batches.push(pending.slice(offset, offset + MAX_BATCH));
12283
12352
  }
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
- }
12353
+ await mapWithConcurrency(
12354
+ batches,
12355
+ MAX_CONCURRENT_BATCHES,
12356
+ (batch) => processBatch(input, batch, results)
12357
+ );
12295
12358
  }
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();
12359
+ async function processBatch(input, batch, results) {
12360
+ const vectors = await embedBatch({
12361
+ apiKey: input.apiKey,
12362
+ baseUrl: input.baseUrl,
12363
+ embeddingsPath: input.embeddingsPath,
12364
+ model: input.model,
12365
+ inputs: batch.map((b) => b.text),
12366
+ fetchImpl: input.fetchImpl,
12367
+ stats: input.stats,
12368
+ providerId: input.providerId
12369
+ });
12370
+ for (let j = 0; j < batch.length; j++) {
12371
+ const slot = batch[j];
12372
+ const vector = vectors[j];
12373
+ if (slot === void 0 || vector === void 0) continue;
12374
+ results[slot.index] = vector;
12375
+ input.cache.set(slot.key, vector);
12318
12376
  }
12319
12377
  }
12320
12378
  async function embedBatch(opts) {
@@ -12747,7 +12805,7 @@ function sanitizeFts5Query(query) {
12747
12805
  return text.trim();
12748
12806
  }
12749
12807
 
12750
- // src/internal/memory/index-db.ts
12808
+ // src/internal/persistence/sqlite-open.ts
12751
12809
  init_errors();
12752
12810
 
12753
12811
  // src/internal/persistence/sqlite-wal.ts
@@ -12775,6 +12833,57 @@ function logFallback(label, reason) {
12775
12833
  );
12776
12834
  }
12777
12835
 
12836
+ // src/internal/persistence/sqlite-open.ts
12837
+ async function openSqliteResilient(options) {
12838
+ await promises.mkdir(path.dirname(options.filePath), { recursive: true });
12839
+ try {
12840
+ return await openConcrete(options);
12841
+ } catch (cause) {
12842
+ if (options.recoverCorrupt !== false && isCorruptionError(cause)) {
12843
+ await renameAside(options.filePath, options.label ?? "sqlite");
12844
+ return await openConcrete(options);
12845
+ }
12846
+ throw cause;
12847
+ }
12848
+ }
12849
+ async function openConcrete(options) {
12850
+ const db = await loadDriver(options.filePath);
12851
+ applyWalWithFallback(db, options.label ?? "sqlite");
12852
+ await options.onOpen?.(db);
12853
+ return db;
12854
+ }
12855
+ async function loadDriver(filePath) {
12856
+ try {
12857
+ const mod = await import('better-sqlite3');
12858
+ const Ctor = mod.default ?? mod;
12859
+ if (typeof Ctor !== "function") {
12860
+ throw new Error(`better-sqlite3 export is not a constructor (got ${typeof Ctor})`);
12861
+ }
12862
+ return new Ctor(filePath);
12863
+ } catch (cause) {
12864
+ const message = cause instanceof Error ? cause.message : String(cause);
12865
+ throw new ConfigurationError(
12866
+ `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
12867
+ { code: "sqlite_driver_unavailable", cause }
12868
+ );
12869
+ }
12870
+ }
12871
+ function isCorruptionError(cause) {
12872
+ if (!(cause instanceof Error)) return false;
12873
+ const msg = cause.message.toLowerCase();
12874
+ return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
12875
+ }
12876
+ async function renameAside(filePath, label) {
12877
+ const asidePath = `${filePath}.corrupt-${Date.now()}`;
12878
+ await promises.rename(filePath, asidePath).catch(() => void 0);
12879
+ await promises.rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
12880
+ await promises.rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
12881
+ process.stderr.write(
12882
+ `[theokit-sdk] ${label} database corrupt; renamed aside to ${asidePath} and rebuilt schema
12883
+ `
12884
+ );
12885
+ }
12886
+
12778
12887
  // src/internal/memory/index-schema.ts
12779
12888
  var SCHEMA_STATEMENTS = [
12780
12889
  `CREATE TABLE IF NOT EXISTS files (
@@ -12818,52 +12927,15 @@ var PRAGMA_STATEMENTS = [
12818
12927
 
12819
12928
  // src/internal/memory/index-db.ts
12820
12929
  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);
12930
+ return openSqliteResilient({
12931
+ filePath: opts.filePath,
12932
+ label: "memory-index",
12933
+ recoverCorrupt: opts.recoverCorrupt,
12934
+ onOpen: (db) => {
12935
+ for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
12936
+ for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
12828
12937
  }
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
- );
12938
+ });
12867
12939
  }
12868
12940
  function defaultIndexPath(cwd) {
12869
12941
  return path.join(cwd, ".theokit", "memory", ".index", "memory.sqlite");