@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d7057f2: Add a **doom-loop / no-progress guard** to the agent loop. The loop now detects when the model repeats IDENTICAL tool calls (same name + same canonical input) that make no progress — the qwen3-coder `read_file`/`not_found` failure mode where the model retries the same failing call and the run grinds to the iteration ceiling — and stops early with a typed `no_progress` terminal instead of hanging. A pure `DoomLoopTracker` (canonical key-sorted-JSON signature + a consecutive-identical counter) escalates from a one-time guidance nudge at a soft threshold to a hard stop; the hard stop surfaces on `RunResult.stoppedByDoomLoop` and, through the continuation driver, as `terminal: "no_progress"` (so the outer loop does not re-send). It complements — does not replace — the existing empty-round `no_progress` (a different failure mode: model stuck repeating vs model gone silent). On by default with generous thresholds (soft 3 / hard 5); tune or disable per send via `SendOptions.doomLoop` (`false` to disable, or `{ softThreshold, hardThreshold }` to tune). Dependency-free. Grounded in a SOTA study of cline's LoopDetectionTracker + opencode's doom-loop.
|
|
8
|
+
|
|
9
|
+
## 2.14.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 6ee4217: Add a public, isolated tool-input **sanitization** primitive on the new `@theokit/sdk/sanitize` subpath, plus a declarative `defineTool({ sanitize })` opt-in. Custom tools can now clean the raw arguments a model emits before they reach the tool schema: `sanitizeToolInput(input, options?)` trims whitespace by default and — opt-in — coerces string values toward their expected type (`"5"`→`5`, `"true"`→`true`, JSON-encoded strings→arrays/objects) and repairs malformed JSON (via `jsonrepair`). Coercion is guarded against silent corruption: numeric coercion round-trips and stays finite, so ID-like strings (`"12345678901234567890"`, `"007"`) and `NaN`/`Infinity` are left as strings; JSON repair only runs on JSON-looking values; a non-object input is returned untouched (the primitive is total — it never throws). When a Zod object schema is passed, coercion is schema-aware (a `z.string()` field keeps `"5"` a string). `defineTool({ sanitize: true })` trims the raw args before validation; `defineTool({ sanitize: { coerce: true } })` additionally coerces toward the tool's own schema — absent, `defineTool` behaviour is unchanged. Internally, the leaked-dialect recovery (`hermes-tool-extract`) now reuses the same primitive, so the public surface and the internal path never diverge. Grounded in a SOTA study of openclaw / agentfw / opencode / cline / vercel-ai-sdk.
|
|
14
|
+
|
|
3
15
|
## 2.13.1
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/a2a/index.cjs
CHANGED
|
@@ -2336,6 +2336,7 @@ function applyScriptMetrics(base, script) {
|
|
|
2336
2336
|
if (script.usage !== void 0) base.usage = script.usage;
|
|
2337
2337
|
if (script.cost !== void 0) base.cost = script.cost;
|
|
2338
2338
|
if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
|
|
2339
|
+
if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
|
|
2339
2340
|
}
|
|
2340
2341
|
var FixtureRunBase;
|
|
2341
2342
|
var init_fixture_run_base = __esm({
|
|
@@ -6812,6 +6813,98 @@ var init_budget_gate = __esm({
|
|
|
6812
6813
|
}
|
|
6813
6814
|
});
|
|
6814
6815
|
|
|
6816
|
+
// src/internal/agent-loop/doom-loop-tracker.ts
|
|
6817
|
+
function createDoomLoopTracker(option) {
|
|
6818
|
+
if (option === false) return void 0;
|
|
6819
|
+
return new DoomLoopTracker(option);
|
|
6820
|
+
}
|
|
6821
|
+
function assertValidThresholds(soft, hard) {
|
|
6822
|
+
for (const [label, value] of [
|
|
6823
|
+
["softThreshold", soft],
|
|
6824
|
+
["hardThreshold", hard]
|
|
6825
|
+
]) {
|
|
6826
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
6827
|
+
throw new ConfigurationError(
|
|
6828
|
+
`doomLoop.${label} must be a positive integer (received ${value}).`,
|
|
6829
|
+
{ code: "invalid_doom_loop_threshold" }
|
|
6830
|
+
);
|
|
6831
|
+
}
|
|
6832
|
+
}
|
|
6833
|
+
}
|
|
6834
|
+
function sortKeys(value) {
|
|
6835
|
+
if (value === null || typeof value !== "object") return value;
|
|
6836
|
+
if (Array.isArray(value)) return value.map(sortKeys);
|
|
6837
|
+
const out = {};
|
|
6838
|
+
for (const key of Object.keys(value).sort()) {
|
|
6839
|
+
out[key] = sortKeys(value[key]);
|
|
6840
|
+
}
|
|
6841
|
+
return out;
|
|
6842
|
+
}
|
|
6843
|
+
function signatureOf(call) {
|
|
6844
|
+
const { input } = call;
|
|
6845
|
+
let inputSig;
|
|
6846
|
+
if (input === null || input === void 0) inputSig = "null";
|
|
6847
|
+
else if (typeof input !== "object") inputSig = String(input);
|
|
6848
|
+
else {
|
|
6849
|
+
try {
|
|
6850
|
+
inputSig = JSON.stringify(sortKeys(input)) ?? "null";
|
|
6851
|
+
} catch {
|
|
6852
|
+
inputSig = String(input);
|
|
6853
|
+
}
|
|
6854
|
+
}
|
|
6855
|
+
return `${call.name}\0${inputSig}`;
|
|
6856
|
+
}
|
|
6857
|
+
function firstDoomLoopVerdict(tracker, calls) {
|
|
6858
|
+
let escalation = { kind: "ok" };
|
|
6859
|
+
for (const call of calls) {
|
|
6860
|
+
const v = tracker.inspect(call);
|
|
6861
|
+
if (v.kind === "hard") return v;
|
|
6862
|
+
if (v.kind === "soft" && escalation.kind === "ok") escalation = v;
|
|
6863
|
+
}
|
|
6864
|
+
return escalation;
|
|
6865
|
+
}
|
|
6866
|
+
var DEFAULT_CONFIG, DoomLoopTracker;
|
|
6867
|
+
var init_doom_loop_tracker = __esm({
|
|
6868
|
+
"src/internal/agent-loop/doom-loop-tracker.ts"() {
|
|
6869
|
+
init_errors();
|
|
6870
|
+
DEFAULT_CONFIG = { softThreshold: 3, hardThreshold: 5 };
|
|
6871
|
+
DoomLoopTracker = class {
|
|
6872
|
+
#config;
|
|
6873
|
+
#lastSignature = "";
|
|
6874
|
+
#count = 0;
|
|
6875
|
+
constructor(config) {
|
|
6876
|
+
const softThreshold = config?.softThreshold ?? DEFAULT_CONFIG.softThreshold;
|
|
6877
|
+
const hardThreshold = config?.hardThreshold ?? DEFAULT_CONFIG.hardThreshold;
|
|
6878
|
+
assertValidThresholds(softThreshold, hardThreshold);
|
|
6879
|
+
this.#config = { softThreshold, hardThreshold };
|
|
6880
|
+
}
|
|
6881
|
+
inspect(call) {
|
|
6882
|
+
const signature = signatureOf(call);
|
|
6883
|
+
this.#count = signature === this.#lastSignature ? this.#count + 1 : 1;
|
|
6884
|
+
this.#lastSignature = signature;
|
|
6885
|
+
const count = this.#count;
|
|
6886
|
+
if (count >= this.#config.hardThreshold) {
|
|
6887
|
+
return {
|
|
6888
|
+
kind: "hard",
|
|
6889
|
+
message: `Detected ${count} consecutive identical calls to \`${call.name}\`; stopping to avoid a loop.`
|
|
6890
|
+
};
|
|
6891
|
+
}
|
|
6892
|
+
if (count === this.#config.softThreshold) {
|
|
6893
|
+
return {
|
|
6894
|
+
kind: "soft",
|
|
6895
|
+
message: `Detected ${count} consecutive identical calls to \`${call.name}\`; try a different approach.`
|
|
6896
|
+
};
|
|
6897
|
+
}
|
|
6898
|
+
return { kind: "ok" };
|
|
6899
|
+
}
|
|
6900
|
+
reset() {
|
|
6901
|
+
this.#lastSignature = "";
|
|
6902
|
+
this.#count = 0;
|
|
6903
|
+
}
|
|
6904
|
+
};
|
|
6905
|
+
}
|
|
6906
|
+
});
|
|
6907
|
+
|
|
6815
6908
|
// src/internal/budget/usage-accumulator.ts
|
|
6816
6909
|
var UsageAccumulator;
|
|
6817
6910
|
var init_usage_accumulator = __esm({
|
|
@@ -7001,6 +7094,7 @@ async function initLoopContext(inputs) {
|
|
|
7001
7094
|
tools,
|
|
7002
7095
|
finalText: "",
|
|
7003
7096
|
finalStatus: "finished",
|
|
7097
|
+
doomLoop: createDoomLoopTracker(inputs.doomLoop),
|
|
7004
7098
|
usage: new UsageAccumulator(),
|
|
7005
7099
|
nudgeAttempts: 0,
|
|
7006
7100
|
stopFeedbackAttempts: 0,
|
|
@@ -7053,6 +7147,7 @@ function sanitize(name) {
|
|
|
7053
7147
|
var init_loop_context_init = __esm({
|
|
7054
7148
|
"src/internal/agent-loop/loop-context-init.ts"() {
|
|
7055
7149
|
init_usage_accumulator();
|
|
7150
|
+
init_doom_loop_tracker();
|
|
7056
7151
|
init_message_builders();
|
|
7057
7152
|
}
|
|
7058
7153
|
});
|
|
@@ -8165,6 +8260,7 @@ async function runAgentLoop(inputs) {
|
|
|
8165
8260
|
ctx.finalStatus = "error";
|
|
8166
8261
|
}
|
|
8167
8262
|
sendSpan?.setAttribute("status", ctx.finalStatus);
|
|
8263
|
+
if (ctx.stoppedByDoomLoop === true) sendSpan?.setAttribute("stoppedByDoomLoop", true);
|
|
8168
8264
|
if (inputs.telemetry?.includeContent === true && ctx.finalText.length > 0) {
|
|
8169
8265
|
sendSpan?.addEvent("response", { content: ctx.finalText });
|
|
8170
8266
|
}
|
|
@@ -8191,7 +8287,8 @@ async function runAgentLoop(inputs) {
|
|
|
8191
8287
|
...usage !== void 0 ? { usage } : {},
|
|
8192
8288
|
...cost !== void 0 ? { cost } : {},
|
|
8193
8289
|
...ctx.error !== void 0 ? { error: ctx.error } : {},
|
|
8194
|
-
...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {}
|
|
8290
|
+
...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {},
|
|
8291
|
+
...ctx.stoppedByDoomLoop === true ? { stoppedByDoomLoop: true } : {}
|
|
8195
8292
|
};
|
|
8196
8293
|
} finally {
|
|
8197
8294
|
if (ctxRef !== void 0 && ctxRef.memoryProviderHandle !== void 0 && inputs.memoryProvider !== void 0) {
|
|
@@ -8350,8 +8447,26 @@ async function continueOrTerminate(inputs, ctx, llmOutput) {
|
|
|
8350
8447
|
}
|
|
8351
8448
|
}
|
|
8352
8449
|
pushToolConversationSteps(ctx, llmOutput.toolCalls, toolResults);
|
|
8450
|
+
if (await inspectDoomLoop(inputs, ctx, llmOutput.toolCalls) === "stop") return "done";
|
|
8353
8451
|
return handleToolErrorContinuation(inputs, ctx, toolResults);
|
|
8354
8452
|
}
|
|
8453
|
+
async function inspectDoomLoop(inputs, ctx, toolCalls) {
|
|
8454
|
+
if (ctx.doomLoop === void 0) return "continue";
|
|
8455
|
+
const verdict = firstDoomLoopVerdict(ctx.doomLoop, toolCalls);
|
|
8456
|
+
if (verdict.kind === "hard") {
|
|
8457
|
+
ctx.stoppedByDoomLoop = true;
|
|
8458
|
+
await emitAssistantTextStep(
|
|
8459
|
+
inputs,
|
|
8460
|
+
ctx,
|
|
8461
|
+
verdict.message ?? "Stopped: repeated identical tool calls made no progress."
|
|
8462
|
+
);
|
|
8463
|
+
return "stop";
|
|
8464
|
+
}
|
|
8465
|
+
if (verdict.kind === "soft") {
|
|
8466
|
+
ctx.messages.push({ role: "user", content: [{ type: "text", text: verdict.message ?? "" }] });
|
|
8467
|
+
}
|
|
8468
|
+
return "continue";
|
|
8469
|
+
}
|
|
8355
8470
|
var MAX_NUDGE_ATTEMPTS, MAX_STOP_FEEDBACK_ATTEMPTS;
|
|
8356
8471
|
var init_loop = __esm({
|
|
8357
8472
|
"src/internal/agent-loop/loop.ts"() {
|
|
@@ -8359,6 +8474,7 @@ var init_loop = __esm({
|
|
|
8359
8474
|
init_safe_call();
|
|
8360
8475
|
init_validate_response();
|
|
8361
8476
|
init_budget_gate();
|
|
8477
|
+
init_doom_loop_tracker();
|
|
8362
8478
|
init_loop_context_init();
|
|
8363
8479
|
init_loop_llm_stream();
|
|
8364
8480
|
init_message_builders();
|
|
@@ -10096,6 +10212,124 @@ var init_ollama_native = __esm({
|
|
|
10096
10212
|
ollamaSystemText = collapseSystemText;
|
|
10097
10213
|
}
|
|
10098
10214
|
});
|
|
10215
|
+
function loadJsonrepair() {
|
|
10216
|
+
if (cachedJsonrepair === void 0) {
|
|
10217
|
+
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)));
|
|
10218
|
+
cachedJsonrepair = req("jsonrepair").jsonrepair;
|
|
10219
|
+
}
|
|
10220
|
+
return cachedJsonrepair;
|
|
10221
|
+
}
|
|
10222
|
+
function isPlainObject(v) {
|
|
10223
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
10224
|
+
}
|
|
10225
|
+
function toFiniteNumber(raw) {
|
|
10226
|
+
if (raw === "") return void 0;
|
|
10227
|
+
const n = Number(raw);
|
|
10228
|
+
return Number.isFinite(n) && String(n) === raw ? n : void 0;
|
|
10229
|
+
}
|
|
10230
|
+
function tryJson(raw, repair) {
|
|
10231
|
+
const t = raw.trimStart();
|
|
10232
|
+
if (!(t.startsWith("{") || t.startsWith("["))) return void 0;
|
|
10233
|
+
try {
|
|
10234
|
+
return JSON.parse(repair ? loadJsonrepair()(t) : t);
|
|
10235
|
+
} catch {
|
|
10236
|
+
return void 0;
|
|
10237
|
+
}
|
|
10238
|
+
}
|
|
10239
|
+
function heuristicCoerce(raw, repairJson) {
|
|
10240
|
+
if (raw === "true") return true;
|
|
10241
|
+
if (raw === "false") return false;
|
|
10242
|
+
if (raw === "null") return null;
|
|
10243
|
+
const n = toFiniteNumber(raw);
|
|
10244
|
+
if (n !== void 0) return n;
|
|
10245
|
+
const json = tryJson(raw, false) ?? (repairJson ? tryJson(raw, true) : void 0);
|
|
10246
|
+
return json === void 0 ? raw : json;
|
|
10247
|
+
}
|
|
10248
|
+
function coerceCandidates(raw, repairJson) {
|
|
10249
|
+
const out = [];
|
|
10250
|
+
if (raw === "true") out.push(true);
|
|
10251
|
+
else if (raw === "false") out.push(false);
|
|
10252
|
+
else if (raw === "null") out.push(null);
|
|
10253
|
+
const n = toFiniteNumber(raw);
|
|
10254
|
+
if (n !== void 0) out.push(n);
|
|
10255
|
+
const json = tryJson(raw, false) ?? (repairJson ? tryJson(raw, true) : void 0);
|
|
10256
|
+
if (json !== void 0) out.push(json);
|
|
10257
|
+
out.push(raw);
|
|
10258
|
+
return out;
|
|
10259
|
+
}
|
|
10260
|
+
function objectShape(schema) {
|
|
10261
|
+
const shape = schema?.shape;
|
|
10262
|
+
return shape !== null && typeof shape === "object" ? shape : void 0;
|
|
10263
|
+
}
|
|
10264
|
+
var cachedJsonrepair;
|
|
10265
|
+
var init_coerce = __esm({
|
|
10266
|
+
"src/sanitize/coerce.ts"() {
|
|
10267
|
+
}
|
|
10268
|
+
});
|
|
10269
|
+
|
|
10270
|
+
// src/sanitize/sanitize-tool-input.ts
|
|
10271
|
+
function applyTrim(key, value, ctx) {
|
|
10272
|
+
const trimmed = value.trim();
|
|
10273
|
+
if (trimmed !== value) ctx.notes.push(`trimmed "${key}"`);
|
|
10274
|
+
return trimmed;
|
|
10275
|
+
}
|
|
10276
|
+
function applyCoerce(key, raw, ctx) {
|
|
10277
|
+
const field = ctx.shape?.[key];
|
|
10278
|
+
let coerced = raw;
|
|
10279
|
+
if (field) {
|
|
10280
|
+
for (const candidate of coerceCandidates(raw, ctx.repairJson)) {
|
|
10281
|
+
if (field.safeParse(candidate).success) {
|
|
10282
|
+
coerced = candidate;
|
|
10283
|
+
break;
|
|
10284
|
+
}
|
|
10285
|
+
}
|
|
10286
|
+
} else {
|
|
10287
|
+
coerced = heuristicCoerce(raw, ctx.repairJson);
|
|
10288
|
+
}
|
|
10289
|
+
if (coerced !== raw) ctx.notes.push(`coerced "${key}"`);
|
|
10290
|
+
return coerced;
|
|
10291
|
+
}
|
|
10292
|
+
function applyRepair(key, value, ctx) {
|
|
10293
|
+
const repaired = tryJson(value, true);
|
|
10294
|
+
if (repaired === void 0) return value;
|
|
10295
|
+
ctx.notes.push(`repaired json "${key}"`);
|
|
10296
|
+
return repaired;
|
|
10297
|
+
}
|
|
10298
|
+
function sanitizeString(key, value, ctx) {
|
|
10299
|
+
let out = ctx.trim ? applyTrim(key, value, ctx) : value;
|
|
10300
|
+
if (ctx.coerce && typeof out === "string") out = applyCoerce(key, out, ctx);
|
|
10301
|
+
if (ctx.repairJson && !ctx.coerce && typeof out === "string") out = applyRepair(key, out, ctx);
|
|
10302
|
+
return out;
|
|
10303
|
+
}
|
|
10304
|
+
function walk(input, ctx, depth) {
|
|
10305
|
+
const out = {};
|
|
10306
|
+
for (const [key, value] of Object.entries(input)) {
|
|
10307
|
+
if (typeof value === "string") out[key] = sanitizeString(key, value, ctx);
|
|
10308
|
+
else if (ctx.deep && depth < ctx.maxDepth && isPlainObject(value))
|
|
10309
|
+
out[key] = walk(value, ctx, depth + 1);
|
|
10310
|
+
else out[key] = value;
|
|
10311
|
+
}
|
|
10312
|
+
return out;
|
|
10313
|
+
}
|
|
10314
|
+
function sanitizeToolInput(input, options) {
|
|
10315
|
+
if (!isPlainObject(input)) return { value: input, changed: false, notes: [] };
|
|
10316
|
+
const ctx = {
|
|
10317
|
+
trim: options?.trim,
|
|
10318
|
+
coerce: options?.coerce ?? false,
|
|
10319
|
+
repairJson: options?.repairJson ?? false,
|
|
10320
|
+
deep: options?.deep ?? false,
|
|
10321
|
+
maxDepth: options?.maxDepth ?? 8,
|
|
10322
|
+
shape: objectShape(options?.schema),
|
|
10323
|
+
notes: []
|
|
10324
|
+
};
|
|
10325
|
+
const value = walk(input, ctx, 0);
|
|
10326
|
+
return { value, changed: ctx.notes.length > 0, notes: ctx.notes };
|
|
10327
|
+
}
|
|
10328
|
+
var init_sanitize_tool_input = __esm({
|
|
10329
|
+
"src/sanitize/sanitize-tool-input.ts"() {
|
|
10330
|
+
init_coerce();
|
|
10331
|
+
}
|
|
10332
|
+
});
|
|
10099
10333
|
|
|
10100
10334
|
// src/internal/llm/hermes-tool-extract.ts
|
|
10101
10335
|
function extractHermesToolCalls(content, makeId) {
|
|
@@ -10119,13 +10353,14 @@ function parseHermesParams(inner) {
|
|
|
10119
10353
|
const key = param[1];
|
|
10120
10354
|
const value = param[2];
|
|
10121
10355
|
if (key === void 0 || value === void 0) continue;
|
|
10122
|
-
input[key.trim()] = value
|
|
10356
|
+
input[key.trim()] = value;
|
|
10123
10357
|
}
|
|
10124
|
-
return input;
|
|
10358
|
+
return sanitizeToolInput(input, { trim: true }).value;
|
|
10125
10359
|
}
|
|
10126
10360
|
var HERMES_BLOCK, HERMES_PARAM;
|
|
10127
10361
|
var init_hermes_tool_extract = __esm({
|
|
10128
10362
|
"src/internal/llm/hermes-tool-extract.ts"() {
|
|
10363
|
+
init_sanitize_tool_input();
|
|
10129
10364
|
HERMES_BLOCK = /<function=\s*([^>\s]+)\s*>([\s\S]*?)<\/tool_call>/g;
|
|
10130
10365
|
HERMES_PARAM = /<parameter=\s*([^>\s]+)\s*>([\s\S]*?)<\/parameter>/g;
|
|
10131
10366
|
}
|
|
@@ -11324,6 +11559,8 @@ function buildLoopInputs(options, runId, userText) {
|
|
|
11324
11559
|
// M1-2: per-send iteration ceiling (validated above). The loop reads
|
|
11325
11560
|
// inputs.maxIterations (default 8 when unset).
|
|
11326
11561
|
...maxIterations !== void 0 ? { maxIterations } : {},
|
|
11562
|
+
// Doom-loop guard config (default on; `false` disables, object tunes thresholds).
|
|
11563
|
+
...options.sendOptions.doomLoop !== void 0 ? { doomLoop: options.sendOptions.doomLoop } : {},
|
|
11327
11564
|
// D315-D317 — tool lifecycle hooks (cost tracking + audit + retry/alert)
|
|
11328
11565
|
...options.agentOptions.onToolStart !== void 0 ? { onToolStart: options.agentOptions.onToolStart } : {},
|
|
11329
11566
|
...options.agentOptions.onToolEnd !== void 0 ? { onToolEnd: options.agentOptions.onToolEnd } : {},
|
|
@@ -11472,6 +11709,7 @@ var init_real_local_run = __esm({
|
|
|
11472
11709
|
if (output.usage !== void 0) this.script.usage = output.usage;
|
|
11473
11710
|
if (output.cost !== void 0) this.script.cost = output.cost;
|
|
11474
11711
|
if (output.stoppedAtIterationLimit === true) this.script.stoppedAtIterationLimit = true;
|
|
11712
|
+
if (output.stoppedByDoomLoop === true) this.script.stoppedByDoomLoop = true;
|
|
11475
11713
|
if (output.error !== void 0 && this.script.errorDetail === void 0) {
|
|
11476
11714
|
this.script.errorDetail = {
|
|
11477
11715
|
message: output.error.message,
|
|
@@ -14463,6 +14701,7 @@ function isEmptyRound(result) {
|
|
|
14463
14701
|
return (result.result ?? "").trim() === "";
|
|
14464
14702
|
}
|
|
14465
14703
|
function classifyRound(result, round, maxRounds, emptyStreak) {
|
|
14704
|
+
if (result.stoppedByDoomLoop === true) return "no_progress";
|
|
14466
14705
|
if (result.stoppedAtIterationLimit !== true) return "done";
|
|
14467
14706
|
if (isEmptyRound(result) && emptyStreak >= 1) return "no_progress";
|
|
14468
14707
|
if (round >= maxRounds) return "step_limit";
|