@theokit/sdk 2.13.0 → 2.14.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 +120 -1
- package/dist/a2a/index.cjs.map +1 -1
- package/dist/a2a/index.js +120 -1
- package/dist/a2a/index.js.map +1 -1
- package/dist/cron.cjs +110 -1
- package/dist/cron.cjs.map +1 -1
- package/dist/cron.js +110 -1
- package/dist/cron.js.map +1 -1
- package/dist/define-tool.d.ts +8 -0
- package/dist/eval.cjs +113 -4
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +113 -4
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +115 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +115 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/llm/hermes-tool-extract.d.ts +1 -0
- 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/package.json +13 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
|
|
9
|
+
## 2.13.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 65763b9: Trim leaked-dialect tool-call parameter values during recovery. Models that leak the Hermes dialect as text (qwen3-coder) emit each parameter on its own line, so the recovered value carried the formatting newlines: `<parameter=path>\npackage.json\n</parameter>` produced `{ path: "\npackage.json\n" }`. Untrimmed, `read_file` / `glob_files` / `search_text` received a path/pattern wrapped in newlines and failed `not_found` (only `shell_exec` tolerated it, since bash ignores blank lines), so a multi-read investigation loop kept re-reading, never converged, and appeared to hang. The recovery extractor now trims each parameter value (leading/trailing whitespace only — internal newlines of a legitimate multi-line command survive), matching the native `tool_calls` path where such formatting noise never occurs. Values remain strings; downstream schema coercion is unchanged.
|
|
14
|
+
|
|
3
15
|
## 2.13.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/dist/a2a/index.cjs
CHANGED
|
@@ -10096,6 +10096,124 @@ var init_ollama_native = __esm({
|
|
|
10096
10096
|
ollamaSystemText = collapseSystemText;
|
|
10097
10097
|
}
|
|
10098
10098
|
});
|
|
10099
|
+
function loadJsonrepair() {
|
|
10100
|
+
if (cachedJsonrepair === void 0) {
|
|
10101
|
+
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)));
|
|
10102
|
+
cachedJsonrepair = req("jsonrepair").jsonrepair;
|
|
10103
|
+
}
|
|
10104
|
+
return cachedJsonrepair;
|
|
10105
|
+
}
|
|
10106
|
+
function isPlainObject(v) {
|
|
10107
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
10108
|
+
}
|
|
10109
|
+
function toFiniteNumber(raw) {
|
|
10110
|
+
if (raw === "") return void 0;
|
|
10111
|
+
const n = Number(raw);
|
|
10112
|
+
return Number.isFinite(n) && String(n) === raw ? n : void 0;
|
|
10113
|
+
}
|
|
10114
|
+
function tryJson(raw, repair) {
|
|
10115
|
+
const t = raw.trimStart();
|
|
10116
|
+
if (!(t.startsWith("{") || t.startsWith("["))) return void 0;
|
|
10117
|
+
try {
|
|
10118
|
+
return JSON.parse(repair ? loadJsonrepair()(t) : t);
|
|
10119
|
+
} catch {
|
|
10120
|
+
return void 0;
|
|
10121
|
+
}
|
|
10122
|
+
}
|
|
10123
|
+
function heuristicCoerce(raw, repairJson) {
|
|
10124
|
+
if (raw === "true") return true;
|
|
10125
|
+
if (raw === "false") return false;
|
|
10126
|
+
if (raw === "null") return null;
|
|
10127
|
+
const n = toFiniteNumber(raw);
|
|
10128
|
+
if (n !== void 0) return n;
|
|
10129
|
+
const json = tryJson(raw, false) ?? (repairJson ? tryJson(raw, true) : void 0);
|
|
10130
|
+
return json === void 0 ? raw : json;
|
|
10131
|
+
}
|
|
10132
|
+
function coerceCandidates(raw, repairJson) {
|
|
10133
|
+
const out = [];
|
|
10134
|
+
if (raw === "true") out.push(true);
|
|
10135
|
+
else if (raw === "false") out.push(false);
|
|
10136
|
+
else if (raw === "null") out.push(null);
|
|
10137
|
+
const n = toFiniteNumber(raw);
|
|
10138
|
+
if (n !== void 0) out.push(n);
|
|
10139
|
+
const json = tryJson(raw, false) ?? (repairJson ? tryJson(raw, true) : void 0);
|
|
10140
|
+
if (json !== void 0) out.push(json);
|
|
10141
|
+
out.push(raw);
|
|
10142
|
+
return out;
|
|
10143
|
+
}
|
|
10144
|
+
function objectShape(schema) {
|
|
10145
|
+
const shape = schema?.shape;
|
|
10146
|
+
return shape !== null && typeof shape === "object" ? shape : void 0;
|
|
10147
|
+
}
|
|
10148
|
+
var cachedJsonrepair;
|
|
10149
|
+
var init_coerce = __esm({
|
|
10150
|
+
"src/sanitize/coerce.ts"() {
|
|
10151
|
+
}
|
|
10152
|
+
});
|
|
10153
|
+
|
|
10154
|
+
// src/sanitize/sanitize-tool-input.ts
|
|
10155
|
+
function applyTrim(key, value, ctx) {
|
|
10156
|
+
const trimmed = value.trim();
|
|
10157
|
+
if (trimmed !== value) ctx.notes.push(`trimmed "${key}"`);
|
|
10158
|
+
return trimmed;
|
|
10159
|
+
}
|
|
10160
|
+
function applyCoerce(key, raw, ctx) {
|
|
10161
|
+
const field = ctx.shape?.[key];
|
|
10162
|
+
let coerced = raw;
|
|
10163
|
+
if (field) {
|
|
10164
|
+
for (const candidate of coerceCandidates(raw, ctx.repairJson)) {
|
|
10165
|
+
if (field.safeParse(candidate).success) {
|
|
10166
|
+
coerced = candidate;
|
|
10167
|
+
break;
|
|
10168
|
+
}
|
|
10169
|
+
}
|
|
10170
|
+
} else {
|
|
10171
|
+
coerced = heuristicCoerce(raw, ctx.repairJson);
|
|
10172
|
+
}
|
|
10173
|
+
if (coerced !== raw) ctx.notes.push(`coerced "${key}"`);
|
|
10174
|
+
return coerced;
|
|
10175
|
+
}
|
|
10176
|
+
function applyRepair(key, value, ctx) {
|
|
10177
|
+
const repaired = tryJson(value, true);
|
|
10178
|
+
if (repaired === void 0) return value;
|
|
10179
|
+
ctx.notes.push(`repaired json "${key}"`);
|
|
10180
|
+
return repaired;
|
|
10181
|
+
}
|
|
10182
|
+
function sanitizeString(key, value, ctx) {
|
|
10183
|
+
let out = ctx.trim ? applyTrim(key, value, ctx) : value;
|
|
10184
|
+
if (ctx.coerce && typeof out === "string") out = applyCoerce(key, out, ctx);
|
|
10185
|
+
if (ctx.repairJson && !ctx.coerce && typeof out === "string") out = applyRepair(key, out, ctx);
|
|
10186
|
+
return out;
|
|
10187
|
+
}
|
|
10188
|
+
function walk(input, ctx, depth) {
|
|
10189
|
+
const out = {};
|
|
10190
|
+
for (const [key, value] of Object.entries(input)) {
|
|
10191
|
+
if (typeof value === "string") out[key] = sanitizeString(key, value, ctx);
|
|
10192
|
+
else if (ctx.deep && depth < ctx.maxDepth && isPlainObject(value))
|
|
10193
|
+
out[key] = walk(value, ctx, depth + 1);
|
|
10194
|
+
else out[key] = value;
|
|
10195
|
+
}
|
|
10196
|
+
return out;
|
|
10197
|
+
}
|
|
10198
|
+
function sanitizeToolInput(input, options) {
|
|
10199
|
+
if (!isPlainObject(input)) return { value: input, changed: false, notes: [] };
|
|
10200
|
+
const ctx = {
|
|
10201
|
+
trim: options?.trim,
|
|
10202
|
+
coerce: options?.coerce ?? false,
|
|
10203
|
+
repairJson: options?.repairJson ?? false,
|
|
10204
|
+
deep: options?.deep ?? false,
|
|
10205
|
+
maxDepth: options?.maxDepth ?? 8,
|
|
10206
|
+
shape: objectShape(options?.schema),
|
|
10207
|
+
notes: []
|
|
10208
|
+
};
|
|
10209
|
+
const value = walk(input, ctx, 0);
|
|
10210
|
+
return { value, changed: ctx.notes.length > 0, notes: ctx.notes };
|
|
10211
|
+
}
|
|
10212
|
+
var init_sanitize_tool_input = __esm({
|
|
10213
|
+
"src/sanitize/sanitize-tool-input.ts"() {
|
|
10214
|
+
init_coerce();
|
|
10215
|
+
}
|
|
10216
|
+
});
|
|
10099
10217
|
|
|
10100
10218
|
// src/internal/llm/hermes-tool-extract.ts
|
|
10101
10219
|
function extractHermesToolCalls(content, makeId) {
|
|
@@ -10121,11 +10239,12 @@ function parseHermesParams(inner) {
|
|
|
10121
10239
|
if (key === void 0 || value === void 0) continue;
|
|
10122
10240
|
input[key.trim()] = value;
|
|
10123
10241
|
}
|
|
10124
|
-
return input;
|
|
10242
|
+
return sanitizeToolInput(input, { trim: true }).value;
|
|
10125
10243
|
}
|
|
10126
10244
|
var HERMES_BLOCK, HERMES_PARAM;
|
|
10127
10245
|
var init_hermes_tool_extract = __esm({
|
|
10128
10246
|
"src/internal/llm/hermes-tool-extract.ts"() {
|
|
10247
|
+
init_sanitize_tool_input();
|
|
10129
10248
|
HERMES_BLOCK = /<function=\s*([^>\s]+)\s*>([\s\S]*?)<\/tool_call>/g;
|
|
10130
10249
|
HERMES_PARAM = /<parameter=\s*([^>\s]+)\s*>([\s\S]*?)<\/parameter>/g;
|
|
10131
10250
|
}
|