@theokit/sdk 2.13.1 → 2.15.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 (46) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/a2a/index.cjs +242 -3
  3. package/dist/a2a/index.cjs.map +1 -1
  4. package/dist/a2a/index.js +242 -3
  5. package/dist/a2a/index.js.map +1 -1
  6. package/dist/{cron-CpxLdAXc.d.cts → cron-BxLSz1UH.d.cts} +1 -1
  7. package/dist/{cron-CL_9nfhQ.d.ts → cron-DcaoP7aW.d.ts} +1 -1
  8. package/dist/cron.cjs +225 -3
  9. package/dist/cron.cjs.map +1 -1
  10. package/dist/cron.d.cts +2 -2
  11. package/dist/cron.d.ts +2 -2
  12. package/dist/cron.js +225 -3
  13. package/dist/cron.js.map +1 -1
  14. package/dist/define-tool.d.ts +8 -0
  15. package/dist/{errors-9yw4UQwX.d.cts → errors-Bart0ptP.d.cts} +1 -1
  16. package/dist/{errors-DFiY-NHK.d.ts → errors-DJuuubJK.d.ts} +1 -1
  17. package/dist/errors.d.cts +2 -2
  18. package/dist/eval.cjs +228 -6
  19. package/dist/eval.cjs.map +1 -1
  20. package/dist/eval.js +228 -6
  21. package/dist/eval.js.map +1 -1
  22. package/dist/index.cjs +230 -4
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +41 -6
  25. package/dist/index.d.ts +41 -6
  26. package/dist/index.js +230 -4
  27. package/dist/index.js.map +1 -1
  28. package/dist/internal/agent-loop/doom-loop-tracker.d.ts +22 -0
  29. package/dist/internal/agent-loop/loop-types.d.ts +6 -0
  30. package/dist/internal/llm/hermes-tool-extract.d.ts +1 -0
  31. package/dist/{run-TMdc7gmo.d.cts → run-DXy_MVwz.d.cts} +29 -1
  32. package/dist/{run-TMdc7gmo.d.ts → run-DXy_MVwz.d.ts} +29 -1
  33. package/dist/sanitize/coerce.d.cts +1 -0
  34. package/dist/sanitize/coerce.d.ts +1 -0
  35. package/dist/sanitize/index.cjs +119 -0
  36. package/dist/sanitize/index.cjs.map +1 -0
  37. package/dist/sanitize/index.d.cts +9 -0
  38. package/dist/sanitize/index.d.ts +9 -0
  39. package/dist/sanitize/index.js +116 -0
  40. package/dist/sanitize/index.js.map +1 -0
  41. package/dist/sanitize/sanitize-tool-input.d.cts +11 -0
  42. package/dist/sanitize/sanitize-tool-input.d.ts +11 -0
  43. package/dist/sanitize/types.d.cts +39 -0
  44. package/dist/sanitize/types.d.ts +39 -0
  45. package/dist/types/run.d.ts +28 -0
  46. package/package.json +13 -2
@@ -1,4 +1,4 @@
1
- import { C as CustomTool, M as ModelSelection, F as SDKUserMessage, H as SendOptions, b as Run, r as RunToCompletionOptions, s as RunToCompletionResult, S as SDKMessage, T as StreamToCompletionResult, a as McpServerConfig } from './run-TMdc7gmo.cjs';
1
+ import { C as CustomTool, M as ModelSelection, G as SDKUserMessage, J as SendOptions, b as Run, r as RunToCompletionOptions, s as RunToCompletionResult, S as SDKMessage, U as StreamToCompletionResult, a as McpServerConfig } from './run-DXy_MVwz.cjs';
2
2
 
3
3
  /**
4
4
  * Fork primitive public type contracts (T1.2, ADRs D110-D114).
@@ -1,4 +1,4 @@
1
- import { C as CustomTool, M as ModelSelection, F as SDKUserMessage, H as SendOptions, b as Run, r as RunToCompletionOptions, s as RunToCompletionResult, S as SDKMessage, T as StreamToCompletionResult, a as McpServerConfig } from './run-TMdc7gmo.js';
1
+ import { C as CustomTool, M as ModelSelection, G as SDKUserMessage, J as SendOptions, b as Run, r as RunToCompletionOptions, s as RunToCompletionResult, S as SDKMessage, U as StreamToCompletionResult, a as McpServerConfig } from './run-DXy_MVwz.js';
2
2
 
3
3
  /**
4
4
  * Fork primitive public type contracts (T1.2, ADRs D110-D114).
package/dist/cron.cjs CHANGED
@@ -1458,6 +1458,7 @@ function isEmptyRound(result) {
1458
1458
  return (result.result ?? "").trim() === "";
1459
1459
  }
1460
1460
  function classifyRound(result, round, maxRounds, emptyStreak) {
1461
+ if (result.stoppedByDoomLoop === true) return "no_progress";
1461
1462
  if (result.stoppedAtIterationLimit !== true) return "done";
1462
1463
  if (isEmptyRound(result) && emptyStreak >= 1) return "no_progress";
1463
1464
  if (round >= maxRounds) return "step_limit";
@@ -4495,6 +4496,7 @@ function applyScriptMetrics(base, script) {
4495
4496
  if (script.usage !== void 0) base.usage = script.usage;
4496
4497
  if (script.cost !== void 0) base.cost = script.cost;
4497
4498
  if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
4499
+ if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
4498
4500
  }
4499
4501
 
4500
4502
  // src/internal/runtime/cloud/cloud-run.ts
@@ -8276,6 +8278,93 @@ function evaluateBudgetGate(tracker) {
8276
8278
  }
8277
8279
  }
8278
8280
 
8281
+ // src/internal/agent-loop/doom-loop-tracker.ts
8282
+ init_errors();
8283
+ function createDoomLoopTracker(option) {
8284
+ if (option === false) return void 0;
8285
+ return new DoomLoopTracker(option);
8286
+ }
8287
+ var DEFAULT_CONFIG = { softThreshold: 3, hardThreshold: 5 };
8288
+ function assertValidThresholds(soft, hard) {
8289
+ for (const [label, value] of [
8290
+ ["softThreshold", soft],
8291
+ ["hardThreshold", hard]
8292
+ ]) {
8293
+ if (!Number.isInteger(value) || value < 1) {
8294
+ throw new ConfigurationError(
8295
+ `doomLoop.${label} must be a positive integer (received ${value}).`,
8296
+ { code: "invalid_doom_loop_threshold" }
8297
+ );
8298
+ }
8299
+ }
8300
+ }
8301
+ function sortKeys(value) {
8302
+ if (value === null || typeof value !== "object") return value;
8303
+ if (Array.isArray(value)) return value.map(sortKeys);
8304
+ const out = {};
8305
+ for (const key of Object.keys(value).sort()) {
8306
+ out[key] = sortKeys(value[key]);
8307
+ }
8308
+ return out;
8309
+ }
8310
+ function signatureOf(call) {
8311
+ const { input } = call;
8312
+ let inputSig;
8313
+ if (input === null || input === void 0) inputSig = "null";
8314
+ else if (typeof input !== "object") inputSig = String(input);
8315
+ else {
8316
+ try {
8317
+ inputSig = JSON.stringify(sortKeys(input)) ?? "null";
8318
+ } catch {
8319
+ inputSig = String(input);
8320
+ }
8321
+ }
8322
+ return `${call.name}\0${inputSig}`;
8323
+ }
8324
+ var DoomLoopTracker = class {
8325
+ #config;
8326
+ #lastSignature = "";
8327
+ #count = 0;
8328
+ constructor(config) {
8329
+ const softThreshold = config?.softThreshold ?? DEFAULT_CONFIG.softThreshold;
8330
+ const hardThreshold = config?.hardThreshold ?? DEFAULT_CONFIG.hardThreshold;
8331
+ assertValidThresholds(softThreshold, hardThreshold);
8332
+ this.#config = { softThreshold, hardThreshold };
8333
+ }
8334
+ inspect(call) {
8335
+ const signature = signatureOf(call);
8336
+ this.#count = signature === this.#lastSignature ? this.#count + 1 : 1;
8337
+ this.#lastSignature = signature;
8338
+ const count = this.#count;
8339
+ if (count >= this.#config.hardThreshold) {
8340
+ return {
8341
+ kind: "hard",
8342
+ message: `Detected ${count} consecutive identical calls to \`${call.name}\`; stopping to avoid a loop.`
8343
+ };
8344
+ }
8345
+ if (count === this.#config.softThreshold) {
8346
+ return {
8347
+ kind: "soft",
8348
+ message: `Detected ${count} consecutive identical calls to \`${call.name}\`; try a different approach.`
8349
+ };
8350
+ }
8351
+ return { kind: "ok" };
8352
+ }
8353
+ reset() {
8354
+ this.#lastSignature = "";
8355
+ this.#count = 0;
8356
+ }
8357
+ };
8358
+ function firstDoomLoopVerdict(tracker, calls) {
8359
+ let escalation = { kind: "ok" };
8360
+ for (const call of calls) {
8361
+ const v = tracker.inspect(call);
8362
+ if (v.kind === "hard") return v;
8363
+ if (v.kind === "soft" && escalation.kind === "ok") escalation = v;
8364
+ }
8365
+ return escalation;
8366
+ }
8367
+
8279
8368
  // src/internal/budget/usage-accumulator.ts
8280
8369
  var UsageAccumulator = class {
8281
8370
  input = 0;
@@ -8456,6 +8545,7 @@ async function initLoopContext(inputs) {
8456
8545
  tools,
8457
8546
  finalText: "",
8458
8547
  finalStatus: "finished",
8548
+ doomLoop: createDoomLoopTracker(inputs.doomLoop),
8459
8549
  usage: new UsageAccumulator(),
8460
8550
  nudgeAttempts: 0,
8461
8551
  stopFeedbackAttempts: 0,
@@ -9480,6 +9570,7 @@ async function runAgentLoop(inputs) {
9480
9570
  ctx.finalStatus = "error";
9481
9571
  }
9482
9572
  sendSpan?.setAttribute("status", ctx.finalStatus);
9573
+ if (ctx.stoppedByDoomLoop === true) sendSpan?.setAttribute("stoppedByDoomLoop", true);
9483
9574
  if (inputs.telemetry?.includeContent === true && ctx.finalText.length > 0) {
9484
9575
  sendSpan?.addEvent("response", { content: ctx.finalText });
9485
9576
  }
@@ -9506,7 +9597,8 @@ async function runAgentLoop(inputs) {
9506
9597
  ...usage !== void 0 ? { usage } : {},
9507
9598
  ...cost !== void 0 ? { cost } : {},
9508
9599
  ...ctx.error !== void 0 ? { error: ctx.error } : {},
9509
- ...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {}
9600
+ ...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {},
9601
+ ...ctx.stoppedByDoomLoop === true ? { stoppedByDoomLoop: true } : {}
9510
9602
  };
9511
9603
  } finally {
9512
9604
  if (ctxRef !== void 0 && ctxRef.memoryProviderHandle !== void 0 && inputs.memoryProvider !== void 0) {
@@ -9665,8 +9757,26 @@ async function continueOrTerminate(inputs, ctx, llmOutput) {
9665
9757
  }
9666
9758
  }
9667
9759
  pushToolConversationSteps(ctx, llmOutput.toolCalls, toolResults);
9760
+ if (await inspectDoomLoop(inputs, ctx, llmOutput.toolCalls) === "stop") return "done";
9668
9761
  return handleToolErrorContinuation(inputs, ctx, toolResults);
9669
9762
  }
9763
+ async function inspectDoomLoop(inputs, ctx, toolCalls) {
9764
+ if (ctx.doomLoop === void 0) return "continue";
9765
+ const verdict = firstDoomLoopVerdict(ctx.doomLoop, toolCalls);
9766
+ if (verdict.kind === "hard") {
9767
+ ctx.stoppedByDoomLoop = true;
9768
+ await emitAssistantTextStep(
9769
+ inputs,
9770
+ ctx,
9771
+ verdict.message ?? "Stopped: repeated identical tool calls made no progress."
9772
+ );
9773
+ return "stop";
9774
+ }
9775
+ if (verdict.kind === "soft") {
9776
+ ctx.messages.push({ role: "user", content: [{ type: "text", text: verdict.message ?? "" }] });
9777
+ }
9778
+ return "continue";
9779
+ }
9670
9780
 
9671
9781
  // src/internal/llm/fallback-client.ts
9672
9782
  init_errors();
@@ -10955,6 +11065,115 @@ function toOllamaTools(tools) {
10955
11065
  }
10956
11066
  }));
10957
11067
  }
11068
+ var cachedJsonrepair;
11069
+ function loadJsonrepair() {
11070
+ if (cachedJsonrepair === void 0) {
11071
+ const req = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cron.cjs', document.baseURI).href)));
11072
+ cachedJsonrepair = req("jsonrepair").jsonrepair;
11073
+ }
11074
+ return cachedJsonrepair;
11075
+ }
11076
+ function isPlainObject(v) {
11077
+ return v !== null && typeof v === "object" && !Array.isArray(v);
11078
+ }
11079
+ function toFiniteNumber(raw) {
11080
+ if (raw === "") return void 0;
11081
+ const n = Number(raw);
11082
+ return Number.isFinite(n) && String(n) === raw ? n : void 0;
11083
+ }
11084
+ function tryJson(raw, repair) {
11085
+ const t = raw.trimStart();
11086
+ if (!(t.startsWith("{") || t.startsWith("["))) return void 0;
11087
+ try {
11088
+ return JSON.parse(repair ? loadJsonrepair()(t) : t);
11089
+ } catch {
11090
+ return void 0;
11091
+ }
11092
+ }
11093
+ function heuristicCoerce(raw, repairJson) {
11094
+ if (raw === "true") return true;
11095
+ if (raw === "false") return false;
11096
+ if (raw === "null") return null;
11097
+ const n = toFiniteNumber(raw);
11098
+ if (n !== void 0) return n;
11099
+ const json = tryJson(raw, false) ?? (repairJson ? tryJson(raw, true) : void 0);
11100
+ return json === void 0 ? raw : json;
11101
+ }
11102
+ function coerceCandidates(raw, repairJson) {
11103
+ const out = [];
11104
+ if (raw === "true") out.push(true);
11105
+ else if (raw === "false") out.push(false);
11106
+ else if (raw === "null") out.push(null);
11107
+ const n = toFiniteNumber(raw);
11108
+ if (n !== void 0) out.push(n);
11109
+ const json = tryJson(raw, false) ?? (repairJson ? tryJson(raw, true) : void 0);
11110
+ if (json !== void 0) out.push(json);
11111
+ out.push(raw);
11112
+ return out;
11113
+ }
11114
+ function objectShape(schema) {
11115
+ const shape = schema?.shape;
11116
+ return shape !== null && typeof shape === "object" ? shape : void 0;
11117
+ }
11118
+
11119
+ // src/sanitize/sanitize-tool-input.ts
11120
+ function applyTrim(key, value, ctx) {
11121
+ const trimmed = value.trim();
11122
+ if (trimmed !== value) ctx.notes.push(`trimmed "${key}"`);
11123
+ return trimmed;
11124
+ }
11125
+ function applyCoerce(key, raw, ctx) {
11126
+ const field = ctx.shape?.[key];
11127
+ let coerced = raw;
11128
+ if (field) {
11129
+ for (const candidate of coerceCandidates(raw, ctx.repairJson)) {
11130
+ if (field.safeParse(candidate).success) {
11131
+ coerced = candidate;
11132
+ break;
11133
+ }
11134
+ }
11135
+ } else {
11136
+ coerced = heuristicCoerce(raw, ctx.repairJson);
11137
+ }
11138
+ if (coerced !== raw) ctx.notes.push(`coerced "${key}"`);
11139
+ return coerced;
11140
+ }
11141
+ function applyRepair(key, value, ctx) {
11142
+ const repaired = tryJson(value, true);
11143
+ if (repaired === void 0) return value;
11144
+ ctx.notes.push(`repaired json "${key}"`);
11145
+ return repaired;
11146
+ }
11147
+ function sanitizeString(key, value, ctx) {
11148
+ let out = ctx.trim ? applyTrim(key, value, ctx) : value;
11149
+ if (ctx.coerce && typeof out === "string") out = applyCoerce(key, out, ctx);
11150
+ if (ctx.repairJson && !ctx.coerce && typeof out === "string") out = applyRepair(key, out, ctx);
11151
+ return out;
11152
+ }
11153
+ function walk(input, ctx, depth) {
11154
+ const out = {};
11155
+ for (const [key, value] of Object.entries(input)) {
11156
+ if (typeof value === "string") out[key] = sanitizeString(key, value, ctx);
11157
+ else if (ctx.deep && depth < ctx.maxDepth && isPlainObject(value))
11158
+ out[key] = walk(value, ctx, depth + 1);
11159
+ else out[key] = value;
11160
+ }
11161
+ return out;
11162
+ }
11163
+ function sanitizeToolInput(input, options) {
11164
+ if (!isPlainObject(input)) return { value: input, changed: false, notes: [] };
11165
+ const ctx = {
11166
+ trim: options?.trim,
11167
+ coerce: options?.coerce ?? false,
11168
+ repairJson: options?.repairJson ?? false,
11169
+ deep: options?.deep ?? false,
11170
+ maxDepth: options?.maxDepth ?? 8,
11171
+ shape: objectShape(options?.schema),
11172
+ notes: []
11173
+ };
11174
+ const value = walk(input, ctx, 0);
11175
+ return { value, changed: ctx.notes.length > 0, notes: ctx.notes };
11176
+ }
10958
11177
 
10959
11178
  // src/internal/llm/hermes-tool-extract.ts
10960
11179
  var HERMES_BLOCK = /<function=\s*([^>\s]+)\s*>([\s\S]*?)<\/tool_call>/g;
@@ -10980,9 +11199,9 @@ function parseHermesParams(inner) {
10980
11199
  const key = param[1];
10981
11200
  const value = param[2];
10982
11201
  if (key === void 0 || value === void 0) continue;
10983
- input[key.trim()] = value.trim();
11202
+ input[key.trim()] = value;
10984
11203
  }
10985
- return input;
11204
+ return sanitizeToolInput(input, { trim: true }).value;
10986
11205
  }
10987
11206
 
10988
11207
  // src/internal/llm/openai.ts
@@ -12102,6 +12321,8 @@ function buildLoopInputs(options, runId, userText) {
12102
12321
  // M1-2: per-send iteration ceiling (validated above). The loop reads
12103
12322
  // inputs.maxIterations (default 8 when unset).
12104
12323
  ...maxIterations !== void 0 ? { maxIterations } : {},
12324
+ // Doom-loop guard config (default on; `false` disables, object tunes thresholds).
12325
+ ...options.sendOptions.doomLoop !== void 0 ? { doomLoop: options.sendOptions.doomLoop } : {},
12105
12326
  // D315-D317 — tool lifecycle hooks (cost tracking + audit + retry/alert)
12106
12327
  ...options.agentOptions.onToolStart !== void 0 ? { onToolStart: options.agentOptions.onToolStart } : {},
12107
12328
  ...options.agentOptions.onToolEnd !== void 0 ? { onToolEnd: options.agentOptions.onToolEnd } : {},
@@ -12234,6 +12455,7 @@ var RealLocalRun = class extends FixtureRunBase {
12234
12455
  if (output.usage !== void 0) this.script.usage = output.usage;
12235
12456
  if (output.cost !== void 0) this.script.cost = output.cost;
12236
12457
  if (output.stoppedAtIterationLimit === true) this.script.stoppedAtIterationLimit = true;
12458
+ if (output.stoppedByDoomLoop === true) this.script.stoppedByDoomLoop = true;
12237
12459
  if (output.error !== void 0 && this.script.errorDetail === void 0) {
12238
12460
  this.script.errorDetail = {
12239
12461
  message: output.error.message,