@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/index.cjs
CHANGED
|
@@ -2817,6 +2817,7 @@ function isEmptyRound(result) {
|
|
|
2817
2817
|
return (result.result ?? "").trim() === "";
|
|
2818
2818
|
}
|
|
2819
2819
|
function classifyRound(result, round, maxRounds, emptyStreak) {
|
|
2820
|
+
if (result.stoppedByDoomLoop === true) return "no_progress";
|
|
2820
2821
|
if (result.stoppedAtIterationLimit !== true) return "done";
|
|
2821
2822
|
if (isEmptyRound(result) && emptyStreak >= 1) return "no_progress";
|
|
2822
2823
|
if (round >= maxRounds) return "step_limit";
|
|
@@ -6982,6 +6983,7 @@ function applyScriptMetrics(base, script) {
|
|
|
6982
6983
|
if (script.usage !== void 0) base.usage = script.usage;
|
|
6983
6984
|
if (script.cost !== void 0) base.cost = script.cost;
|
|
6984
6985
|
if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
|
|
6986
|
+
if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
|
|
6985
6987
|
}
|
|
6986
6988
|
|
|
6987
6989
|
// src/internal/runtime/cloud/cloud-run.ts
|
|
@@ -10733,6 +10735,93 @@ function evaluateBudgetGate(tracker) {
|
|
|
10733
10735
|
}
|
|
10734
10736
|
}
|
|
10735
10737
|
|
|
10738
|
+
// src/internal/agent-loop/doom-loop-tracker.ts
|
|
10739
|
+
init_errors();
|
|
10740
|
+
function createDoomLoopTracker(option) {
|
|
10741
|
+
if (option === false) return void 0;
|
|
10742
|
+
return new DoomLoopTracker(option);
|
|
10743
|
+
}
|
|
10744
|
+
var DEFAULT_CONFIG = { softThreshold: 3, hardThreshold: 5 };
|
|
10745
|
+
function assertValidThresholds(soft, hard) {
|
|
10746
|
+
for (const [label, value] of [
|
|
10747
|
+
["softThreshold", soft],
|
|
10748
|
+
["hardThreshold", hard]
|
|
10749
|
+
]) {
|
|
10750
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
10751
|
+
throw new exports.ConfigurationError(
|
|
10752
|
+
`doomLoop.${label} must be a positive integer (received ${value}).`,
|
|
10753
|
+
{ code: "invalid_doom_loop_threshold" }
|
|
10754
|
+
);
|
|
10755
|
+
}
|
|
10756
|
+
}
|
|
10757
|
+
}
|
|
10758
|
+
function sortKeys(value) {
|
|
10759
|
+
if (value === null || typeof value !== "object") return value;
|
|
10760
|
+
if (Array.isArray(value)) return value.map(sortKeys);
|
|
10761
|
+
const out = {};
|
|
10762
|
+
for (const key2 of Object.keys(value).sort()) {
|
|
10763
|
+
out[key2] = sortKeys(value[key2]);
|
|
10764
|
+
}
|
|
10765
|
+
return out;
|
|
10766
|
+
}
|
|
10767
|
+
function signatureOf(call) {
|
|
10768
|
+
const { input } = call;
|
|
10769
|
+
let inputSig;
|
|
10770
|
+
if (input === null || input === void 0) inputSig = "null";
|
|
10771
|
+
else if (typeof input !== "object") inputSig = String(input);
|
|
10772
|
+
else {
|
|
10773
|
+
try {
|
|
10774
|
+
inputSig = JSON.stringify(sortKeys(input)) ?? "null";
|
|
10775
|
+
} catch {
|
|
10776
|
+
inputSig = String(input);
|
|
10777
|
+
}
|
|
10778
|
+
}
|
|
10779
|
+
return `${call.name}\0${inputSig}`;
|
|
10780
|
+
}
|
|
10781
|
+
var DoomLoopTracker = class {
|
|
10782
|
+
#config;
|
|
10783
|
+
#lastSignature = "";
|
|
10784
|
+
#count = 0;
|
|
10785
|
+
constructor(config) {
|
|
10786
|
+
const softThreshold = config?.softThreshold ?? DEFAULT_CONFIG.softThreshold;
|
|
10787
|
+
const hardThreshold = config?.hardThreshold ?? DEFAULT_CONFIG.hardThreshold;
|
|
10788
|
+
assertValidThresholds(softThreshold, hardThreshold);
|
|
10789
|
+
this.#config = { softThreshold, hardThreshold };
|
|
10790
|
+
}
|
|
10791
|
+
inspect(call) {
|
|
10792
|
+
const signature = signatureOf(call);
|
|
10793
|
+
this.#count = signature === this.#lastSignature ? this.#count + 1 : 1;
|
|
10794
|
+
this.#lastSignature = signature;
|
|
10795
|
+
const count = this.#count;
|
|
10796
|
+
if (count >= this.#config.hardThreshold) {
|
|
10797
|
+
return {
|
|
10798
|
+
kind: "hard",
|
|
10799
|
+
message: `Detected ${count} consecutive identical calls to \`${call.name}\`; stopping to avoid a loop.`
|
|
10800
|
+
};
|
|
10801
|
+
}
|
|
10802
|
+
if (count === this.#config.softThreshold) {
|
|
10803
|
+
return {
|
|
10804
|
+
kind: "soft",
|
|
10805
|
+
message: `Detected ${count} consecutive identical calls to \`${call.name}\`; try a different approach.`
|
|
10806
|
+
};
|
|
10807
|
+
}
|
|
10808
|
+
return { kind: "ok" };
|
|
10809
|
+
}
|
|
10810
|
+
reset() {
|
|
10811
|
+
this.#lastSignature = "";
|
|
10812
|
+
this.#count = 0;
|
|
10813
|
+
}
|
|
10814
|
+
};
|
|
10815
|
+
function firstDoomLoopVerdict(tracker, calls) {
|
|
10816
|
+
let escalation = { kind: "ok" };
|
|
10817
|
+
for (const call of calls) {
|
|
10818
|
+
const v = tracker.inspect(call);
|
|
10819
|
+
if (v.kind === "hard") return v;
|
|
10820
|
+
if (v.kind === "soft" && escalation.kind === "ok") escalation = v;
|
|
10821
|
+
}
|
|
10822
|
+
return escalation;
|
|
10823
|
+
}
|
|
10824
|
+
|
|
10736
10825
|
// src/internal/budget/usage-accumulator.ts
|
|
10737
10826
|
var UsageAccumulator = class {
|
|
10738
10827
|
input = 0;
|
|
@@ -10913,6 +11002,7 @@ async function initLoopContext(inputs) {
|
|
|
10913
11002
|
tools,
|
|
10914
11003
|
finalText: "",
|
|
10915
11004
|
finalStatus: "finished",
|
|
11005
|
+
doomLoop: createDoomLoopTracker(inputs.doomLoop),
|
|
10916
11006
|
usage: new UsageAccumulator(),
|
|
10917
11007
|
nudgeAttempts: 0,
|
|
10918
11008
|
stopFeedbackAttempts: 0,
|
|
@@ -11937,6 +12027,7 @@ async function runAgentLoop(inputs) {
|
|
|
11937
12027
|
ctx.finalStatus = "error";
|
|
11938
12028
|
}
|
|
11939
12029
|
sendSpan?.setAttribute("status", ctx.finalStatus);
|
|
12030
|
+
if (ctx.stoppedByDoomLoop === true) sendSpan?.setAttribute("stoppedByDoomLoop", true);
|
|
11940
12031
|
if (inputs.telemetry?.includeContent === true && ctx.finalText.length > 0) {
|
|
11941
12032
|
sendSpan?.addEvent("response", { content: ctx.finalText });
|
|
11942
12033
|
}
|
|
@@ -11963,7 +12054,8 @@ async function runAgentLoop(inputs) {
|
|
|
11963
12054
|
...usage !== void 0 ? { usage } : {},
|
|
11964
12055
|
...cost !== void 0 ? { cost } : {},
|
|
11965
12056
|
...ctx.error !== void 0 ? { error: ctx.error } : {},
|
|
11966
|
-
...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {}
|
|
12057
|
+
...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {},
|
|
12058
|
+
...ctx.stoppedByDoomLoop === true ? { stoppedByDoomLoop: true } : {}
|
|
11967
12059
|
};
|
|
11968
12060
|
} finally {
|
|
11969
12061
|
if (ctxRef !== void 0 && ctxRef.memoryProviderHandle !== void 0 && inputs.memoryProvider !== void 0) {
|
|
@@ -12122,8 +12214,26 @@ async function continueOrTerminate(inputs, ctx, llmOutput) {
|
|
|
12122
12214
|
}
|
|
12123
12215
|
}
|
|
12124
12216
|
pushToolConversationSteps(ctx, llmOutput.toolCalls, toolResults);
|
|
12217
|
+
if (await inspectDoomLoop(inputs, ctx, llmOutput.toolCalls) === "stop") return "done";
|
|
12125
12218
|
return handleToolErrorContinuation(inputs, ctx, toolResults);
|
|
12126
12219
|
}
|
|
12220
|
+
async function inspectDoomLoop(inputs, ctx, toolCalls) {
|
|
12221
|
+
if (ctx.doomLoop === void 0) return "continue";
|
|
12222
|
+
const verdict = firstDoomLoopVerdict(ctx.doomLoop, toolCalls);
|
|
12223
|
+
if (verdict.kind === "hard") {
|
|
12224
|
+
ctx.stoppedByDoomLoop = true;
|
|
12225
|
+
await emitAssistantTextStep(
|
|
12226
|
+
inputs,
|
|
12227
|
+
ctx,
|
|
12228
|
+
verdict.message ?? "Stopped: repeated identical tool calls made no progress."
|
|
12229
|
+
);
|
|
12230
|
+
return "stop";
|
|
12231
|
+
}
|
|
12232
|
+
if (verdict.kind === "soft") {
|
|
12233
|
+
ctx.messages.push({ role: "user", content: [{ type: "text", text: verdict.message ?? "" }] });
|
|
12234
|
+
}
|
|
12235
|
+
return "continue";
|
|
12236
|
+
}
|
|
12127
12237
|
|
|
12128
12238
|
// src/internal/llm/fallback-client.ts
|
|
12129
12239
|
init_errors();
|
|
@@ -13427,6 +13537,115 @@ function toOllamaTools(tools) {
|
|
|
13427
13537
|
}
|
|
13428
13538
|
}));
|
|
13429
13539
|
}
|
|
13540
|
+
var cachedJsonrepair;
|
|
13541
|
+
function loadJsonrepair() {
|
|
13542
|
+
if (cachedJsonrepair === void 0) {
|
|
13543
|
+
const req = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
13544
|
+
cachedJsonrepair = req("jsonrepair").jsonrepair;
|
|
13545
|
+
}
|
|
13546
|
+
return cachedJsonrepair;
|
|
13547
|
+
}
|
|
13548
|
+
function isPlainObject(v) {
|
|
13549
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
13550
|
+
}
|
|
13551
|
+
function toFiniteNumber(raw) {
|
|
13552
|
+
if (raw === "") return void 0;
|
|
13553
|
+
const n = Number(raw);
|
|
13554
|
+
return Number.isFinite(n) && String(n) === raw ? n : void 0;
|
|
13555
|
+
}
|
|
13556
|
+
function tryJson(raw, repair) {
|
|
13557
|
+
const t = raw.trimStart();
|
|
13558
|
+
if (!(t.startsWith("{") || t.startsWith("["))) return void 0;
|
|
13559
|
+
try {
|
|
13560
|
+
return JSON.parse(repair ? loadJsonrepair()(t) : t);
|
|
13561
|
+
} catch {
|
|
13562
|
+
return void 0;
|
|
13563
|
+
}
|
|
13564
|
+
}
|
|
13565
|
+
function heuristicCoerce(raw, repairJson) {
|
|
13566
|
+
if (raw === "true") return true;
|
|
13567
|
+
if (raw === "false") return false;
|
|
13568
|
+
if (raw === "null") return null;
|
|
13569
|
+
const n = toFiniteNumber(raw);
|
|
13570
|
+
if (n !== void 0) return n;
|
|
13571
|
+
const json = tryJson(raw, false) ?? (repairJson ? tryJson(raw, true) : void 0);
|
|
13572
|
+
return json === void 0 ? raw : json;
|
|
13573
|
+
}
|
|
13574
|
+
function coerceCandidates(raw, repairJson) {
|
|
13575
|
+
const out = [];
|
|
13576
|
+
if (raw === "true") out.push(true);
|
|
13577
|
+
else if (raw === "false") out.push(false);
|
|
13578
|
+
else if (raw === "null") out.push(null);
|
|
13579
|
+
const n = toFiniteNumber(raw);
|
|
13580
|
+
if (n !== void 0) out.push(n);
|
|
13581
|
+
const json = tryJson(raw, false) ?? (repairJson ? tryJson(raw, true) : void 0);
|
|
13582
|
+
if (json !== void 0) out.push(json);
|
|
13583
|
+
out.push(raw);
|
|
13584
|
+
return out;
|
|
13585
|
+
}
|
|
13586
|
+
function objectShape(schema) {
|
|
13587
|
+
const shape = schema?.shape;
|
|
13588
|
+
return shape !== null && typeof shape === "object" ? shape : void 0;
|
|
13589
|
+
}
|
|
13590
|
+
|
|
13591
|
+
// src/sanitize/sanitize-tool-input.ts
|
|
13592
|
+
function applyTrim(key2, value, ctx) {
|
|
13593
|
+
const trimmed = value.trim();
|
|
13594
|
+
if (trimmed !== value) ctx.notes.push(`trimmed "${key2}"`);
|
|
13595
|
+
return trimmed;
|
|
13596
|
+
}
|
|
13597
|
+
function applyCoerce(key2, raw, ctx) {
|
|
13598
|
+
const field = ctx.shape?.[key2];
|
|
13599
|
+
let coerced = raw;
|
|
13600
|
+
if (field) {
|
|
13601
|
+
for (const candidate of coerceCandidates(raw, ctx.repairJson)) {
|
|
13602
|
+
if (field.safeParse(candidate).success) {
|
|
13603
|
+
coerced = candidate;
|
|
13604
|
+
break;
|
|
13605
|
+
}
|
|
13606
|
+
}
|
|
13607
|
+
} else {
|
|
13608
|
+
coerced = heuristicCoerce(raw, ctx.repairJson);
|
|
13609
|
+
}
|
|
13610
|
+
if (coerced !== raw) ctx.notes.push(`coerced "${key2}"`);
|
|
13611
|
+
return coerced;
|
|
13612
|
+
}
|
|
13613
|
+
function applyRepair(key2, value, ctx) {
|
|
13614
|
+
const repaired = tryJson(value, true);
|
|
13615
|
+
if (repaired === void 0) return value;
|
|
13616
|
+
ctx.notes.push(`repaired json "${key2}"`);
|
|
13617
|
+
return repaired;
|
|
13618
|
+
}
|
|
13619
|
+
function sanitizeString(key2, value, ctx) {
|
|
13620
|
+
let out = ctx.trim ? applyTrim(key2, value, ctx) : value;
|
|
13621
|
+
if (ctx.coerce && typeof out === "string") out = applyCoerce(key2, out, ctx);
|
|
13622
|
+
if (ctx.repairJson && !ctx.coerce && typeof out === "string") out = applyRepair(key2, out, ctx);
|
|
13623
|
+
return out;
|
|
13624
|
+
}
|
|
13625
|
+
function walk(input, ctx, depth) {
|
|
13626
|
+
const out = {};
|
|
13627
|
+
for (const [key2, value] of Object.entries(input)) {
|
|
13628
|
+
if (typeof value === "string") out[key2] = sanitizeString(key2, value, ctx);
|
|
13629
|
+
else if (ctx.deep && depth < ctx.maxDepth && isPlainObject(value))
|
|
13630
|
+
out[key2] = walk(value, ctx, depth + 1);
|
|
13631
|
+
else out[key2] = value;
|
|
13632
|
+
}
|
|
13633
|
+
return out;
|
|
13634
|
+
}
|
|
13635
|
+
function sanitizeToolInput(input, options) {
|
|
13636
|
+
if (!isPlainObject(input)) return { value: input, changed: false, notes: [] };
|
|
13637
|
+
const ctx = {
|
|
13638
|
+
trim: options?.trim ?? true,
|
|
13639
|
+
coerce: options?.coerce ?? false,
|
|
13640
|
+
repairJson: options?.repairJson ?? false,
|
|
13641
|
+
deep: options?.deep ?? false,
|
|
13642
|
+
maxDepth: options?.maxDepth ?? 8,
|
|
13643
|
+
shape: objectShape(options?.schema),
|
|
13644
|
+
notes: []
|
|
13645
|
+
};
|
|
13646
|
+
const value = walk(input, ctx, 0);
|
|
13647
|
+
return { value, changed: ctx.notes.length > 0, notes: ctx.notes };
|
|
13648
|
+
}
|
|
13430
13649
|
|
|
13431
13650
|
// src/internal/llm/hermes-tool-extract.ts
|
|
13432
13651
|
var HERMES_BLOCK = /<function=\s*([^>\s]+)\s*>([\s\S]*?)<\/tool_call>/g;
|
|
@@ -13452,9 +13671,9 @@ function parseHermesParams(inner) {
|
|
|
13452
13671
|
const key2 = param[1];
|
|
13453
13672
|
const value = param[2];
|
|
13454
13673
|
if (key2 === void 0 || value === void 0) continue;
|
|
13455
|
-
input[key2.trim()] = value
|
|
13674
|
+
input[key2.trim()] = value;
|
|
13456
13675
|
}
|
|
13457
|
-
return input;
|
|
13676
|
+
return sanitizeToolInput(input, { trim: true }).value;
|
|
13458
13677
|
}
|
|
13459
13678
|
|
|
13460
13679
|
// src/internal/llm/openai.ts
|
|
@@ -14575,6 +14794,8 @@ function buildLoopInputs(options, runId, userText) {
|
|
|
14575
14794
|
// M1-2: per-send iteration ceiling (validated above). The loop reads
|
|
14576
14795
|
// inputs.maxIterations (default 8 when unset).
|
|
14577
14796
|
...maxIterations !== void 0 ? { maxIterations } : {},
|
|
14797
|
+
// Doom-loop guard config (default on; `false` disables, object tunes thresholds).
|
|
14798
|
+
...options.sendOptions.doomLoop !== void 0 ? { doomLoop: options.sendOptions.doomLoop } : {},
|
|
14578
14799
|
// D315-D317 — tool lifecycle hooks (cost tracking + audit + retry/alert)
|
|
14579
14800
|
...options.agentOptions.onToolStart !== void 0 ? { onToolStart: options.agentOptions.onToolStart } : {},
|
|
14580
14801
|
...options.agentOptions.onToolEnd !== void 0 ? { onToolEnd: options.agentOptions.onToolEnd } : {},
|
|
@@ -14707,6 +14928,7 @@ var RealLocalRun = class extends FixtureRunBase {
|
|
|
14707
14928
|
if (output.usage !== void 0) this.script.usage = output.usage;
|
|
14708
14929
|
if (output.cost !== void 0) this.script.cost = output.cost;
|
|
14709
14930
|
if (output.stoppedAtIterationLimit === true) this.script.stoppedAtIterationLimit = true;
|
|
14931
|
+
if (output.stoppedByDoomLoop === true) this.script.stoppedByDoomLoop = true;
|
|
14710
14932
|
if (output.error !== void 0 && this.script.errorDetail === void 0) {
|
|
14711
14933
|
this.script.errorDetail = {
|
|
14712
14934
|
message: output.error.message,
|
|
@@ -18175,7 +18397,11 @@ function defineTool(spec) {
|
|
|
18175
18397
|
description: spec.description,
|
|
18176
18398
|
inputSchema,
|
|
18177
18399
|
handler: async (input) => {
|
|
18178
|
-
const
|
|
18400
|
+
const raw = spec.sanitize ? sanitizeToolInput(input, {
|
|
18401
|
+
...spec.sanitize === true ? {} : spec.sanitize,
|
|
18402
|
+
schema: spec.inputSchema
|
|
18403
|
+
}).value : input;
|
|
18404
|
+
const parsed = spec.inputSchema.parse(raw);
|
|
18179
18405
|
return await spec.handler(parsed);
|
|
18180
18406
|
}
|
|
18181
18407
|
};
|