@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.cjs CHANGED
@@ -22,6 +22,24 @@ var __export = (target, all) => {
22
22
  __defProp(target, name, { get: all[name], enumerable: true });
23
23
  };
24
24
 
25
+ // src/internal/default-retriable.ts
26
+ function defaultRetriableForCode(code) {
27
+ switch (code) {
28
+ case "rate_limit":
29
+ case "timeout":
30
+ case "server_error":
31
+ case "network":
32
+ case "provider_unreachable":
33
+ return true;
34
+ default:
35
+ return false;
36
+ }
37
+ }
38
+ var init_default_retriable = __esm({
39
+ "src/internal/default-retriable.ts"() {
40
+ }
41
+ });
42
+
25
43
  // src/internal/security/redact.ts
26
44
  function readEnvOnce() {
27
45
  const raw = process.env.THEOKIT_REDACT_SECRETS;
@@ -172,7 +190,8 @@ __export(errors_exports, {
172
190
  UnsupportedBudgetOperationError: () => UnsupportedBudgetOperationError,
173
191
  UnsupportedRunOperationError: () => UnsupportedRunOperationError,
174
192
  UnsupportedTaskOperationError: () => UnsupportedTaskOperationError,
175
- coerceToKnownAgentRunErrorCode: () => coerceToKnownAgentRunErrorCode
193
+ coerceToKnownAgentRunErrorCode: () => coerceToKnownAgentRunErrorCode,
194
+ isTransientError: () => isTransientError
176
195
  });
177
196
  function coerceToKnownAgentRunErrorCode(code) {
178
197
  if (code !== void 0 && KNOWN_AGENT_RUN_ERROR_CODES.has(code)) {
@@ -204,21 +223,13 @@ function safeStringify(value) {
204
223
  return String(value);
205
224
  }
206
225
  }
207
- function defaultRetriableForCode(code) {
208
- switch (code) {
209
- case "rate_limit":
210
- case "timeout":
211
- case "server_error":
212
- case "network":
213
- case "provider_unreachable":
214
- return true;
215
- default:
216
- return false;
217
- }
226
+ function isTransientError(err) {
227
+ return err instanceof TheokitAgentError && err.isRetryable === true;
218
228
  }
219
229
  var KNOWN_AGENT_RUN_ERROR_CODES, TheokitAgentError, AuthenticationError, RateLimitError, ConfigurationError, IntegrationNotConnectedError, NetworkError, UnknownAgentError, AgentRunError, UnsupportedRunOperationError, CredentialPoolExhaustedError, MemoryAdapterError, InvalidTaskIdError, TaskNotFoundError, UnsupportedTaskOperationError, BudgetExceededError, AgentDisposedError, UnsupportedBudgetOperationError;
220
230
  var init_errors = __esm({
221
231
  "src/errors.ts"() {
232
+ init_default_retriable();
222
233
  init_redact();
223
234
  KNOWN_AGENT_RUN_ERROR_CODES = /* @__PURE__ */ new Set([
224
235
  "rate_limit",
@@ -817,6 +828,49 @@ var init_async_local_storage = __esm({
817
828
  }
818
829
  });
819
830
 
831
+ // src/internal/runtime/concurrency/async-semaphore.ts
832
+ function createSemaphore(permits) {
833
+ if (!Number.isInteger(permits) || permits < 1) {
834
+ throw new ConfigurationError(
835
+ `async-semaphore: permits must be a positive integer, got ${permits}`,
836
+ { code: "invalid_concurrency" }
837
+ );
838
+ }
839
+ let active = 0;
840
+ const queue = [];
841
+ function tryGrant() {
842
+ if (active < permits && queue.length > 0) {
843
+ const resolve3 = queue.shift();
844
+ if (resolve3 !== void 0) {
845
+ active += 1;
846
+ resolve3();
847
+ }
848
+ }
849
+ }
850
+ return {
851
+ inFlight: () => active,
852
+ pending: () => queue.length + active,
853
+ async acquire() {
854
+ await new Promise((resolve3) => {
855
+ queue.push(resolve3);
856
+ tryGrant();
857
+ });
858
+ let released = false;
859
+ return () => {
860
+ if (released) return;
861
+ released = true;
862
+ active -= 1;
863
+ tryGrant();
864
+ };
865
+ }
866
+ };
867
+ }
868
+ var init_async_semaphore = __esm({
869
+ "src/internal/runtime/concurrency/async-semaphore.ts"() {
870
+ init_errors();
871
+ }
872
+ });
873
+
820
874
  // src/internal/llm/credential-pool-types.ts
821
875
  var COOLDOWN_MS, DEFAULT_COOLDOWN_MS;
822
876
  var init_credential_pool_types = __esm({
@@ -1468,49 +1522,6 @@ var init_task = __esm({
1468
1522
  }
1469
1523
  });
1470
1524
 
1471
- // src/internal/runtime/concurrency/async-semaphore.ts
1472
- function createSemaphore(permits) {
1473
- if (!Number.isInteger(permits) || permits < 1) {
1474
- throw new ConfigurationError(
1475
- `async-semaphore: permits must be a positive integer, got ${permits}`,
1476
- { code: "invalid_concurrency" }
1477
- );
1478
- }
1479
- let active = 0;
1480
- const queue = [];
1481
- function tryGrant() {
1482
- if (active < permits && queue.length > 0) {
1483
- const resolve3 = queue.shift();
1484
- if (resolve3 !== void 0) {
1485
- active += 1;
1486
- resolve3();
1487
- }
1488
- }
1489
- }
1490
- return {
1491
- inFlight: () => active,
1492
- pending: () => queue.length + active,
1493
- async acquire() {
1494
- await new Promise((resolve3) => {
1495
- queue.push(resolve3);
1496
- tryGrant();
1497
- });
1498
- let released = false;
1499
- return () => {
1500
- if (released) return;
1501
- released = true;
1502
- active -= 1;
1503
- tryGrant();
1504
- };
1505
- }
1506
- };
1507
- }
1508
- var init_async_semaphore = __esm({
1509
- "src/internal/runtime/concurrency/async-semaphore.ts"() {
1510
- init_errors();
1511
- }
1512
- });
1513
-
1514
1525
  // src/internal/task/ring-buffer.ts
1515
1526
  var RingBuffer;
1516
1527
  var init_ring_buffer = __esm({
@@ -3082,6 +3093,19 @@ function sanitizeIdentifier(input, options) {
3082
3093
  }
3083
3094
  return input.toLowerCase();
3084
3095
  }
3096
+ function safeFilenameForId(id, options) {
3097
+ if (id.length === 0) {
3098
+ throw new ConfigurationError("Filename id must be a non-empty string", {
3099
+ code: "invalid_filename_id"
3100
+ });
3101
+ }
3102
+ const maxLen = options?.maxLen;
3103
+ const lower = id.toLowerCase();
3104
+ if (lower.length <= maxLen && IDENTIFIER_PATTERN.test(lower)) {
3105
+ return lower;
3106
+ }
3107
+ return `h-${crypto.createHash("sha256").update(id).digest("hex").slice(0, 16)}`;
3108
+ }
3085
3109
 
3086
3110
  // src/internal/runtime/config/default-model.ts
3087
3111
  var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
@@ -4291,8 +4315,7 @@ var FixtureRunBase = class {
4291
4315
  if (status === "error" && this.script.errorDetail !== void 0) {
4292
4316
  base.error = this.script.errorDetail;
4293
4317
  }
4294
- if (this.script.usage !== void 0) base.usage = this.script.usage;
4295
- if (this.script.cost !== void 0) base.cost = this.script.cost;
4318
+ applyScriptMetrics(base, this.script);
4296
4319
  return this.extendRunResult(applyExtraRunFields(base, this.script));
4297
4320
  }
4298
4321
  /** Subclasses override to attach runtime-specific fields (e.g. cloud git info). */
@@ -4326,6 +4349,11 @@ function makeNotifier() {
4326
4349
  });
4327
4350
  return { promise, resolve: resolve3 };
4328
4351
  }
4352
+ function applyScriptMetrics(base, script) {
4353
+ if (script.usage !== void 0) base.usage = script.usage;
4354
+ if (script.cost !== void 0) base.cost = script.cost;
4355
+ if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
4356
+ }
4329
4357
 
4330
4358
  // src/internal/runtime/cloud/cloud-run.ts
4331
4359
  function createCloudRun(options) {
@@ -5794,10 +5822,7 @@ function sessionsDir(cwd) {
5794
5822
  return path.join(memoryDir(cwd), "sessions");
5795
5823
  }
5796
5824
  function sessionSummaryPath(cwd, runId) {
5797
- return path.join(sessionsDir(cwd), `${sanitizeRunId(runId)}.md`);
5798
- }
5799
- function sanitizeRunId(runId) {
5800
- return runId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 128);
5825
+ return path.join(sessionsDir(cwd), `${safeFilenameForId(runId, { maxLen: 128 })}.md`);
5801
5826
  }
5802
5827
  function truncate(text) {
5803
5828
  if (text.length <= MAX_TURN_CHARS) return text;
@@ -7961,6 +7986,9 @@ var LocalRun = class extends FixtureRunBase {
7961
7986
  }
7962
7987
  };
7963
7988
 
7989
+ // src/internal/runtime/local-agent/real-local-run.ts
7990
+ init_errors();
7991
+
7964
7992
  // src/internal/runtime/budget/budget.ts
7965
7993
  var IterationBudget = class {
7966
7994
  #remaining;
@@ -8414,6 +8442,27 @@ async function emitTextDeltaCallback(inputs, text) {
8414
8442
  // src/internal/agent-loop/tool-dispatch.ts
8415
8443
  init_async_local_storage();
8416
8444
 
8445
+ // src/internal/runtime/concurrency/map-with-concurrency.ts
8446
+ init_async_semaphore();
8447
+ var NEVER_ABORT = new AbortController().signal;
8448
+ async function mapWithConcurrency(items, concurrency, fn, options) {
8449
+ const semaphore = createSemaphore(concurrency);
8450
+ const signal = NEVER_ABORT;
8451
+ return Promise.all(
8452
+ items.map(async (item, index) => {
8453
+ const release = await semaphore.acquire();
8454
+ try {
8455
+ if (signal.aborted) {
8456
+ throw signal.reason instanceof Error ? signal.reason : new Error("mapWithConcurrency: aborted");
8457
+ }
8458
+ return await fn(item, index, signal);
8459
+ } finally {
8460
+ release();
8461
+ }
8462
+ })
8463
+ );
8464
+ }
8465
+
8417
8466
  // src/internal/tool-dispatch/repair-middleware.ts
8418
8467
  var DECIMAL_RE = /^-?\d+(\.\d+)?$/;
8419
8468
  function repairToolCall(raw, registry) {
@@ -8588,38 +8637,12 @@ ${result.stderr}`.trim();
8588
8637
  // src/internal/agent-loop/tool-dispatch.ts
8589
8638
  async function dispatchTools(inputs, tools, toolCalls, events) {
8590
8639
  const maxConcurrent = inputs.maxConcurrentTools ?? 4;
8591
- return boundedParallel(
8592
- maxConcurrent,
8640
+ return mapWithConcurrency(
8593
8641
  toolCalls,
8642
+ maxConcurrent,
8594
8643
  (call) => dispatchSingleCall(inputs, tools, call, events)
8595
8644
  );
8596
8645
  }
8597
- async function boundedParallel(max, items, fn) {
8598
- let running = 0;
8599
- const queue = [];
8600
- async function acquire() {
8601
- if (running < max) {
8602
- running++;
8603
- return;
8604
- }
8605
- await new Promise((resolve3) => queue.push(resolve3));
8606
- running++;
8607
- }
8608
- function release() {
8609
- running--;
8610
- if (queue.length > 0) queue.shift()();
8611
- }
8612
- return Promise.all(
8613
- items.map(async (item) => {
8614
- await acquire();
8615
- try {
8616
- return await fn(item);
8617
- } finally {
8618
- release();
8619
- }
8620
- })
8621
- );
8622
- }
8623
8646
  async function dispatchSingleCall(inputs, tools, call, events) {
8624
8647
  const { call: workingCall, repairs } = applyRepairAndExtractCall(tools, call);
8625
8648
  const callId = generateCallId();
@@ -9190,6 +9213,7 @@ async function runAgentLoop(inputs) {
9190
9213
  const ctx = await initLoopContext(inputs);
9191
9214
  ctxRef = ctx;
9192
9215
  const budget = inputs.budget ?? new IterationBudget({ maxIterations: inputs.maxIterations ?? 8 });
9216
+ let lastTurnDecision;
9193
9217
  while (budget.shouldContinue()) {
9194
9218
  if (inputs.budgetTracker !== void 0) {
9195
9219
  const decision2 = evaluateBudgetGate(inputs.budgetTracker);
@@ -9198,18 +9222,26 @@ async function runAgentLoop(inputs) {
9198
9222
  if (decision2.detail !== void 0) {
9199
9223
  ctx.error = { message: decision2.detail, code: decision2.reason ?? "budget" };
9200
9224
  }
9225
+ if (decision2.reason === "iteration_limit") {
9226
+ ctx.stoppedAtIterationLimit = true;
9227
+ }
9201
9228
  break;
9202
9229
  }
9203
9230
  }
9204
9231
  const usingGrace = budget.remaining <= 0 && !budget.graceCallUsed;
9205
9232
  if (usingGrace) budget.useGraceCall();
9206
9233
  const decision = await runIteration(inputs, ctx);
9234
+ lastTurnDecision = decision;
9207
9235
  if (decision === "done") break;
9208
9236
  if (decision === "error") {
9209
9237
  ctx.finalStatus = "error";
9210
9238
  break;
9211
9239
  }
9212
9240
  budget.consume();
9241
+ inputs.budgetTracker?.nextIteration?.();
9242
+ }
9243
+ if (lastTurnDecision === "continue" && budget.shouldContinue() === false) {
9244
+ ctx.stoppedAtIterationLimit = true;
9213
9245
  }
9214
9246
  if (budget.shouldContinue() === false && ctx.finalStatus === "finished" && ctx.finalText === "") {
9215
9247
  ctx.finalStatus = "error";
@@ -9240,7 +9272,8 @@ async function runAgentLoop(inputs) {
9240
9272
  conversation: ctx.conversation,
9241
9273
  ...usage !== void 0 ? { usage } : {},
9242
9274
  ...cost !== void 0 ? { cost } : {},
9243
- ...ctx.error !== void 0 ? { error: ctx.error } : {}
9275
+ ...ctx.error !== void 0 ? { error: ctx.error } : {},
9276
+ ...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {}
9244
9277
  };
9245
9278
  } finally {
9246
9279
  if (ctxRef !== void 0 && ctxRef.memoryProviderHandle !== void 0 && inputs.memoryProvider !== void 0) {
@@ -11553,6 +11586,16 @@ function resolveMcpCwd(configCwd) {
11553
11586
  return safePathJoin(process.cwd(), configCwd);
11554
11587
  }
11555
11588
 
11589
+ // src/internal/providers/register-plugin-providers.ts
11590
+ function registerPluginProviderProfiles(entries) {
11591
+ let registered4 = 0;
11592
+ for (const entry of entries) {
11593
+ registerProvider(entry.profile);
11594
+ registered4 += 1;
11595
+ }
11596
+ return registered4;
11597
+ }
11598
+
11556
11599
  // src/internal/tool-registry/personality-filter.ts
11557
11600
  init_hooks_source();
11558
11601
  function applyPersonalityFilter(exposedTools, whitelist, opts) {
@@ -11629,12 +11672,34 @@ function createRealLocalRun(options) {
11629
11672
  registerRun(handle);
11630
11673
  return handle;
11631
11674
  }
11632
- function buildLoopInputs(options, runId, userText) {
11675
+ var pluginProvidersAnnounced = false;
11676
+ function resolveRunProvider(options) {
11633
11677
  registerBuiltins();
11678
+ const profiles = options.pluginManager?.aggregated.providerProfiles ?? [];
11679
+ const registered4 = registerPluginProviderProfiles(profiles);
11680
+ if (registered4 > 0 && !pluginProvidersAnnounced) {
11681
+ pluginProvidersAnnounced = true;
11682
+ const names = profiles.map((e) => e.profile.name).join(", ");
11683
+ process.stderr.write(
11684
+ `[theokit-sdk] registered ${registered4} plugin provider profile(s): ${names}
11685
+ `
11686
+ );
11687
+ }
11634
11688
  const parsedModel = parseModelId(options.model?.id);
11635
11689
  const inferredProvider = parsedModel.provider !== void 0 && getProviderProfile(parsedModel.provider) !== void 0 ? parsedModel.provider : void 0;
11636
11690
  const primary = options.agentOptions.providers?.routes?.[0]?.provider ?? inferredProvider ?? detectPrimaryProvider();
11637
11691
  const effectiveModelId = inferredProvider !== void 0 ? parsedModel.name : options.model?.id ?? "claude-sonnet-4-6";
11692
+ return { primary, effectiveModelId };
11693
+ }
11694
+ function buildLoopInputs(options, runId, userText) {
11695
+ const maxIterations = options.sendOptions.maxIterations;
11696
+ if (maxIterations !== void 0 && (!Number.isInteger(maxIterations) || maxIterations < 1)) {
11697
+ throw new ConfigurationError(
11698
+ `SendOptions.maxIterations must be a positive integer, got ${maxIterations}`,
11699
+ { code: "invalid_max_iterations" }
11700
+ );
11701
+ }
11702
+ const { primary, effectiveModelId } = resolveRunProvider(options);
11638
11703
  const fallback = options.agentOptions.providers?.fallback;
11639
11704
  const apiKeys = options.agentOptions.providers?.apiKeys;
11640
11705
  const credentialPoolStrategy = options.agentOptions.providers?.credentialPoolStrategy;
@@ -11672,6 +11737,9 @@ function buildLoopInputs(options, runId, userText) {
11672
11737
  // D318 — forward SendOptions.signal to the agent loop so streamLlmTurn
11673
11738
  // can attach it to the LLM `fetch({ signal })` call.
11674
11739
  ...options.sendOptions.signal !== void 0 ? { signal: options.sendOptions.signal } : {},
11740
+ // M1-2: per-send iteration ceiling (validated above). The loop reads
11741
+ // inputs.maxIterations (default 8 when unset).
11742
+ ...maxIterations !== void 0 ? { maxIterations } : {},
11675
11743
  // D315-D317 — tool lifecycle hooks (cost tracking + audit + retry/alert)
11676
11744
  ...options.agentOptions.onToolStart !== void 0 ? { onToolStart: options.agentOptions.onToolStart } : {},
11677
11745
  ...options.agentOptions.onToolEnd !== void 0 ? { onToolEnd: options.agentOptions.onToolEnd } : {},
@@ -11803,6 +11871,7 @@ var RealLocalRun = class extends FixtureRunBase {
11803
11871
  if (output.result.length > 0) this.script.result = output.result;
11804
11872
  if (output.usage !== void 0) this.script.usage = output.usage;
11805
11873
  if (output.cost !== void 0) this.script.cost = output.cost;
11874
+ if (output.stoppedAtIterationLimit === true) this.script.stoppedAtIterationLimit = true;
11806
11875
  if (output.error !== void 0 && this.script.errorDetail === void 0) {
11807
11876
  this.script.errorDetail = {
11808
11877
  message: output.error.message,
@@ -12252,7 +12321,7 @@ async function embedTexts(input) {
12252
12321
  pending
12253
12322
  });
12254
12323
  }
12255
- await runBatches(input, pending, results);
12324
+ await embedInBoundedBatches(input, pending, results);
12256
12325
  return results.map((v) => v ?? new Array(dimension).fill(0));
12257
12326
  }
12258
12327
  function classifyEntry(args) {
@@ -12271,45 +12340,34 @@ function classifyEntry(args) {
12271
12340
  args.pending.push({ index: args.index, text: args.text, key });
12272
12341
  }
12273
12342
  var MAX_CONCURRENT_BATCHES = 3;
12274
- async function runBatches(input, pending, results) {
12343
+ async function embedInBoundedBatches(input, pending, results) {
12275
12344
  const batches = [];
12276
12345
  for (let offset = 0; offset < pending.length; offset += MAX_BATCH) {
12277
12346
  batches.push(pending.slice(offset, offset + MAX_BATCH));
12278
12347
  }
12279
- let running = 0;
12280
- const queue = [];
12281
- await Promise.all(batches.map((batch) => processBatch(input, batch, results, acquire, release)));
12282
- async function acquire() {
12283
- if (running >= MAX_CONCURRENT_BATCHES) await new Promise((r) => queue.push(r));
12284
- running++;
12285
- }
12286
- function release() {
12287
- running--;
12288
- if (queue.length > 0) queue.shift()();
12289
- }
12348
+ await mapWithConcurrency(
12349
+ batches,
12350
+ MAX_CONCURRENT_BATCHES,
12351
+ (batch) => processBatch(input, batch, results)
12352
+ );
12290
12353
  }
12291
- async function processBatch(input, batch, results, acquire, release) {
12292
- await acquire();
12293
- try {
12294
- const vectors = await embedBatch({
12295
- apiKey: input.apiKey,
12296
- baseUrl: input.baseUrl,
12297
- embeddingsPath: input.embeddingsPath,
12298
- model: input.model,
12299
- inputs: batch.map((b) => b.text),
12300
- fetchImpl: input.fetchImpl,
12301
- stats: input.stats,
12302
- providerId: input.providerId
12303
- });
12304
- for (let j = 0; j < batch.length; j++) {
12305
- const slot = batch[j];
12306
- const vector = vectors[j];
12307
- if (slot === void 0 || vector === void 0) continue;
12308
- results[slot.index] = vector;
12309
- input.cache.set(slot.key, vector);
12310
- }
12311
- } finally {
12312
- release();
12354
+ async function processBatch(input, batch, results) {
12355
+ const vectors = await embedBatch({
12356
+ apiKey: input.apiKey,
12357
+ baseUrl: input.baseUrl,
12358
+ embeddingsPath: input.embeddingsPath,
12359
+ model: input.model,
12360
+ inputs: batch.map((b) => b.text),
12361
+ fetchImpl: input.fetchImpl,
12362
+ stats: input.stats,
12363
+ providerId: input.providerId
12364
+ });
12365
+ for (let j = 0; j < batch.length; j++) {
12366
+ const slot = batch[j];
12367
+ const vector = vectors[j];
12368
+ if (slot === void 0 || vector === void 0) continue;
12369
+ results[slot.index] = vector;
12370
+ input.cache.set(slot.key, vector);
12313
12371
  }
12314
12372
  }
12315
12373
  async function embedBatch(opts) {
@@ -12742,7 +12800,7 @@ function sanitizeFts5Query(query) {
12742
12800
  return text.trim();
12743
12801
  }
12744
12802
 
12745
- // src/internal/memory/index-db.ts
12803
+ // src/internal/persistence/sqlite-open.ts
12746
12804
  init_errors();
12747
12805
 
12748
12806
  // src/internal/persistence/sqlite-wal.ts
@@ -12770,6 +12828,57 @@ function logFallback(label, reason) {
12770
12828
  );
12771
12829
  }
12772
12830
 
12831
+ // src/internal/persistence/sqlite-open.ts
12832
+ async function openSqliteResilient(options) {
12833
+ await promises.mkdir(path.dirname(options.filePath), { recursive: true });
12834
+ try {
12835
+ return await openConcrete(options);
12836
+ } catch (cause) {
12837
+ if (options.recoverCorrupt !== false && isCorruptionError(cause)) {
12838
+ await renameAside(options.filePath, options.label ?? "sqlite");
12839
+ return await openConcrete(options);
12840
+ }
12841
+ throw cause;
12842
+ }
12843
+ }
12844
+ async function openConcrete(options) {
12845
+ const db = await loadDriver(options.filePath);
12846
+ applyWalWithFallback(db, options.label ?? "sqlite");
12847
+ await options.onOpen?.(db);
12848
+ return db;
12849
+ }
12850
+ async function loadDriver(filePath) {
12851
+ try {
12852
+ const mod = await import('better-sqlite3');
12853
+ const Ctor = mod.default ?? mod;
12854
+ if (typeof Ctor !== "function") {
12855
+ throw new Error(`better-sqlite3 export is not a constructor (got ${typeof Ctor})`);
12856
+ }
12857
+ return new Ctor(filePath);
12858
+ } catch (cause) {
12859
+ const message = cause instanceof Error ? cause.message : String(cause);
12860
+ throw new ConfigurationError(
12861
+ `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
12862
+ { code: "sqlite_driver_unavailable", cause }
12863
+ );
12864
+ }
12865
+ }
12866
+ function isCorruptionError(cause) {
12867
+ if (!(cause instanceof Error)) return false;
12868
+ const msg = cause.message.toLowerCase();
12869
+ return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
12870
+ }
12871
+ async function renameAside(filePath, label) {
12872
+ const asidePath = `${filePath}.corrupt-${Date.now()}`;
12873
+ await promises.rename(filePath, asidePath).catch(() => void 0);
12874
+ await promises.rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
12875
+ await promises.rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
12876
+ process.stderr.write(
12877
+ `[theokit-sdk] ${label} database corrupt; renamed aside to ${asidePath} and rebuilt schema
12878
+ `
12879
+ );
12880
+ }
12881
+
12773
12882
  // src/internal/memory/index-schema.ts
12774
12883
  var SCHEMA_STATEMENTS = [
12775
12884
  `CREATE TABLE IF NOT EXISTS files (
@@ -12813,52 +12922,15 @@ var PRAGMA_STATEMENTS = [
12813
12922
 
12814
12923
  // src/internal/memory/index-db.ts
12815
12924
  async function openMemoryDb(opts) {
12816
- await promises.mkdir(path.dirname(opts.filePath), { recursive: true });
12817
- try {
12818
- return await openConcrete(opts.filePath);
12819
- } catch (cause) {
12820
- if (opts.recoverCorrupt !== false && isCorruptionError(cause)) {
12821
- await renameAside(opts.filePath);
12822
- return await openConcrete(opts.filePath);
12925
+ return openSqliteResilient({
12926
+ filePath: opts.filePath,
12927
+ label: "memory-index",
12928
+ recoverCorrupt: opts.recoverCorrupt,
12929
+ onOpen: (db) => {
12930
+ for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
12931
+ for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
12823
12932
  }
12824
- throw cause;
12825
- }
12826
- }
12827
- async function openConcrete(filePath) {
12828
- const db = await loadDriver(filePath);
12829
- applyWalWithFallback(db, "memory-index");
12830
- for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
12831
- for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
12832
- return db;
12833
- }
12834
- async function loadDriver(filePath) {
12835
- try {
12836
- const mod = await import('better-sqlite3');
12837
- const Ctor = mod.default ?? mod;
12838
- const db = new Ctor(filePath);
12839
- return db;
12840
- } catch (cause) {
12841
- const message = cause instanceof Error ? cause.message : String(cause);
12842
- throw new ConfigurationError(
12843
- `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
12844
- { code: "sqlite_driver_unavailable", cause }
12845
- );
12846
- }
12847
- }
12848
- function isCorruptionError(cause) {
12849
- if (!(cause instanceof Error)) return false;
12850
- const msg = cause.message.toLowerCase();
12851
- return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
12852
- }
12853
- async function renameAside(filePath) {
12854
- const asidePath = `${filePath}.corrupt-${Date.now()}`;
12855
- await promises.rename(filePath, asidePath).catch(() => void 0);
12856
- await promises.rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
12857
- await promises.rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
12858
- process.stderr.write(
12859
- `[theokit-sdk] memory index corrupt; renamed aside to ${asidePath} and rebuilt schema
12860
- `
12861
- );
12933
+ });
12862
12934
  }
12863
12935
  function defaultIndexPath(cwd) {
12864
12936
  return path.join(cwd, ".theokit", "memory", ".index", "memory.sqlite");