@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/index.js CHANGED
@@ -1,7 +1,7 @@
1
+ import { createHash, randomUUID, randomBytes } from 'crypto';
1
2
  import { existsSync, rmSync, mkdirSync, renameSync, readFileSync, realpathSync, lstatSync, readlinkSync, readdirSync } from 'fs';
2
3
  import { join, dirname, relative, resolve, sep, isAbsolute } from 'path';
3
- import { createHash, randomUUID, randomBytes } from 'crypto';
4
- import { readFile, stat, rm, readdir, mkdir, appendFile, rename, open, unlink, statfs, access } from 'fs/promises';
4
+ import { readFile, stat, rm, readdir, mkdir, appendFile, open, rename, unlink, statfs, access } from 'fs/promises';
5
5
  import { z, toJSONSchema } from 'zod';
6
6
  import { AsyncLocalStorage } from 'async_hooks';
7
7
  import { createRequire } from 'module';
@@ -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;
@@ -176,7 +194,8 @@ __export(errors_exports, {
176
194
  UnsupportedBudgetOperationError: () => UnsupportedBudgetOperationError,
177
195
  UnsupportedRunOperationError: () => UnsupportedRunOperationError,
178
196
  UnsupportedTaskOperationError: () => UnsupportedTaskOperationError,
179
- coerceToKnownAgentRunErrorCode: () => coerceToKnownAgentRunErrorCode
197
+ coerceToKnownAgentRunErrorCode: () => coerceToKnownAgentRunErrorCode,
198
+ isTransientError: () => isTransientError
180
199
  });
181
200
  function coerceToKnownAgentRunErrorCode(code) {
182
201
  if (code !== void 0 && KNOWN_AGENT_RUN_ERROR_CODES.has(code)) {
@@ -208,21 +227,13 @@ function safeStringify(value) {
208
227
  return String(value);
209
228
  }
210
229
  }
211
- function defaultRetriableForCode(code) {
212
- switch (code) {
213
- case "rate_limit":
214
- case "timeout":
215
- case "server_error":
216
- case "network":
217
- case "provider_unreachable":
218
- return true;
219
- default:
220
- return false;
221
- }
230
+ function isTransientError(err) {
231
+ return err instanceof TheokitAgentError && err.isRetryable === true;
222
232
  }
223
233
  var KNOWN_AGENT_RUN_ERROR_CODES, TheokitAgentError, AuthenticationError, RateLimitError, ConfigurationError, IntegrationNotConnectedError, NetworkError, UnknownAgentError, AgentRunError, UnsupportedRunOperationError, CredentialPoolExhaustedError, MemoryAdapterError, InvalidTaskIdError, TaskNotFoundError, UnsupportedTaskOperationError, BudgetExceededError, AgentDisposedError, UnsupportedBudgetOperationError;
224
234
  var init_errors = __esm({
225
235
  "src/errors.ts"() {
236
+ init_default_retriable();
226
237
  init_redact();
227
238
  KNOWN_AGENT_RUN_ERROR_CODES = /* @__PURE__ */ new Set([
228
239
  "rate_limit",
@@ -646,6 +657,19 @@ function sanitizeIdentifier(input, options) {
646
657
  }
647
658
  return input.toLowerCase();
648
659
  }
660
+ function safeFilenameForId(id, options) {
661
+ if (id.length === 0) {
662
+ throw new ConfigurationError("Filename id must be a non-empty string", {
663
+ code: "invalid_filename_id"
664
+ });
665
+ }
666
+ const maxLen = options?.maxLen;
667
+ const lower = id.toLowerCase();
668
+ if (lower.length <= maxLen && IDENTIFIER_PATTERN.test(lower)) {
669
+ return lower;
670
+ }
671
+ return `h-${createHash("sha256").update(id).digest("hex").slice(0, 16)}`;
672
+ }
649
673
  var PathTraversalError, IDENTIFIER_PATTERN;
650
674
  var init_path_guard = __esm({
651
675
  "src/internal/security/path-guard.ts"() {
@@ -1048,10 +1072,7 @@ function sessionsDir(cwd) {
1048
1072
  return join(memoryDir(cwd), "sessions");
1049
1073
  }
1050
1074
  function sessionSummaryPath(cwd, runId) {
1051
- return join(sessionsDir(cwd), `${sanitizeRunId(runId)}.md`);
1052
- }
1053
- function sanitizeRunId(runId) {
1054
- return runId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 128);
1075
+ return join(sessionsDir(cwd), `${safeFilenameForId(runId, { maxLen: 128 })}.md`);
1055
1076
  }
1056
1077
  function truncate(text) {
1057
1078
  if (text.length <= MAX_TURN_CHARS) return text;
@@ -1087,6 +1108,7 @@ var MAX_TURN_CHARS;
1087
1108
  var init_session_summary_writer = __esm({
1088
1109
  "src/internal/memory/storage/session-summary-writer.ts"() {
1089
1110
  init_atomic_write();
1111
+ init_path_guard();
1090
1112
  init_types();
1091
1113
  init_markdown_store();
1092
1114
  MAX_TURN_CHARS = 2e3;
@@ -1116,6 +1138,49 @@ var init_async_local_storage = __esm({
1116
1138
  }
1117
1139
  });
1118
1140
 
1141
+ // src/internal/runtime/concurrency/async-semaphore.ts
1142
+ function createSemaphore(permits) {
1143
+ if (!Number.isInteger(permits) || permits < 1) {
1144
+ throw new ConfigurationError(
1145
+ `async-semaphore: permits must be a positive integer, got ${permits}`,
1146
+ { code: "invalid_concurrency" }
1147
+ );
1148
+ }
1149
+ let active = 0;
1150
+ const queue = [];
1151
+ function tryGrant() {
1152
+ if (active < permits && queue.length > 0) {
1153
+ const resolve3 = queue.shift();
1154
+ if (resolve3 !== void 0) {
1155
+ active += 1;
1156
+ resolve3();
1157
+ }
1158
+ }
1159
+ }
1160
+ return {
1161
+ inFlight: () => active,
1162
+ pending: () => queue.length + active,
1163
+ async acquire() {
1164
+ await new Promise((resolve3) => {
1165
+ queue.push(resolve3);
1166
+ tryGrant();
1167
+ });
1168
+ let released = false;
1169
+ return () => {
1170
+ if (released) return;
1171
+ released = true;
1172
+ active -= 1;
1173
+ tryGrant();
1174
+ };
1175
+ }
1176
+ };
1177
+ }
1178
+ var init_async_semaphore = __esm({
1179
+ "src/internal/runtime/concurrency/async-semaphore.ts"() {
1180
+ init_errors();
1181
+ }
1182
+ });
1183
+
1119
1184
  // src/internal/llm/credential-pool-types.ts
1120
1185
  var COOLDOWN_MS, DEFAULT_COOLDOWN_MS;
1121
1186
  var init_credential_pool_types = __esm({
@@ -1497,6 +1562,61 @@ var init_sqlite_wal = __esm({
1497
1562
  warnedLabels = /* @__PURE__ */ new Set();
1498
1563
  }
1499
1564
  });
1565
+ async function openSqliteResilient(options) {
1566
+ await mkdir(dirname(options.filePath), { recursive: true });
1567
+ try {
1568
+ return await openConcrete(options);
1569
+ } catch (cause) {
1570
+ if (options.recoverCorrupt !== false && isCorruptionError(cause)) {
1571
+ await renameAside(options.filePath, options.label ?? "sqlite");
1572
+ return await openConcrete(options);
1573
+ }
1574
+ throw cause;
1575
+ }
1576
+ }
1577
+ async function openConcrete(options) {
1578
+ const db = await loadDriver(options.filePath);
1579
+ applyWalWithFallback(db, options.label ?? "sqlite");
1580
+ await options.onOpen?.(db);
1581
+ return db;
1582
+ }
1583
+ async function loadDriver(filePath) {
1584
+ try {
1585
+ const mod = await import('better-sqlite3');
1586
+ const Ctor = mod.default ?? mod;
1587
+ if (typeof Ctor !== "function") {
1588
+ throw new Error(`better-sqlite3 export is not a constructor (got ${typeof Ctor})`);
1589
+ }
1590
+ return new Ctor(filePath);
1591
+ } catch (cause) {
1592
+ const message = cause instanceof Error ? cause.message : String(cause);
1593
+ throw new ConfigurationError(
1594
+ `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
1595
+ { code: "sqlite_driver_unavailable", cause }
1596
+ );
1597
+ }
1598
+ }
1599
+ function isCorruptionError(cause) {
1600
+ if (!(cause instanceof Error)) return false;
1601
+ const msg = cause.message.toLowerCase();
1602
+ return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
1603
+ }
1604
+ async function renameAside(filePath, label) {
1605
+ const asidePath = `${filePath}.corrupt-${Date.now()}`;
1606
+ await rename(filePath, asidePath).catch(() => void 0);
1607
+ await rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
1608
+ await rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
1609
+ process.stderr.write(
1610
+ `[theokit-sdk] ${label} database corrupt; renamed aside to ${asidePath} and rebuilt schema
1611
+ `
1612
+ );
1613
+ }
1614
+ var init_sqlite_open = __esm({
1615
+ "src/internal/persistence/sqlite-open.ts"() {
1616
+ init_errors();
1617
+ init_sqlite_wal();
1618
+ }
1619
+ });
1500
1620
 
1501
1621
  // src/internal/memory/index-schema.ts
1502
1622
  var SCHEMA_STATEMENTS, PRAGMA_STATEMENTS;
@@ -1544,60 +1664,22 @@ var init_index_schema = __esm({
1544
1664
  }
1545
1665
  });
1546
1666
  async function openMemoryDb(opts) {
1547
- await mkdir(dirname(opts.filePath), { recursive: true });
1548
- try {
1549
- return await openConcrete(opts.filePath);
1550
- } catch (cause) {
1551
- if (opts.recoverCorrupt !== false && isCorruptionError(cause)) {
1552
- await renameAside(opts.filePath);
1553
- return await openConcrete(opts.filePath);
1667
+ return openSqliteResilient({
1668
+ filePath: opts.filePath,
1669
+ label: "memory-index",
1670
+ recoverCorrupt: opts.recoverCorrupt,
1671
+ onOpen: (db) => {
1672
+ for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
1673
+ for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
1554
1674
  }
1555
- throw cause;
1556
- }
1557
- }
1558
- async function openConcrete(filePath) {
1559
- const db = await loadDriver(filePath);
1560
- applyWalWithFallback(db, "memory-index");
1561
- for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
1562
- for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
1563
- return db;
1564
- }
1565
- async function loadDriver(filePath) {
1566
- try {
1567
- const mod = await import('better-sqlite3');
1568
- const Ctor = mod.default ?? mod;
1569
- const db = new Ctor(filePath);
1570
- return db;
1571
- } catch (cause) {
1572
- const message = cause instanceof Error ? cause.message : String(cause);
1573
- throw new ConfigurationError(
1574
- `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
1575
- { code: "sqlite_driver_unavailable", cause }
1576
- );
1577
- }
1578
- }
1579
- function isCorruptionError(cause) {
1580
- if (!(cause instanceof Error)) return false;
1581
- const msg = cause.message.toLowerCase();
1582
- return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
1583
- }
1584
- async function renameAside(filePath) {
1585
- const asidePath = `${filePath}.corrupt-${Date.now()}`;
1586
- await rename(filePath, asidePath).catch(() => void 0);
1587
- await rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
1588
- await rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
1589
- process.stderr.write(
1590
- `[theokit-sdk] memory index corrupt; renamed aside to ${asidePath} and rebuilt schema
1591
- `
1592
- );
1675
+ });
1593
1676
  }
1594
1677
  function defaultIndexPath(cwd) {
1595
1678
  return join(cwd, ".theokit", "memory", ".index", "memory.sqlite");
1596
1679
  }
1597
1680
  var init_index_db = __esm({
1598
1681
  "src/internal/memory/index-db.ts"() {
1599
- init_errors();
1600
- init_sqlite_wal();
1682
+ init_sqlite_open();
1601
1683
  init_index_schema();
1602
1684
  }
1603
1685
  });
@@ -2797,49 +2879,6 @@ var init_task = __esm({
2797
2879
  }
2798
2880
  });
2799
2881
 
2800
- // src/internal/runtime/concurrency/async-semaphore.ts
2801
- function createSemaphore(permits) {
2802
- if (!Number.isInteger(permits) || permits < 1) {
2803
- throw new ConfigurationError(
2804
- `async-semaphore: permits must be a positive integer, got ${permits}`,
2805
- { code: "invalid_concurrency" }
2806
- );
2807
- }
2808
- let active = 0;
2809
- const queue = [];
2810
- function tryGrant() {
2811
- if (active < permits && queue.length > 0) {
2812
- const resolve3 = queue.shift();
2813
- if (resolve3 !== void 0) {
2814
- active += 1;
2815
- resolve3();
2816
- }
2817
- }
2818
- }
2819
- return {
2820
- inFlight: () => active,
2821
- pending: () => queue.length + active,
2822
- async acquire() {
2823
- await new Promise((resolve3) => {
2824
- queue.push(resolve3);
2825
- tryGrant();
2826
- });
2827
- let released = false;
2828
- return () => {
2829
- if (released) return;
2830
- released = true;
2831
- active -= 1;
2832
- tryGrant();
2833
- };
2834
- }
2835
- };
2836
- }
2837
- var init_async_semaphore = __esm({
2838
- "src/internal/runtime/concurrency/async-semaphore.ts"() {
2839
- init_errors();
2840
- }
2841
- });
2842
-
2843
2882
  // src/internal/task/ring-buffer.ts
2844
2883
  var RingBuffer;
2845
2884
  var init_ring_buffer = __esm({
@@ -6765,8 +6804,7 @@ var FixtureRunBase = class {
6765
6804
  if (status === "error" && this.script.errorDetail !== void 0) {
6766
6805
  base.error = this.script.errorDetail;
6767
6806
  }
6768
- if (this.script.usage !== void 0) base.usage = this.script.usage;
6769
- if (this.script.cost !== void 0) base.cost = this.script.cost;
6807
+ applyScriptMetrics(base, this.script);
6770
6808
  return this.extendRunResult(applyExtraRunFields(base, this.script));
6771
6809
  }
6772
6810
  /** Subclasses override to attach runtime-specific fields (e.g. cloud git info). */
@@ -6800,6 +6838,11 @@ function makeNotifier() {
6800
6838
  });
6801
6839
  return { promise, resolve: resolve3 };
6802
6840
  }
6841
+ function applyScriptMetrics(base, script) {
6842
+ if (script.usage !== void 0) base.usage = script.usage;
6843
+ if (script.cost !== void 0) base.cost = script.cost;
6844
+ if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
6845
+ }
6803
6846
 
6804
6847
  // src/internal/runtime/cloud/cloud-run.ts
6805
6848
  function createCloudRun(options) {
@@ -10402,6 +10445,9 @@ var LocalRun = class extends FixtureRunBase {
10402
10445
  }
10403
10446
  };
10404
10447
 
10448
+ // src/internal/runtime/local-agent/real-local-run.ts
10449
+ init_errors();
10450
+
10405
10451
  // src/internal/runtime/budget/budget.ts
10406
10452
  var IterationBudget = class {
10407
10453
  #remaining;
@@ -10855,6 +10901,27 @@ async function emitTextDeltaCallback(inputs, text) {
10855
10901
  // src/internal/agent-loop/tool-dispatch.ts
10856
10902
  init_async_local_storage();
10857
10903
 
10904
+ // src/internal/runtime/concurrency/map-with-concurrency.ts
10905
+ init_async_semaphore();
10906
+ var NEVER_ABORT = new AbortController().signal;
10907
+ async function mapWithConcurrency(items, concurrency, fn, options) {
10908
+ const semaphore = createSemaphore(concurrency);
10909
+ const signal = NEVER_ABORT;
10910
+ return Promise.all(
10911
+ items.map(async (item, index) => {
10912
+ const release = await semaphore.acquire();
10913
+ try {
10914
+ if (signal.aborted) {
10915
+ throw signal.reason instanceof Error ? signal.reason : new Error("mapWithConcurrency: aborted");
10916
+ }
10917
+ return await fn(item, index, signal);
10918
+ } finally {
10919
+ release();
10920
+ }
10921
+ })
10922
+ );
10923
+ }
10924
+
10858
10925
  // src/internal/tool-dispatch/repair-middleware.ts
10859
10926
  var DECIMAL_RE = /^-?\d+(\.\d+)?$/;
10860
10927
  function repairToolCall(raw, registry3) {
@@ -11029,38 +11096,12 @@ ${result.stderr}`.trim();
11029
11096
  // src/internal/agent-loop/tool-dispatch.ts
11030
11097
  async function dispatchTools(inputs, tools, toolCalls, events) {
11031
11098
  const maxConcurrent = inputs.maxConcurrentTools ?? 4;
11032
- return boundedParallel(
11033
- maxConcurrent,
11099
+ return mapWithConcurrency(
11034
11100
  toolCalls,
11101
+ maxConcurrent,
11035
11102
  (call) => dispatchSingleCall(inputs, tools, call, events)
11036
11103
  );
11037
11104
  }
11038
- async function boundedParallel(max, items, fn) {
11039
- let running = 0;
11040
- const queue = [];
11041
- async function acquire() {
11042
- if (running < max) {
11043
- running++;
11044
- return;
11045
- }
11046
- await new Promise((resolve3) => queue.push(resolve3));
11047
- running++;
11048
- }
11049
- function release() {
11050
- running--;
11051
- if (queue.length > 0) queue.shift()();
11052
- }
11053
- return Promise.all(
11054
- items.map(async (item) => {
11055
- await acquire();
11056
- try {
11057
- return await fn(item);
11058
- } finally {
11059
- release();
11060
- }
11061
- })
11062
- );
11063
- }
11064
11105
  async function dispatchSingleCall(inputs, tools, call, events) {
11065
11106
  const { call: workingCall, repairs } = applyRepairAndExtractCall(tools, call);
11066
11107
  const callId = generateCallId();
@@ -11631,6 +11672,7 @@ async function runAgentLoop(inputs) {
11631
11672
  const ctx = await initLoopContext(inputs);
11632
11673
  ctxRef = ctx;
11633
11674
  const budget = inputs.budget ?? new IterationBudget({ maxIterations: inputs.maxIterations ?? 8 });
11675
+ let lastTurnDecision;
11634
11676
  while (budget.shouldContinue()) {
11635
11677
  if (inputs.budgetTracker !== void 0) {
11636
11678
  const decision2 = evaluateBudgetGate(inputs.budgetTracker);
@@ -11639,18 +11681,26 @@ async function runAgentLoop(inputs) {
11639
11681
  if (decision2.detail !== void 0) {
11640
11682
  ctx.error = { message: decision2.detail, code: decision2.reason ?? "budget" };
11641
11683
  }
11684
+ if (decision2.reason === "iteration_limit") {
11685
+ ctx.stoppedAtIterationLimit = true;
11686
+ }
11642
11687
  break;
11643
11688
  }
11644
11689
  }
11645
11690
  const usingGrace = budget.remaining <= 0 && !budget.graceCallUsed;
11646
11691
  if (usingGrace) budget.useGraceCall();
11647
11692
  const decision = await runIteration(inputs, ctx);
11693
+ lastTurnDecision = decision;
11648
11694
  if (decision === "done") break;
11649
11695
  if (decision === "error") {
11650
11696
  ctx.finalStatus = "error";
11651
11697
  break;
11652
11698
  }
11653
11699
  budget.consume();
11700
+ inputs.budgetTracker?.nextIteration?.();
11701
+ }
11702
+ if (lastTurnDecision === "continue" && budget.shouldContinue() === false) {
11703
+ ctx.stoppedAtIterationLimit = true;
11654
11704
  }
11655
11705
  if (budget.shouldContinue() === false && ctx.finalStatus === "finished" && ctx.finalText === "") {
11656
11706
  ctx.finalStatus = "error";
@@ -11681,7 +11731,8 @@ async function runAgentLoop(inputs) {
11681
11731
  conversation: ctx.conversation,
11682
11732
  ...usage !== void 0 ? { usage } : {},
11683
11733
  ...cost !== void 0 ? { cost } : {},
11684
- ...ctx.error !== void 0 ? { error: ctx.error } : {}
11734
+ ...ctx.error !== void 0 ? { error: ctx.error } : {},
11735
+ ...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {}
11685
11736
  };
11686
11737
  } finally {
11687
11738
  if (ctxRef !== void 0 && ctxRef.memoryProviderHandle !== void 0 && inputs.memoryProvider !== void 0) {
@@ -14010,6 +14061,16 @@ function resolveMcpCwd(configCwd) {
14010
14061
  return safePathJoin(process.cwd(), configCwd);
14011
14062
  }
14012
14063
 
14064
+ // src/internal/providers/register-plugin-providers.ts
14065
+ function registerPluginProviderProfiles(entries) {
14066
+ let registered4 = 0;
14067
+ for (const entry of entries) {
14068
+ registerProvider(entry.profile);
14069
+ registered4 += 1;
14070
+ }
14071
+ return registered4;
14072
+ }
14073
+
14013
14074
  // src/internal/tool-registry/personality-filter.ts
14014
14075
  init_hooks_source();
14015
14076
  function applyPersonalityFilter(exposedTools, whitelist, opts) {
@@ -14086,12 +14147,34 @@ function createRealLocalRun(options) {
14086
14147
  registerRun(handle);
14087
14148
  return handle;
14088
14149
  }
14089
- function buildLoopInputs(options, runId, userText) {
14150
+ var pluginProvidersAnnounced = false;
14151
+ function resolveRunProvider(options) {
14090
14152
  registerBuiltins();
14153
+ const profiles = options.pluginManager?.aggregated.providerProfiles ?? [];
14154
+ const registered4 = registerPluginProviderProfiles(profiles);
14155
+ if (registered4 > 0 && !pluginProvidersAnnounced) {
14156
+ pluginProvidersAnnounced = true;
14157
+ const names = profiles.map((e) => e.profile.name).join(", ");
14158
+ process.stderr.write(
14159
+ `[theokit-sdk] registered ${registered4} plugin provider profile(s): ${names}
14160
+ `
14161
+ );
14162
+ }
14091
14163
  const parsedModel = parseModelId(options.model?.id);
14092
14164
  const inferredProvider = parsedModel.provider !== void 0 && getProviderProfile(parsedModel.provider) !== void 0 ? parsedModel.provider : void 0;
14093
14165
  const primary = options.agentOptions.providers?.routes?.[0]?.provider ?? inferredProvider ?? detectPrimaryProvider();
14094
14166
  const effectiveModelId = inferredProvider !== void 0 ? parsedModel.name : options.model?.id ?? "claude-sonnet-4-6";
14167
+ return { primary, effectiveModelId };
14168
+ }
14169
+ function buildLoopInputs(options, runId, userText) {
14170
+ const maxIterations = options.sendOptions.maxIterations;
14171
+ if (maxIterations !== void 0 && (!Number.isInteger(maxIterations) || maxIterations < 1)) {
14172
+ throw new ConfigurationError(
14173
+ `SendOptions.maxIterations must be a positive integer, got ${maxIterations}`,
14174
+ { code: "invalid_max_iterations" }
14175
+ );
14176
+ }
14177
+ const { primary, effectiveModelId } = resolveRunProvider(options);
14095
14178
  const fallback = options.agentOptions.providers?.fallback;
14096
14179
  const apiKeys = options.agentOptions.providers?.apiKeys;
14097
14180
  const credentialPoolStrategy = options.agentOptions.providers?.credentialPoolStrategy;
@@ -14129,6 +14212,9 @@ function buildLoopInputs(options, runId, userText) {
14129
14212
  // D318 — forward SendOptions.signal to the agent loop so streamLlmTurn
14130
14213
  // can attach it to the LLM `fetch({ signal })` call.
14131
14214
  ...options.sendOptions.signal !== void 0 ? { signal: options.sendOptions.signal } : {},
14215
+ // M1-2: per-send iteration ceiling (validated above). The loop reads
14216
+ // inputs.maxIterations (default 8 when unset).
14217
+ ...maxIterations !== void 0 ? { maxIterations } : {},
14132
14218
  // D315-D317 — tool lifecycle hooks (cost tracking + audit + retry/alert)
14133
14219
  ...options.agentOptions.onToolStart !== void 0 ? { onToolStart: options.agentOptions.onToolStart } : {},
14134
14220
  ...options.agentOptions.onToolEnd !== void 0 ? { onToolEnd: options.agentOptions.onToolEnd } : {},
@@ -14260,6 +14346,7 @@ var RealLocalRun = class extends FixtureRunBase {
14260
14346
  if (output.result.length > 0) this.script.result = output.result;
14261
14347
  if (output.usage !== void 0) this.script.usage = output.usage;
14262
14348
  if (output.cost !== void 0) this.script.cost = output.cost;
14349
+ if (output.stoppedAtIterationLimit === true) this.script.stoppedAtIterationLimit = true;
14263
14350
  if (output.error !== void 0 && this.script.errorDetail === void 0) {
14264
14351
  this.script.errorDetail = {
14265
14352
  message: output.error.message,
@@ -14681,7 +14768,7 @@ async function embedTexts(input) {
14681
14768
  pending
14682
14769
  });
14683
14770
  }
14684
- await runBatches(input, pending, results);
14771
+ await embedInBoundedBatches(input, pending, results);
14685
14772
  return results.map((v) => v ?? new Array(dimension).fill(0));
14686
14773
  }
14687
14774
  function classifyEntry(args) {
@@ -14700,45 +14787,34 @@ function classifyEntry(args) {
14700
14787
  args.pending.push({ index: args.index, text: args.text, key: key2 });
14701
14788
  }
14702
14789
  var MAX_CONCURRENT_BATCHES = 3;
14703
- async function runBatches(input, pending, results) {
14790
+ async function embedInBoundedBatches(input, pending, results) {
14704
14791
  const batches = [];
14705
14792
  for (let offset = 0; offset < pending.length; offset += MAX_BATCH) {
14706
14793
  batches.push(pending.slice(offset, offset + MAX_BATCH));
14707
14794
  }
14708
- let running = 0;
14709
- const queue = [];
14710
- await Promise.all(batches.map((batch) => processBatch(input, batch, results, acquire, release)));
14711
- async function acquire() {
14712
- if (running >= MAX_CONCURRENT_BATCHES) await new Promise((r) => queue.push(r));
14713
- running++;
14714
- }
14715
- function release() {
14716
- running--;
14717
- if (queue.length > 0) queue.shift()();
14718
- }
14795
+ await mapWithConcurrency(
14796
+ batches,
14797
+ MAX_CONCURRENT_BATCHES,
14798
+ (batch) => processBatch(input, batch, results)
14799
+ );
14719
14800
  }
14720
- async function processBatch(input, batch, results, acquire, release) {
14721
- await acquire();
14722
- try {
14723
- const vectors = await embedBatch({
14724
- apiKey: input.apiKey,
14725
- baseUrl: input.baseUrl,
14726
- embeddingsPath: input.embeddingsPath,
14727
- model: input.model,
14728
- inputs: batch.map((b) => b.text),
14729
- fetchImpl: input.fetchImpl,
14730
- stats: input.stats,
14731
- providerId: input.providerId
14732
- });
14733
- for (let j = 0; j < batch.length; j++) {
14734
- const slot = batch[j];
14735
- const vector = vectors[j];
14736
- if (slot === void 0 || vector === void 0) continue;
14737
- results[slot.index] = vector;
14738
- input.cache.set(slot.key, vector);
14739
- }
14740
- } finally {
14741
- release();
14801
+ async function processBatch(input, batch, results) {
14802
+ const vectors = await embedBatch({
14803
+ apiKey: input.apiKey,
14804
+ baseUrl: input.baseUrl,
14805
+ embeddingsPath: input.embeddingsPath,
14806
+ model: input.model,
14807
+ inputs: batch.map((b) => b.text),
14808
+ fetchImpl: input.fetchImpl,
14809
+ stats: input.stats,
14810
+ providerId: input.providerId
14811
+ });
14812
+ for (let j = 0; j < batch.length; j++) {
14813
+ const slot = batch[j];
14814
+ const vector = vectors[j];
14815
+ if (slot === void 0 || vector === void 0) continue;
14816
+ results[slot.index] = vector;
14817
+ input.cache.set(slot.key, vector);
14742
14818
  }
14743
14819
  }
14744
14820
  async function embedBatch(opts) {
@@ -17699,6 +17775,16 @@ async function updateJobStatus(jobId, enabled) {
17699
17775
  return updated;
17700
17776
  }
17701
17777
 
17778
+ // src/define-provider.ts
17779
+ function defineProvider(profile, opts) {
17780
+ return {
17781
+ name: profile.name,
17782
+ version: opts?.version ?? "1.0.0",
17783
+ kind: "model-provider",
17784
+ profile
17785
+ };
17786
+ }
17787
+
17702
17788
  // src/define-tool.ts
17703
17789
  init_to_json_schema();
17704
17790
  function defineTool(spec) {
@@ -19208,6 +19294,6 @@ function safeStringify2(v) {
19208
19294
  }
19209
19295
  }
19210
19296
 
19211
- export { Agent, AgentBuilder, AgentDisposedError, AgentRunError, AuthenticationError, Budget, BudgetExceededError, ConfigurationError, Cron, EventBus, FileSystemConversationStorage, GenerateObjectError, InMemoryConversationStorage, IntegrationNotConnectedError, InvalidTaskIdError, JobQueue, Memory, MemoryAdapterError, NetworkError, PermissionEngine, RateLimitError, Security, StreamObjectError, Task, TaskNotFoundError, Theokit, TheokitAgentError, UnknownAgentError, UnsupportedBudgetOperationError, UnsupportedRunOperationError, UnsupportedTaskOperationError, UsageAccumulator, chargeAndCheckThresholds, computeCost, createAgentFactory, createCounterBudgetTracker, createNoopMemoryProvider, createSquad, definePlugin, defineTool, extractRawId, getPricingEntry, inferApiMode, migrateSqliteToLance2 as migrateSqliteToLance, mkMemoryId, normalizeUsage, preflightCheck, toShareGptTrajectory, withCwdMutex };
19297
+ export { Agent, AgentBuilder, AgentDisposedError, AgentRunError, AuthenticationError, Budget, BudgetExceededError, ConfigurationError, Cron, EventBus, FileSystemConversationStorage, GenerateObjectError, InMemoryConversationStorage, IntegrationNotConnectedError, InvalidTaskIdError, JobQueue, Memory, MemoryAdapterError, NetworkError, PermissionEngine, RateLimitError, Security, StreamObjectError, Task, TaskNotFoundError, Theokit, TheokitAgentError, UnknownAgentError, UnsupportedBudgetOperationError, UnsupportedRunOperationError, UnsupportedTaskOperationError, UsageAccumulator, chargeAndCheckThresholds, computeCost, createAgentFactory, createCounterBudgetTracker, createNoopMemoryProvider, createSquad, definePlugin, defineProvider, defineTool, extractRawId, getPricingEntry, inferApiMode, isTransientError, migrateSqliteToLance2 as migrateSqliteToLance, mkMemoryId, normalizeUsage, preflightCheck, toShareGptTrajectory, withCwdMutex };
19212
19298
  //# sourceMappingURL=index.js.map
19213
19299
  //# sourceMappingURL=index.js.map