@theokit/sdk 2.14.0 → 2.15.1
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 +151 -8
- package/dist/a2a/index.cjs.map +1 -1
- package/dist/a2a/index.js +151 -8
- 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 +144 -8
- 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 +144 -8
- package/dist/cron.js.map +1 -1
- 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 +144 -8
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +144 -8
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +144 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +144 -8
- 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 +5 -1
- package/dist/internal/llm/openai.d.ts +5 -1
- 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/types/run.d.ts +28 -0
- package/package.json +1 -1
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-
|
|
2
|
-
import './run-
|
|
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();
|
|
@@ -11063,11 +11173,16 @@ function sanitizeToolInput(input, options) {
|
|
|
11063
11173
|
// src/internal/llm/hermes-tool-extract.ts
|
|
11064
11174
|
var HERMES_BLOCK = /<function=\s*([^>\s]+)\s*>([\s\S]*?)<\/tool_call>/g;
|
|
11065
11175
|
var HERMES_PARAM = /<parameter=\s*([^>\s]+)\s*>([\s\S]*?)<\/parameter>/g;
|
|
11066
|
-
function extractHermesToolCalls(content, makeId) {
|
|
11176
|
+
function extractHermesToolCalls(content, makeId, allowedToolNames) {
|
|
11177
|
+
const isPromoted = (name) => name.length > 0 && (allowedToolNames === void 0 || allowedToolNames.has(name));
|
|
11067
11178
|
const toolCalls = [];
|
|
11179
|
+
const droppedNames = [];
|
|
11068
11180
|
for (const block of content.matchAll(HERMES_BLOCK)) {
|
|
11069
11181
|
const name = (block[1] ?? "").trim();
|
|
11070
|
-
if (name
|
|
11182
|
+
if (!isPromoted(name)) {
|
|
11183
|
+
if (name.length > 0 && allowedToolNames !== void 0) droppedNames.push(name);
|
|
11184
|
+
continue;
|
|
11185
|
+
}
|
|
11071
11186
|
toolCalls.push({
|
|
11072
11187
|
type: "tool_use",
|
|
11073
11188
|
id: makeId(),
|
|
@@ -11075,8 +11190,11 @@ function extractHermesToolCalls(content, makeId) {
|
|
|
11075
11190
|
input: parseHermesParams(block[2] ?? "")
|
|
11076
11191
|
});
|
|
11077
11192
|
}
|
|
11078
|
-
const residualText = toolCalls.length === 0 ? content : content.replace(
|
|
11079
|
-
|
|
11193
|
+
const residualText = toolCalls.length === 0 ? content : content.replace(
|
|
11194
|
+
HERMES_BLOCK,
|
|
11195
|
+
(full, rawName) => isPromoted((rawName ?? "").trim()) ? "" : full
|
|
11196
|
+
).trim();
|
|
11197
|
+
return { toolCalls, residualText, droppedNames };
|
|
11080
11198
|
}
|
|
11081
11199
|
function parseHermesParams(inner) {
|
|
11082
11200
|
const input = {};
|
|
@@ -11160,7 +11278,10 @@ var OpenAIClient = class {
|
|
|
11160
11278
|
}
|
|
11161
11279
|
const accumulator = new OpenAIStreamAccumulator(
|
|
11162
11280
|
this.options.extractToolCallsFromContent ?? false,
|
|
11163
|
-
providerId
|
|
11281
|
+
providerId,
|
|
11282
|
+
// R5: request-scoped allowlist — leaked recovery only promotes a block whose name is a tool the
|
|
11283
|
+
// model was actually given. Empty set (no tools) recovers nothing.
|
|
11284
|
+
new Set(request.tools?.map((tool) => tool.name) ?? [])
|
|
11164
11285
|
);
|
|
11165
11286
|
for await (const record of parseSseStream(response.body, signal)) {
|
|
11166
11287
|
if (record.data === "[DONE]") break;
|
|
@@ -11190,13 +11311,18 @@ var OpenAIStreamAccumulator = class {
|
|
|
11190
11311
|
/**
|
|
11191
11312
|
* @param extractFromContent opt-in leaked-dialect safe-parse (theokit#58). Default false.
|
|
11192
11313
|
* @param providerName provider id, used only to label the recovery log line.
|
|
11314
|
+
* @param allowedToolNames R5 request-scoped allowlist — built from `request.tools` at `stream()`;
|
|
11315
|
+
* leaked recovery in `finish()` only promotes a block whose name is in this set. `undefined`
|
|
11316
|
+
* (direct construction) recovers all (back-compat); an empty set recovers nothing.
|
|
11193
11317
|
*/
|
|
11194
|
-
constructor(extractFromContent = false, providerName = "openai") {
|
|
11318
|
+
constructor(extractFromContent = false, providerName = "openai", allowedToolNames) {
|
|
11195
11319
|
this.extractFromContent = extractFromContent;
|
|
11196
11320
|
this.providerName = providerName;
|
|
11321
|
+
this.allowedToolNames = allowedToolNames;
|
|
11197
11322
|
}
|
|
11198
11323
|
extractFromContent;
|
|
11199
11324
|
providerName;
|
|
11325
|
+
allowedToolNames;
|
|
11200
11326
|
text = "";
|
|
11201
11327
|
stopReason = "end_turn";
|
|
11202
11328
|
inputTokens;
|
|
@@ -11267,7 +11393,8 @@ var OpenAIStreamAccumulator = class {
|
|
|
11267
11393
|
if (this.extractFromContent && toolCalls.length === 0) {
|
|
11268
11394
|
const recovered = extractHermesToolCalls(
|
|
11269
11395
|
this.text,
|
|
11270
|
-
() => `hermes-${globalThis.crypto.randomUUID()}
|
|
11396
|
+
() => `hermes-${globalThis.crypto.randomUUID()}`,
|
|
11397
|
+
this.allowedToolNames
|
|
11271
11398
|
);
|
|
11272
11399
|
if (recovered.toolCalls.length > 0) {
|
|
11273
11400
|
toolCalls.push(...recovered.toolCalls);
|
|
@@ -11275,6 +11402,12 @@ var OpenAIStreamAccumulator = class {
|
|
|
11275
11402
|
stopReason = "tool_use";
|
|
11276
11403
|
process.stderr.write(
|
|
11277
11404
|
`[theokit-sdk] recovered ${recovered.toolCalls.length} leaked tool call(s) from assistant content (provider="${this.providerName}", names=${recovered.toolCalls.map((c) => c.name).join(",")})
|
|
11405
|
+
`
|
|
11406
|
+
);
|
|
11407
|
+
}
|
|
11408
|
+
if (recovered.droppedNames.length > 0) {
|
|
11409
|
+
process.stderr.write(
|
|
11410
|
+
`[theokit-sdk] dropped ${recovered.droppedNames.length} leaked block(s) whose name is not a tool in the request (provider="${this.providerName}", names=${recovered.droppedNames.join(",")})
|
|
11278
11411
|
`
|
|
11279
11412
|
);
|
|
11280
11413
|
}
|
|
@@ -12206,6 +12339,8 @@ function buildLoopInputs(options, runId, userText) {
|
|
|
12206
12339
|
// M1-2: per-send iteration ceiling (validated above). The loop reads
|
|
12207
12340
|
// inputs.maxIterations (default 8 when unset).
|
|
12208
12341
|
...maxIterations !== void 0 ? { maxIterations } : {},
|
|
12342
|
+
// Doom-loop guard config (default on; `false` disables, object tunes thresholds).
|
|
12343
|
+
...options.sendOptions.doomLoop !== void 0 ? { doomLoop: options.sendOptions.doomLoop } : {},
|
|
12209
12344
|
// D315-D317 — tool lifecycle hooks (cost tracking + audit + retry/alert)
|
|
12210
12345
|
...options.agentOptions.onToolStart !== void 0 ? { onToolStart: options.agentOptions.onToolStart } : {},
|
|
12211
12346
|
...options.agentOptions.onToolEnd !== void 0 ? { onToolEnd: options.agentOptions.onToolEnd } : {},
|
|
@@ -12338,6 +12473,7 @@ var RealLocalRun = class extends FixtureRunBase {
|
|
|
12338
12473
|
if (output.usage !== void 0) this.script.usage = output.usage;
|
|
12339
12474
|
if (output.cost !== void 0) this.script.cost = output.cost;
|
|
12340
12475
|
if (output.stoppedAtIterationLimit === true) this.script.stoppedAtIterationLimit = true;
|
|
12476
|
+
if (output.stoppedByDoomLoop === true) this.script.stoppedByDoomLoop = true;
|
|
12341
12477
|
if (output.error !== void 0 && this.script.errorDetail === void 0) {
|
|
12342
12478
|
this.script.errorDetail = {
|
|
12343
12479
|
message: output.error.message,
|