@theokit/sdk 2.14.0 → 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.
@@ -1,4 +1,4 @@
1
- import { p as RunOperation } from './run-TMdc7gmo.cjs';
1
+ import { p as RunOperation } from './run-DXy_MVwz.cjs';
2
2
 
3
3
  /**
4
4
  * Public type contract for the Budget enforcement primitive
@@ -1,4 +1,4 @@
1
- import { p as RunOperation } from './run-TMdc7gmo.js';
1
+ import { p as RunOperation } from './run-DXy_MVwz.js';
2
2
 
3
3
  /**
4
4
  * Public type contract for the Budget enforcement primitive
package/dist/errors.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- export { A as AgentDisposedError, c as AgentRunError, d as AgentRunErrorCode, e as AuthenticationError, g as BudgetExceededError, C as ConfigurationError, u as CredentialPoolExhaustedError, E as ErrorCode, m as ErrorMetadata, I as IntegrationNotConnectedError, n as InvalidTaskIdError, K as KnownAgentRunErrorCode, M as MemoryAdapterError, o as MemoryAdapterErrorCode, N as NetworkError, R as RateLimitError, p as TaskNotFoundError, T as TheokitAgentError, U as UnknownAgentError, q as UnsupportedBudgetOperationError, r as UnsupportedRunOperationError, s as UnsupportedTaskOperationError, t as isTransientError } from './errors-9yw4UQwX.cjs';
2
- import './run-TMdc7gmo.cjs';
1
+ export { A as AgentDisposedError, c as AgentRunError, d as AgentRunErrorCode, e as AuthenticationError, g as BudgetExceededError, C as ConfigurationError, u as CredentialPoolExhaustedError, E as ErrorCode, m as ErrorMetadata, I as IntegrationNotConnectedError, n as InvalidTaskIdError, K as KnownAgentRunErrorCode, M as MemoryAdapterError, o as MemoryAdapterErrorCode, N as NetworkError, R as RateLimitError, p as TaskNotFoundError, T as TheokitAgentError, U as UnknownAgentError, q as UnsupportedBudgetOperationError, r as UnsupportedRunOperationError, s as UnsupportedTaskOperationError, t as isTransientError } from './errors-Bart0ptP.cjs';
2
+ import './run-DXy_MVwz.cjs';
package/dist/eval.cjs CHANGED
@@ -1457,6 +1457,7 @@ function isEmptyRound(result) {
1457
1457
  return (result.result ?? "").trim() === "";
1458
1458
  }
1459
1459
  function classifyRound(result, round, maxRounds, emptyStreak) {
1460
+ if (result.stoppedByDoomLoop === true) return "no_progress";
1460
1461
  if (result.stoppedAtIterationLimit !== true) return "done";
1461
1462
  if (isEmptyRound(result) && emptyStreak >= 1) return "no_progress";
1462
1463
  if (round >= maxRounds) return "step_limit";
@@ -4490,6 +4491,7 @@ function applyScriptMetrics(base, script) {
4490
4491
  if (script.usage !== void 0) base.usage = script.usage;
4491
4492
  if (script.cost !== void 0) base.cost = script.cost;
4492
4493
  if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
4494
+ if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
4493
4495
  }
4494
4496
 
4495
4497
  // src/internal/runtime/cloud/cloud-run.ts
@@ -8271,6 +8273,93 @@ function evaluateBudgetGate(tracker) {
8271
8273
  }
8272
8274
  }
8273
8275
 
8276
+ // src/internal/agent-loop/doom-loop-tracker.ts
8277
+ init_errors();
8278
+ function createDoomLoopTracker(option) {
8279
+ if (option === false) return void 0;
8280
+ return new DoomLoopTracker(option);
8281
+ }
8282
+ var DEFAULT_CONFIG = { softThreshold: 3, hardThreshold: 5 };
8283
+ function assertValidThresholds(soft, hard) {
8284
+ for (const [label, value] of [
8285
+ ["softThreshold", soft],
8286
+ ["hardThreshold", hard]
8287
+ ]) {
8288
+ if (!Number.isInteger(value) || value < 1) {
8289
+ throw new ConfigurationError(
8290
+ `doomLoop.${label} must be a positive integer (received ${value}).`,
8291
+ { code: "invalid_doom_loop_threshold" }
8292
+ );
8293
+ }
8294
+ }
8295
+ }
8296
+ function sortKeys(value) {
8297
+ if (value === null || typeof value !== "object") return value;
8298
+ if (Array.isArray(value)) return value.map(sortKeys);
8299
+ const out = {};
8300
+ for (const key of Object.keys(value).sort()) {
8301
+ out[key] = sortKeys(value[key]);
8302
+ }
8303
+ return out;
8304
+ }
8305
+ function signatureOf(call) {
8306
+ const { input } = call;
8307
+ let inputSig;
8308
+ if (input === null || input === void 0) inputSig = "null";
8309
+ else if (typeof input !== "object") inputSig = String(input);
8310
+ else {
8311
+ try {
8312
+ inputSig = JSON.stringify(sortKeys(input)) ?? "null";
8313
+ } catch {
8314
+ inputSig = String(input);
8315
+ }
8316
+ }
8317
+ return `${call.name}\0${inputSig}`;
8318
+ }
8319
+ var DoomLoopTracker = class {
8320
+ #config;
8321
+ #lastSignature = "";
8322
+ #count = 0;
8323
+ constructor(config) {
8324
+ const softThreshold = config?.softThreshold ?? DEFAULT_CONFIG.softThreshold;
8325
+ const hardThreshold = config?.hardThreshold ?? DEFAULT_CONFIG.hardThreshold;
8326
+ assertValidThresholds(softThreshold, hardThreshold);
8327
+ this.#config = { softThreshold, hardThreshold };
8328
+ }
8329
+ inspect(call) {
8330
+ const signature = signatureOf(call);
8331
+ this.#count = signature === this.#lastSignature ? this.#count + 1 : 1;
8332
+ this.#lastSignature = signature;
8333
+ const count = this.#count;
8334
+ if (count >= this.#config.hardThreshold) {
8335
+ return {
8336
+ kind: "hard",
8337
+ message: `Detected ${count} consecutive identical calls to \`${call.name}\`; stopping to avoid a loop.`
8338
+ };
8339
+ }
8340
+ if (count === this.#config.softThreshold) {
8341
+ return {
8342
+ kind: "soft",
8343
+ message: `Detected ${count} consecutive identical calls to \`${call.name}\`; try a different approach.`
8344
+ };
8345
+ }
8346
+ return { kind: "ok" };
8347
+ }
8348
+ reset() {
8349
+ this.#lastSignature = "";
8350
+ this.#count = 0;
8351
+ }
8352
+ };
8353
+ function firstDoomLoopVerdict(tracker, calls) {
8354
+ let escalation = { kind: "ok" };
8355
+ for (const call of calls) {
8356
+ const v = tracker.inspect(call);
8357
+ if (v.kind === "hard") return v;
8358
+ if (v.kind === "soft" && escalation.kind === "ok") escalation = v;
8359
+ }
8360
+ return escalation;
8361
+ }
8362
+
8274
8363
  // src/internal/budget/usage-accumulator.ts
8275
8364
  var UsageAccumulator = class {
8276
8365
  input = 0;
@@ -8451,6 +8540,7 @@ async function initLoopContext(inputs) {
8451
8540
  tools,
8452
8541
  finalText: "",
8453
8542
  finalStatus: "finished",
8543
+ doomLoop: createDoomLoopTracker(inputs.doomLoop),
8454
8544
  usage: new UsageAccumulator(),
8455
8545
  nudgeAttempts: 0,
8456
8546
  stopFeedbackAttempts: 0,
@@ -9475,6 +9565,7 @@ async function runAgentLoop(inputs) {
9475
9565
  ctx.finalStatus = "error";
9476
9566
  }
9477
9567
  sendSpan?.setAttribute("status", ctx.finalStatus);
9568
+ if (ctx.stoppedByDoomLoop === true) sendSpan?.setAttribute("stoppedByDoomLoop", true);
9478
9569
  if (inputs.telemetry?.includeContent === true && ctx.finalText.length > 0) {
9479
9570
  sendSpan?.addEvent("response", { content: ctx.finalText });
9480
9571
  }
@@ -9501,7 +9592,8 @@ async function runAgentLoop(inputs) {
9501
9592
  ...usage !== void 0 ? { usage } : {},
9502
9593
  ...cost !== void 0 ? { cost } : {},
9503
9594
  ...ctx.error !== void 0 ? { error: ctx.error } : {},
9504
- ...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {}
9595
+ ...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {},
9596
+ ...ctx.stoppedByDoomLoop === true ? { stoppedByDoomLoop: true } : {}
9505
9597
  };
9506
9598
  } finally {
9507
9599
  if (ctxRef !== void 0 && ctxRef.memoryProviderHandle !== void 0 && inputs.memoryProvider !== void 0) {
@@ -9660,8 +9752,26 @@ async function continueOrTerminate(inputs, ctx, llmOutput) {
9660
9752
  }
9661
9753
  }
9662
9754
  pushToolConversationSteps(ctx, llmOutput.toolCalls, toolResults);
9755
+ if (await inspectDoomLoop(inputs, ctx, llmOutput.toolCalls) === "stop") return "done";
9663
9756
  return handleToolErrorContinuation(inputs, ctx, toolResults);
9664
9757
  }
9758
+ async function inspectDoomLoop(inputs, ctx, toolCalls) {
9759
+ if (ctx.doomLoop === void 0) return "continue";
9760
+ const verdict = firstDoomLoopVerdict(ctx.doomLoop, toolCalls);
9761
+ if (verdict.kind === "hard") {
9762
+ ctx.stoppedByDoomLoop = true;
9763
+ await emitAssistantTextStep(
9764
+ inputs,
9765
+ ctx,
9766
+ verdict.message ?? "Stopped: repeated identical tool calls made no progress."
9767
+ );
9768
+ return "stop";
9769
+ }
9770
+ if (verdict.kind === "soft") {
9771
+ ctx.messages.push({ role: "user", content: [{ type: "text", text: verdict.message ?? "" }] });
9772
+ }
9773
+ return "continue";
9774
+ }
9665
9775
 
9666
9776
  // src/internal/llm/fallback-client.ts
9667
9777
  init_errors();
@@ -12206,6 +12316,8 @@ function buildLoopInputs(options, runId, userText) {
12206
12316
  // M1-2: per-send iteration ceiling (validated above). The loop reads
12207
12317
  // inputs.maxIterations (default 8 when unset).
12208
12318
  ...maxIterations !== void 0 ? { maxIterations } : {},
12319
+ // Doom-loop guard config (default on; `false` disables, object tunes thresholds).
12320
+ ...options.sendOptions.doomLoop !== void 0 ? { doomLoop: options.sendOptions.doomLoop } : {},
12209
12321
  // D315-D317 — tool lifecycle hooks (cost tracking + audit + retry/alert)
12210
12322
  ...options.agentOptions.onToolStart !== void 0 ? { onToolStart: options.agentOptions.onToolStart } : {},
12211
12323
  ...options.agentOptions.onToolEnd !== void 0 ? { onToolEnd: options.agentOptions.onToolEnd } : {},
@@ -12338,6 +12450,7 @@ var RealLocalRun = class extends FixtureRunBase {
12338
12450
  if (output.usage !== void 0) this.script.usage = output.usage;
12339
12451
  if (output.cost !== void 0) this.script.cost = output.cost;
12340
12452
  if (output.stoppedAtIterationLimit === true) this.script.stoppedAtIterationLimit = true;
12453
+ if (output.stoppedByDoomLoop === true) this.script.stoppedByDoomLoop = true;
12341
12454
  if (output.error !== void 0 && this.script.errorDetail === void 0) {
12342
12455
  this.script.errorDetail = {
12343
12456
  message: output.error.message,