@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.
- package/CHANGELOG.md +12 -0
- package/dist/a2a/index.cjs +242 -3
- package/dist/a2a/index.cjs.map +1 -1
- package/dist/a2a/index.js +242 -3
- package/dist/a2a/index.js.map +1 -1
- package/dist/{cron-CpxLdAXc.d.cts → cron-BxLSz1UH.d.cts} +1 -1
- package/dist/{cron-CL_9nfhQ.d.ts → cron-DcaoP7aW.d.ts} +1 -1
- package/dist/cron.cjs +225 -3
- package/dist/cron.cjs.map +1 -1
- package/dist/cron.d.cts +2 -2
- package/dist/cron.d.ts +2 -2
- package/dist/cron.js +225 -3
- package/dist/cron.js.map +1 -1
- package/dist/define-tool.d.ts +8 -0
- package/dist/{errors-9yw4UQwX.d.cts → errors-Bart0ptP.d.cts} +1 -1
- package/dist/{errors-DFiY-NHK.d.ts → errors-DJuuubJK.d.ts} +1 -1
- package/dist/errors.d.cts +2 -2
- package/dist/eval.cjs +228 -6
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +228 -6
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +230 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -6
- package/dist/index.d.ts +41 -6
- package/dist/index.js +230 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/agent-loop/doom-loop-tracker.d.ts +22 -0
- package/dist/internal/agent-loop/loop-types.d.ts +6 -0
- package/dist/internal/llm/hermes-tool-extract.d.ts +1 -0
- package/dist/{run-TMdc7gmo.d.cts → run-DXy_MVwz.d.cts} +29 -1
- package/dist/{run-TMdc7gmo.d.ts → run-DXy_MVwz.d.ts} +29 -1
- package/dist/sanitize/coerce.d.cts +1 -0
- package/dist/sanitize/coerce.d.ts +1 -0
- package/dist/sanitize/index.cjs +119 -0
- package/dist/sanitize/index.cjs.map +1 -0
- package/dist/sanitize/index.d.cts +9 -0
- package/dist/sanitize/index.d.ts +9 -0
- package/dist/sanitize/index.js +116 -0
- package/dist/sanitize/index.js.map +1 -0
- package/dist/sanitize/sanitize-tool-input.d.cts +11 -0
- package/dist/sanitize/sanitize-tool-input.d.ts +11 -0
- package/dist/sanitize/types.d.cts +39 -0
- package/dist/sanitize/types.d.ts +39 -0
- package/dist/types/run.d.ts +28 -0
- package/package.json +13 -2
package/dist/eval.js
CHANGED
|
@@ -1454,6 +1454,7 @@ function isEmptyRound(result) {
|
|
|
1454
1454
|
return (result.result ?? "").trim() === "";
|
|
1455
1455
|
}
|
|
1456
1456
|
function classifyRound(result, round, maxRounds, emptyStreak) {
|
|
1457
|
+
if (result.stoppedByDoomLoop === true) return "no_progress";
|
|
1457
1458
|
if (result.stoppedAtIterationLimit !== true) return "done";
|
|
1458
1459
|
if (isEmptyRound(result) && emptyStreak >= 1) return "no_progress";
|
|
1459
1460
|
if (round >= maxRounds) return "step_limit";
|
|
@@ -4487,6 +4488,7 @@ function applyScriptMetrics(base, script) {
|
|
|
4487
4488
|
if (script.usage !== void 0) base.usage = script.usage;
|
|
4488
4489
|
if (script.cost !== void 0) base.cost = script.cost;
|
|
4489
4490
|
if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
|
|
4491
|
+
if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
|
|
4490
4492
|
}
|
|
4491
4493
|
|
|
4492
4494
|
// src/internal/runtime/cloud/cloud-run.ts
|
|
@@ -8268,6 +8270,93 @@ function evaluateBudgetGate(tracker) {
|
|
|
8268
8270
|
}
|
|
8269
8271
|
}
|
|
8270
8272
|
|
|
8273
|
+
// src/internal/agent-loop/doom-loop-tracker.ts
|
|
8274
|
+
init_errors();
|
|
8275
|
+
function createDoomLoopTracker(option) {
|
|
8276
|
+
if (option === false) return void 0;
|
|
8277
|
+
return new DoomLoopTracker(option);
|
|
8278
|
+
}
|
|
8279
|
+
var DEFAULT_CONFIG = { softThreshold: 3, hardThreshold: 5 };
|
|
8280
|
+
function assertValidThresholds(soft, hard) {
|
|
8281
|
+
for (const [label, value] of [
|
|
8282
|
+
["softThreshold", soft],
|
|
8283
|
+
["hardThreshold", hard]
|
|
8284
|
+
]) {
|
|
8285
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
8286
|
+
throw new ConfigurationError(
|
|
8287
|
+
`doomLoop.${label} must be a positive integer (received ${value}).`,
|
|
8288
|
+
{ code: "invalid_doom_loop_threshold" }
|
|
8289
|
+
);
|
|
8290
|
+
}
|
|
8291
|
+
}
|
|
8292
|
+
}
|
|
8293
|
+
function sortKeys(value) {
|
|
8294
|
+
if (value === null || typeof value !== "object") return value;
|
|
8295
|
+
if (Array.isArray(value)) return value.map(sortKeys);
|
|
8296
|
+
const out = {};
|
|
8297
|
+
for (const key of Object.keys(value).sort()) {
|
|
8298
|
+
out[key] = sortKeys(value[key]);
|
|
8299
|
+
}
|
|
8300
|
+
return out;
|
|
8301
|
+
}
|
|
8302
|
+
function signatureOf(call) {
|
|
8303
|
+
const { input } = call;
|
|
8304
|
+
let inputSig;
|
|
8305
|
+
if (input === null || input === void 0) inputSig = "null";
|
|
8306
|
+
else if (typeof input !== "object") inputSig = String(input);
|
|
8307
|
+
else {
|
|
8308
|
+
try {
|
|
8309
|
+
inputSig = JSON.stringify(sortKeys(input)) ?? "null";
|
|
8310
|
+
} catch {
|
|
8311
|
+
inputSig = String(input);
|
|
8312
|
+
}
|
|
8313
|
+
}
|
|
8314
|
+
return `${call.name}\0${inputSig}`;
|
|
8315
|
+
}
|
|
8316
|
+
var DoomLoopTracker = class {
|
|
8317
|
+
#config;
|
|
8318
|
+
#lastSignature = "";
|
|
8319
|
+
#count = 0;
|
|
8320
|
+
constructor(config) {
|
|
8321
|
+
const softThreshold = config?.softThreshold ?? DEFAULT_CONFIG.softThreshold;
|
|
8322
|
+
const hardThreshold = config?.hardThreshold ?? DEFAULT_CONFIG.hardThreshold;
|
|
8323
|
+
assertValidThresholds(softThreshold, hardThreshold);
|
|
8324
|
+
this.#config = { softThreshold, hardThreshold };
|
|
8325
|
+
}
|
|
8326
|
+
inspect(call) {
|
|
8327
|
+
const signature = signatureOf(call);
|
|
8328
|
+
this.#count = signature === this.#lastSignature ? this.#count + 1 : 1;
|
|
8329
|
+
this.#lastSignature = signature;
|
|
8330
|
+
const count = this.#count;
|
|
8331
|
+
if (count >= this.#config.hardThreshold) {
|
|
8332
|
+
return {
|
|
8333
|
+
kind: "hard",
|
|
8334
|
+
message: `Detected ${count} consecutive identical calls to \`${call.name}\`; stopping to avoid a loop.`
|
|
8335
|
+
};
|
|
8336
|
+
}
|
|
8337
|
+
if (count === this.#config.softThreshold) {
|
|
8338
|
+
return {
|
|
8339
|
+
kind: "soft",
|
|
8340
|
+
message: `Detected ${count} consecutive identical calls to \`${call.name}\`; try a different approach.`
|
|
8341
|
+
};
|
|
8342
|
+
}
|
|
8343
|
+
return { kind: "ok" };
|
|
8344
|
+
}
|
|
8345
|
+
reset() {
|
|
8346
|
+
this.#lastSignature = "";
|
|
8347
|
+
this.#count = 0;
|
|
8348
|
+
}
|
|
8349
|
+
};
|
|
8350
|
+
function firstDoomLoopVerdict(tracker, calls) {
|
|
8351
|
+
let escalation = { kind: "ok" };
|
|
8352
|
+
for (const call of calls) {
|
|
8353
|
+
const v = tracker.inspect(call);
|
|
8354
|
+
if (v.kind === "hard") return v;
|
|
8355
|
+
if (v.kind === "soft" && escalation.kind === "ok") escalation = v;
|
|
8356
|
+
}
|
|
8357
|
+
return escalation;
|
|
8358
|
+
}
|
|
8359
|
+
|
|
8271
8360
|
// src/internal/budget/usage-accumulator.ts
|
|
8272
8361
|
var UsageAccumulator = class {
|
|
8273
8362
|
input = 0;
|
|
@@ -8448,6 +8537,7 @@ async function initLoopContext(inputs) {
|
|
|
8448
8537
|
tools,
|
|
8449
8538
|
finalText: "",
|
|
8450
8539
|
finalStatus: "finished",
|
|
8540
|
+
doomLoop: createDoomLoopTracker(inputs.doomLoop),
|
|
8451
8541
|
usage: new UsageAccumulator(),
|
|
8452
8542
|
nudgeAttempts: 0,
|
|
8453
8543
|
stopFeedbackAttempts: 0,
|
|
@@ -9472,6 +9562,7 @@ async function runAgentLoop(inputs) {
|
|
|
9472
9562
|
ctx.finalStatus = "error";
|
|
9473
9563
|
}
|
|
9474
9564
|
sendSpan?.setAttribute("status", ctx.finalStatus);
|
|
9565
|
+
if (ctx.stoppedByDoomLoop === true) sendSpan?.setAttribute("stoppedByDoomLoop", true);
|
|
9475
9566
|
if (inputs.telemetry?.includeContent === true && ctx.finalText.length > 0) {
|
|
9476
9567
|
sendSpan?.addEvent("response", { content: ctx.finalText });
|
|
9477
9568
|
}
|
|
@@ -9498,7 +9589,8 @@ async function runAgentLoop(inputs) {
|
|
|
9498
9589
|
...usage !== void 0 ? { usage } : {},
|
|
9499
9590
|
...cost !== void 0 ? { cost } : {},
|
|
9500
9591
|
...ctx.error !== void 0 ? { error: ctx.error } : {},
|
|
9501
|
-
...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {}
|
|
9592
|
+
...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {},
|
|
9593
|
+
...ctx.stoppedByDoomLoop === true ? { stoppedByDoomLoop: true } : {}
|
|
9502
9594
|
};
|
|
9503
9595
|
} finally {
|
|
9504
9596
|
if (ctxRef !== void 0 && ctxRef.memoryProviderHandle !== void 0 && inputs.memoryProvider !== void 0) {
|
|
@@ -9657,8 +9749,26 @@ async function continueOrTerminate(inputs, ctx, llmOutput) {
|
|
|
9657
9749
|
}
|
|
9658
9750
|
}
|
|
9659
9751
|
pushToolConversationSteps(ctx, llmOutput.toolCalls, toolResults);
|
|
9752
|
+
if (await inspectDoomLoop(inputs, ctx, llmOutput.toolCalls) === "stop") return "done";
|
|
9660
9753
|
return handleToolErrorContinuation(inputs, ctx, toolResults);
|
|
9661
9754
|
}
|
|
9755
|
+
async function inspectDoomLoop(inputs, ctx, toolCalls) {
|
|
9756
|
+
if (ctx.doomLoop === void 0) return "continue";
|
|
9757
|
+
const verdict = firstDoomLoopVerdict(ctx.doomLoop, toolCalls);
|
|
9758
|
+
if (verdict.kind === "hard") {
|
|
9759
|
+
ctx.stoppedByDoomLoop = true;
|
|
9760
|
+
await emitAssistantTextStep(
|
|
9761
|
+
inputs,
|
|
9762
|
+
ctx,
|
|
9763
|
+
verdict.message ?? "Stopped: repeated identical tool calls made no progress."
|
|
9764
|
+
);
|
|
9765
|
+
return "stop";
|
|
9766
|
+
}
|
|
9767
|
+
if (verdict.kind === "soft") {
|
|
9768
|
+
ctx.messages.push({ role: "user", content: [{ type: "text", text: verdict.message ?? "" }] });
|
|
9769
|
+
}
|
|
9770
|
+
return "continue";
|
|
9771
|
+
}
|
|
9662
9772
|
|
|
9663
9773
|
// src/internal/llm/fallback-client.ts
|
|
9664
9774
|
init_errors();
|
|
@@ -10947,6 +11057,115 @@ function toOllamaTools(tools) {
|
|
|
10947
11057
|
}
|
|
10948
11058
|
}));
|
|
10949
11059
|
}
|
|
11060
|
+
var cachedJsonrepair;
|
|
11061
|
+
function loadJsonrepair() {
|
|
11062
|
+
if (cachedJsonrepair === void 0) {
|
|
11063
|
+
const req = createRequire(import.meta.url);
|
|
11064
|
+
cachedJsonrepair = req("jsonrepair").jsonrepair;
|
|
11065
|
+
}
|
|
11066
|
+
return cachedJsonrepair;
|
|
11067
|
+
}
|
|
11068
|
+
function isPlainObject(v) {
|
|
11069
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
11070
|
+
}
|
|
11071
|
+
function toFiniteNumber(raw) {
|
|
11072
|
+
if (raw === "") return void 0;
|
|
11073
|
+
const n = Number(raw);
|
|
11074
|
+
return Number.isFinite(n) && String(n) === raw ? n : void 0;
|
|
11075
|
+
}
|
|
11076
|
+
function tryJson(raw, repair) {
|
|
11077
|
+
const t = raw.trimStart();
|
|
11078
|
+
if (!(t.startsWith("{") || t.startsWith("["))) return void 0;
|
|
11079
|
+
try {
|
|
11080
|
+
return JSON.parse(repair ? loadJsonrepair()(t) : t);
|
|
11081
|
+
} catch {
|
|
11082
|
+
return void 0;
|
|
11083
|
+
}
|
|
11084
|
+
}
|
|
11085
|
+
function heuristicCoerce(raw, repairJson) {
|
|
11086
|
+
if (raw === "true") return true;
|
|
11087
|
+
if (raw === "false") return false;
|
|
11088
|
+
if (raw === "null") return null;
|
|
11089
|
+
const n = toFiniteNumber(raw);
|
|
11090
|
+
if (n !== void 0) return n;
|
|
11091
|
+
const json = tryJson(raw, false) ?? (repairJson ? tryJson(raw, true) : void 0);
|
|
11092
|
+
return json === void 0 ? raw : json;
|
|
11093
|
+
}
|
|
11094
|
+
function coerceCandidates(raw, repairJson) {
|
|
11095
|
+
const out = [];
|
|
11096
|
+
if (raw === "true") out.push(true);
|
|
11097
|
+
else if (raw === "false") out.push(false);
|
|
11098
|
+
else if (raw === "null") out.push(null);
|
|
11099
|
+
const n = toFiniteNumber(raw);
|
|
11100
|
+
if (n !== void 0) out.push(n);
|
|
11101
|
+
const json = tryJson(raw, false) ?? (repairJson ? tryJson(raw, true) : void 0);
|
|
11102
|
+
if (json !== void 0) out.push(json);
|
|
11103
|
+
out.push(raw);
|
|
11104
|
+
return out;
|
|
11105
|
+
}
|
|
11106
|
+
function objectShape(schema) {
|
|
11107
|
+
const shape = schema?.shape;
|
|
11108
|
+
return shape !== null && typeof shape === "object" ? shape : void 0;
|
|
11109
|
+
}
|
|
11110
|
+
|
|
11111
|
+
// src/sanitize/sanitize-tool-input.ts
|
|
11112
|
+
function applyTrim(key, value, ctx) {
|
|
11113
|
+
const trimmed = value.trim();
|
|
11114
|
+
if (trimmed !== value) ctx.notes.push(`trimmed "${key}"`);
|
|
11115
|
+
return trimmed;
|
|
11116
|
+
}
|
|
11117
|
+
function applyCoerce(key, raw, ctx) {
|
|
11118
|
+
const field = ctx.shape?.[key];
|
|
11119
|
+
let coerced = raw;
|
|
11120
|
+
if (field) {
|
|
11121
|
+
for (const candidate of coerceCandidates(raw, ctx.repairJson)) {
|
|
11122
|
+
if (field.safeParse(candidate).success) {
|
|
11123
|
+
coerced = candidate;
|
|
11124
|
+
break;
|
|
11125
|
+
}
|
|
11126
|
+
}
|
|
11127
|
+
} else {
|
|
11128
|
+
coerced = heuristicCoerce(raw, ctx.repairJson);
|
|
11129
|
+
}
|
|
11130
|
+
if (coerced !== raw) ctx.notes.push(`coerced "${key}"`);
|
|
11131
|
+
return coerced;
|
|
11132
|
+
}
|
|
11133
|
+
function applyRepair(key, value, ctx) {
|
|
11134
|
+
const repaired = tryJson(value, true);
|
|
11135
|
+
if (repaired === void 0) return value;
|
|
11136
|
+
ctx.notes.push(`repaired json "${key}"`);
|
|
11137
|
+
return repaired;
|
|
11138
|
+
}
|
|
11139
|
+
function sanitizeString(key, value, ctx) {
|
|
11140
|
+
let out = ctx.trim ? applyTrim(key, value, ctx) : value;
|
|
11141
|
+
if (ctx.coerce && typeof out === "string") out = applyCoerce(key, out, ctx);
|
|
11142
|
+
if (ctx.repairJson && !ctx.coerce && typeof out === "string") out = applyRepair(key, out, ctx);
|
|
11143
|
+
return out;
|
|
11144
|
+
}
|
|
11145
|
+
function walk(input, ctx, depth) {
|
|
11146
|
+
const out = {};
|
|
11147
|
+
for (const [key, value] of Object.entries(input)) {
|
|
11148
|
+
if (typeof value === "string") out[key] = sanitizeString(key, value, ctx);
|
|
11149
|
+
else if (ctx.deep && depth < ctx.maxDepth && isPlainObject(value))
|
|
11150
|
+
out[key] = walk(value, ctx, depth + 1);
|
|
11151
|
+
else out[key] = value;
|
|
11152
|
+
}
|
|
11153
|
+
return out;
|
|
11154
|
+
}
|
|
11155
|
+
function sanitizeToolInput(input, options) {
|
|
11156
|
+
if (!isPlainObject(input)) return { value: input, changed: false, notes: [] };
|
|
11157
|
+
const ctx = {
|
|
11158
|
+
trim: options?.trim,
|
|
11159
|
+
coerce: options?.coerce ?? false,
|
|
11160
|
+
repairJson: options?.repairJson ?? false,
|
|
11161
|
+
deep: options?.deep ?? false,
|
|
11162
|
+
maxDepth: options?.maxDepth ?? 8,
|
|
11163
|
+
shape: objectShape(options?.schema),
|
|
11164
|
+
notes: []
|
|
11165
|
+
};
|
|
11166
|
+
const value = walk(input, ctx, 0);
|
|
11167
|
+
return { value, changed: ctx.notes.length > 0, notes: ctx.notes };
|
|
11168
|
+
}
|
|
10950
11169
|
|
|
10951
11170
|
// src/internal/llm/hermes-tool-extract.ts
|
|
10952
11171
|
var HERMES_BLOCK = /<function=\s*([^>\s]+)\s*>([\s\S]*?)<\/tool_call>/g;
|
|
@@ -10972,9 +11191,9 @@ function parseHermesParams(inner) {
|
|
|
10972
11191
|
const key = param[1];
|
|
10973
11192
|
const value = param[2];
|
|
10974
11193
|
if (key === void 0 || value === void 0) continue;
|
|
10975
|
-
input[key.trim()] = value
|
|
11194
|
+
input[key.trim()] = value;
|
|
10976
11195
|
}
|
|
10977
|
-
return input;
|
|
11196
|
+
return sanitizeToolInput(input, { trim: true }).value;
|
|
10978
11197
|
}
|
|
10979
11198
|
|
|
10980
11199
|
// src/internal/llm/openai.ts
|
|
@@ -12094,6 +12313,8 @@ function buildLoopInputs(options, runId, userText) {
|
|
|
12094
12313
|
// M1-2: per-send iteration ceiling (validated above). The loop reads
|
|
12095
12314
|
// inputs.maxIterations (default 8 when unset).
|
|
12096
12315
|
...maxIterations !== void 0 ? { maxIterations } : {},
|
|
12316
|
+
// Doom-loop guard config (default on; `false` disables, object tunes thresholds).
|
|
12317
|
+
...options.sendOptions.doomLoop !== void 0 ? { doomLoop: options.sendOptions.doomLoop } : {},
|
|
12097
12318
|
// D315-D317 — tool lifecycle hooks (cost tracking + audit + retry/alert)
|
|
12098
12319
|
...options.agentOptions.onToolStart !== void 0 ? { onToolStart: options.agentOptions.onToolStart } : {},
|
|
12099
12320
|
...options.agentOptions.onToolEnd !== void 0 ? { onToolEnd: options.agentOptions.onToolEnd } : {},
|
|
@@ -12226,6 +12447,7 @@ var RealLocalRun = class extends FixtureRunBase {
|
|
|
12226
12447
|
if (output.usage !== void 0) this.script.usage = output.usage;
|
|
12227
12448
|
if (output.cost !== void 0) this.script.cost = output.cost;
|
|
12228
12449
|
if (output.stoppedAtIterationLimit === true) this.script.stoppedAtIterationLimit = true;
|
|
12450
|
+
if (output.stoppedByDoomLoop === true) this.script.stoppedByDoomLoop = true;
|
|
12229
12451
|
if (output.error !== void 0 && this.script.errorDetail === void 0) {
|
|
12230
12452
|
this.script.errorDetail = {
|
|
12231
12453
|
message: output.error.message,
|
|
@@ -15784,7 +16006,7 @@ var JsonlParseError = class extends Error {
|
|
|
15784
16006
|
}
|
|
15785
16007
|
line;
|
|
15786
16008
|
};
|
|
15787
|
-
function
|
|
16009
|
+
function isPlainObject2(value) {
|
|
15788
16010
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15789
16011
|
}
|
|
15790
16012
|
function tryParseObjectLine(line) {
|
|
@@ -15795,7 +16017,7 @@ function tryParseObjectLine(line) {
|
|
|
15795
16017
|
} catch {
|
|
15796
16018
|
return void 0;
|
|
15797
16019
|
}
|
|
15798
|
-
return
|
|
16020
|
+
return isPlainObject2(parsed) ? parsed : void 0;
|
|
15799
16021
|
}
|
|
15800
16022
|
function loadJsonl(path, opts = {}) {
|
|
15801
16023
|
const text = readFileSync(path, "utf8");
|
|
@@ -15811,7 +16033,7 @@ function loadJsonl(path, opts = {}) {
|
|
|
15811
16033
|
} catch {
|
|
15812
16034
|
throw new JsonlParseError(`line ${lineNumber}: invalid JSON`, lineNumber);
|
|
15813
16035
|
}
|
|
15814
|
-
if (!
|
|
16036
|
+
if (!isPlainObject2(parsed)) {
|
|
15815
16037
|
throw new JsonlParseError(`line ${lineNumber}: not a JSON object`, lineNumber);
|
|
15816
16038
|
}
|
|
15817
16039
|
out.push(opts.map ? opts.map(parsed, lineNumber) : parsed);
|