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