tool-wrap 0.1.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/README.md +147 -0
- package/dist/index.cjs +202 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +59 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +172 -0
- package/dist/index.js.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# tool-wrap
|
|
2
|
+
|
|
3
|
+
Runtime repair layer for Anthropic tool-calling failures.
|
|
4
|
+
|
|
5
|
+
LLM tool-call failures are overwhelmingly a harness problem, not a model capability problem. This library sits between the model's raw output and your tool implementation: it validates input with Zod, attempts a small set of well-known shape repairs, and returns a model-readable error string when repair is not possible.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install tool-wrap
|
|
11
|
+
# peer dependency
|
|
12
|
+
npm install zod
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import Anthropic from "@anthropic-ai/sdk"
|
|
19
|
+
import { z } from "zod"
|
|
20
|
+
import { wrap, pathString } from "tool-wrap"
|
|
21
|
+
|
|
22
|
+
// Default: Anthropic format
|
|
23
|
+
const readFile = wrap({
|
|
24
|
+
name: "read_file",
|
|
25
|
+
description: "Read the contents of a file",
|
|
26
|
+
schema: z.object({
|
|
27
|
+
path: pathString(), // unwraps markdown auto-links
|
|
28
|
+
limit: z.number().optional(),
|
|
29
|
+
offset: z.number().optional(),
|
|
30
|
+
}),
|
|
31
|
+
execute: async ({ path, limit, offset }) => {
|
|
32
|
+
// your implementation
|
|
33
|
+
},
|
|
34
|
+
relationalDefaults: [
|
|
35
|
+
{ ifPresent: "limit", thenDefault: { offset: 0 } },
|
|
36
|
+
],
|
|
37
|
+
logger: console,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// OpenAI / DeepSeek format — pass openaiAdapter
|
|
41
|
+
import { openaiAdapter } from "tool-wrap"
|
|
42
|
+
import OpenAI from "openai"
|
|
43
|
+
|
|
44
|
+
const readFileOAI = wrap({
|
|
45
|
+
name: "read_file",
|
|
46
|
+
description: "Read the contents of a file",
|
|
47
|
+
schema: z.object({ path: pathString(), limit: z.number().optional() }),
|
|
48
|
+
execute: async ({ path, limit }) => { /* your implementation */ },
|
|
49
|
+
adapter: openaiAdapter,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// Pass definition to Anthropic
|
|
53
|
+
const client = new Anthropic()
|
|
54
|
+
const response = await client.messages.create({
|
|
55
|
+
model: "claude-opus-4-7",
|
|
56
|
+
tools: [readFile.definition], // Anthropic.Tool shape
|
|
57
|
+
messages: [...],
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Or pass to DeepSeek / OpenAI
|
|
61
|
+
const deepseek = new OpenAI({ baseURL: "https://api.deepseek.com", apiKey: "..." })
|
|
62
|
+
const response2 = await deepseek.chat.completions.create({
|
|
63
|
+
model: "deepseek-chat",
|
|
64
|
+
tools: [readFileOAI.definition], // OpenAI ChatCompletionTool shape
|
|
65
|
+
messages: [...],
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// Handle tool_use blocks
|
|
69
|
+
for (const block of response.content) {
|
|
70
|
+
if (block.type === "tool_use" && block.name === "read_file") {
|
|
71
|
+
const result = await readFile.execute(block.input)
|
|
72
|
+
// result is your tool's return value, or a model-readable error string
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## What it repairs
|
|
78
|
+
|
|
79
|
+
Four shape errors cover ~90% of tool-call failures across open models:
|
|
80
|
+
|
|
81
|
+
| # | Failure | Example | Repair |
|
|
82
|
+
|---|---------|---------|--------|
|
|
83
|
+
| 2 | JSON array emitted as string | `"[\"a\",\"b\"]"` | parse stringified JSON |
|
|
84
|
+
| 4 | Bare string where array expected | `"foo"` | wrap to `["foo"]` |
|
|
85
|
+
| 1 | `null` for optional field | `{ limit: null }` | drop the key |
|
|
86
|
+
| 3 | Single object where array expected | `{ id: 1 }` | wrap to `[{ id: 1 }]` |
|
|
87
|
+
|
|
88
|
+
Repairs run in the order shown (#2 before #4 so stringified arrays are parsed, not re-wrapped). Valid inputs are never touched.
|
|
89
|
+
|
|
90
|
+
## API
|
|
91
|
+
|
|
92
|
+
### `wrap(options)`
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
wrap({
|
|
96
|
+
name: string
|
|
97
|
+
description: string
|
|
98
|
+
schema: ZodObject // Zod schema; input_schema is derived from this
|
|
99
|
+
execute: (input) => Promise<unknown>
|
|
100
|
+
relationalDefaults?: Array<{ ifPresent: string; thenDefault: Record<string, unknown> }>
|
|
101
|
+
logger?: { info(...): void; warn(...): void } // optional; pino, winston, console, etc.
|
|
102
|
+
adapter?: ToolAdapter<TDef> // optional; defaults to anthropicAdapter
|
|
103
|
+
}): {
|
|
104
|
+
definition: TDef // shape determined by adapter (Anthropic.Tool by default)
|
|
105
|
+
execute: (rawInput: unknown) => Promise<unknown>
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Two built-in adapters are exported:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { anthropicAdapter, openaiAdapter } from "tool-wrap"
|
|
113
|
+
// anthropicAdapter → { name, description, input_schema }
|
|
114
|
+
// openaiAdapter → { type: "function", function: { name, description, parameters } }
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Custom adapters implement `ToolAdapter<TDef>`:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import type { ToolAdapter, BaseDefinition } from "tool-wrap"
|
|
121
|
+
|
|
122
|
+
const myAdapter: ToolAdapter<MyProviderTool> = (base: BaseDefinition) => ({
|
|
123
|
+
// map base.name, base.description, base.inputSchema to your provider's format
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
On unrecoverable failure, `execute` returns a model-readable string (no `Error:` prefix) so the model can self-correct and retry.
|
|
128
|
+
|
|
129
|
+
### `pathString()`
|
|
130
|
+
|
|
131
|
+
A Zod type that unwraps markdown auto-links before validation:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
"[notes.md](http://notes.md)" → "notes.md"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Use it for any field that accepts a file path.
|
|
138
|
+
|
|
139
|
+
## Logging
|
|
140
|
+
|
|
141
|
+
Pass any logger with `.info()` and `.warn()` methods. Two events are emitted:
|
|
142
|
+
|
|
143
|
+
| Event | Level | When |
|
|
144
|
+
|-------|-------|------|
|
|
145
|
+
| `tool_input_repaired` | info | repair succeeded; includes `{ tool, repairs }` |
|
|
146
|
+
| `tool_input_invalid` | warn | unrecoverable; includes `{ tool, issues }` |
|
|
147
|
+
| `tool_relational_defaults_applied` | info | defaults were injected; includes `{ tool, defaults }` |
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
anthropicAdapter: () => anthropicAdapter,
|
|
24
|
+
openaiAdapter: () => openaiAdapter,
|
|
25
|
+
pathString: () => pathString,
|
|
26
|
+
wrap: () => wrap
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/wrap.ts
|
|
31
|
+
var import_zod_to_json_schema = require("zod-to-json-schema");
|
|
32
|
+
|
|
33
|
+
// src/repair.ts
|
|
34
|
+
function getAtPath(obj, path) {
|
|
35
|
+
let cur = obj;
|
|
36
|
+
for (const key of path) {
|
|
37
|
+
if (cur == null || typeof cur !== "object") return void 0;
|
|
38
|
+
cur = cur[key];
|
|
39
|
+
}
|
|
40
|
+
return cur;
|
|
41
|
+
}
|
|
42
|
+
function setAtPath(obj, path, value) {
|
|
43
|
+
let cur = obj;
|
|
44
|
+
for (const key of path.slice(0, -1)) {
|
|
45
|
+
if (cur == null || typeof cur !== "object") return;
|
|
46
|
+
cur = cur[key];
|
|
47
|
+
}
|
|
48
|
+
const last = path[path.length - 1];
|
|
49
|
+
if (cur != null && typeof cur === "object") {
|
|
50
|
+
;
|
|
51
|
+
cur[last] = value;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function deleteAtPath(obj, path) {
|
|
55
|
+
let cur = obj;
|
|
56
|
+
for (const key of path.slice(0, -1)) {
|
|
57
|
+
if (cur == null || typeof cur !== "object") return;
|
|
58
|
+
cur = cur[key];
|
|
59
|
+
}
|
|
60
|
+
const last = path[path.length - 1];
|
|
61
|
+
if (cur != null && typeof cur === "object") {
|
|
62
|
+
delete cur[last];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function isOptionalField(schema, path) {
|
|
66
|
+
if (path.length !== 1 || typeof path[0] !== "string") return false;
|
|
67
|
+
return schema.shape[path[0]]?.isOptional() ?? false;
|
|
68
|
+
}
|
|
69
|
+
function repair(args, error, schema) {
|
|
70
|
+
const repaired = structuredClone(args);
|
|
71
|
+
const repairs = [];
|
|
72
|
+
for (const issue of error.issues) {
|
|
73
|
+
if (issue.path.length === 0) continue;
|
|
74
|
+
const value = getAtPath(repaired, issue.path);
|
|
75
|
+
if (issue.code === "invalid_type" && issue.expected === "array" && typeof value === "string") {
|
|
76
|
+
try {
|
|
77
|
+
const parsed = JSON.parse(value);
|
|
78
|
+
if (Array.isArray(parsed)) {
|
|
79
|
+
setAtPath(repaired, issue.path, parsed);
|
|
80
|
+
repairs.push("stringify_parse");
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (issue.code === "invalid_type" && issue.expected === "array" && typeof value === "string") {
|
|
87
|
+
setAtPath(repaired, issue.path, [value]);
|
|
88
|
+
repairs.push("string_to_array");
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (value === null && isOptionalField(schema, issue.path)) {
|
|
92
|
+
deleteAtPath(repaired, issue.path);
|
|
93
|
+
repairs.push("null_drop");
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (issue.code === "invalid_type" && issue.expected === "array" && value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
97
|
+
setAtPath(repaired, issue.path, [value]);
|
|
98
|
+
repairs.push("object_to_array");
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { repaired, repairs };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/relational.ts
|
|
106
|
+
function applyRelationalDefaults(args, defaults) {
|
|
107
|
+
const result = { ...args };
|
|
108
|
+
const applied = [];
|
|
109
|
+
for (const def of defaults) {
|
|
110
|
+
if (result[def.ifPresent] !== void 0) {
|
|
111
|
+
let anyApplied = false;
|
|
112
|
+
for (const [key, value] of Object.entries(def.thenDefault)) {
|
|
113
|
+
if (result[key] === void 0) {
|
|
114
|
+
result[key] = value;
|
|
115
|
+
anyApplied = true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (anyApplied) applied.push(def);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return { result, applied };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/adapters.ts
|
|
125
|
+
var anthropicAdapter = (base) => ({
|
|
126
|
+
name: base.name,
|
|
127
|
+
description: base.description,
|
|
128
|
+
input_schema: base.inputSchema
|
|
129
|
+
});
|
|
130
|
+
var openaiAdapter = (base) => ({
|
|
131
|
+
type: "function",
|
|
132
|
+
function: {
|
|
133
|
+
name: base.name,
|
|
134
|
+
description: base.description,
|
|
135
|
+
parameters: base.inputSchema
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// src/wrap.ts
|
|
140
|
+
function wrap(options) {
|
|
141
|
+
const { name, description, schema, execute, relationalDefaults = [], logger, adapter } = options;
|
|
142
|
+
const { $schema: _$schema, ...inputSchema } = (0, import_zod_to_json_schema.zodToJsonSchema)(schema);
|
|
143
|
+
const base = { name, description, inputSchema };
|
|
144
|
+
const resolvedAdapter = adapter ?? anthropicAdapter;
|
|
145
|
+
const definition = resolvedAdapter(base);
|
|
146
|
+
async function executeWrapped(rawInput) {
|
|
147
|
+
if (rawInput == null || typeof rawInput !== "object" || Array.isArray(rawInput)) {
|
|
148
|
+
return `Input for "${name}" must be an object. Received: ${typeof rawInput}. Retry with a valid JSON object.`;
|
|
149
|
+
}
|
|
150
|
+
const args = rawInput;
|
|
151
|
+
const { result: withDefaults, applied } = applyRelationalDefaults(args, relationalDefaults);
|
|
152
|
+
if (applied.length > 0) {
|
|
153
|
+
logger?.info("tool_relational_defaults_applied", {
|
|
154
|
+
tool: name,
|
|
155
|
+
defaults: applied.map((d) => ({ ifPresent: d.ifPresent, thenDefault: d.thenDefault }))
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const first = schema.safeParse(withDefaults);
|
|
159
|
+
if (first.success) {
|
|
160
|
+
return execute(first.data);
|
|
161
|
+
}
|
|
162
|
+
const { repaired, repairs } = repair(withDefaults, first.error, schema);
|
|
163
|
+
const second = schema.safeParse(repaired);
|
|
164
|
+
if (second.success) {
|
|
165
|
+
logger?.info("tool_input_repaired", { tool: name, repairs });
|
|
166
|
+
return execute(second.data);
|
|
167
|
+
}
|
|
168
|
+
logger?.warn("tool_input_invalid", {
|
|
169
|
+
tool: name,
|
|
170
|
+
issues: second.error.issues.map((i) => ({ path: i.path, message: i.message }))
|
|
171
|
+
});
|
|
172
|
+
return formatFailureMessage(name, second.error);
|
|
173
|
+
}
|
|
174
|
+
return { definition, execute: executeWrapped };
|
|
175
|
+
}
|
|
176
|
+
function formatFailureMessage(tool, error) {
|
|
177
|
+
const lines = error.issues.map((i) => {
|
|
178
|
+
const path = i.path.length > 0 ? i.path.join(".") : "(root)";
|
|
179
|
+
return ` - ${path}: ${i.message}`;
|
|
180
|
+
});
|
|
181
|
+
return [
|
|
182
|
+
`Input for "${tool}" could not be parsed after repair attempts.`,
|
|
183
|
+
`Issues:`,
|
|
184
|
+
...lines,
|
|
185
|
+
`Retry with corrected input.`
|
|
186
|
+
].join("\n");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/types.ts
|
|
190
|
+
var import_zod = require("zod");
|
|
191
|
+
var pathString = () => import_zod.z.string().transform((val) => {
|
|
192
|
+
const match = val.match(/^\[.*?\]\((.*?)\)$/);
|
|
193
|
+
return match ? match[1] : val;
|
|
194
|
+
});
|
|
195
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
196
|
+
0 && (module.exports = {
|
|
197
|
+
anthropicAdapter,
|
|
198
|
+
openaiAdapter,
|
|
199
|
+
pathString,
|
|
200
|
+
wrap
|
|
201
|
+
});
|
|
202
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/wrap.ts","../src/repair.ts","../src/relational.ts","../src/adapters.ts","../src/types.ts"],"sourcesContent":["export { wrap } from \"./wrap\"\r\nexport { pathString } from \"./types\"\r\nexport { anthropicAdapter, openaiAdapter } from \"./adapters\"\r\nexport type { WrapOptions, WrappedTool } from \"./wrap\"\r\nexport type { RelationalDefault, Logger, BaseDefinition, ToolAdapter } from \"./types\"\r\nexport type { AnthropicToolDefinition, OpenAIToolDefinition } from \"./adapters\"\r\n","import { z, ZodObject, ZodRawShape } from \"zod\"\r\nimport { zodToJsonSchema } from \"zod-to-json-schema\"\r\nimport { repair } from \"./repair\"\r\nimport { applyRelationalDefaults } from \"./relational\"\r\nimport type { RelationalDefault, Logger, BaseDefinition, ToolAdapter } from \"./types\"\r\nimport { anthropicAdapter, type AnthropicToolDefinition } from \"./adapters\"\r\n\r\nexport interface WrapOptions<S extends ZodObject<ZodRawShape>> {\r\n name: string\r\n description: string\r\n schema: S\r\n execute: (input: z.infer<S>) => Promise<unknown>\r\n relationalDefaults?: RelationalDefault[]\r\n logger?: Logger\r\n}\r\n\r\nexport interface WrappedTool<TDef> {\r\n definition: TDef\r\n execute: (rawInput: unknown) => Promise<unknown>\r\n}\r\n\r\nexport function wrap<S extends ZodObject<ZodRawShape>>(\r\n options: WrapOptions<S> & { adapter?: undefined }\r\n): WrappedTool<AnthropicToolDefinition>\r\n\r\nexport function wrap<S extends ZodObject<ZodRawShape>, TDef>(\r\n options: WrapOptions<S> & { adapter: ToolAdapter<TDef> }\r\n): WrappedTool<TDef>\r\n\r\nexport function wrap<S extends ZodObject<ZodRawShape>, TDef = AnthropicToolDefinition>(\r\n options: WrapOptions<S> & { adapter?: ToolAdapter<TDef> }\r\n): WrappedTool<TDef> {\r\n const { name, description, schema, execute, relationalDefaults = [], logger, adapter } = options\r\n\r\n const { $schema: _$schema, ...inputSchema } = zodToJsonSchema(schema) as Record<string, unknown>\r\n\r\n const base: BaseDefinition = { name, description, inputSchema }\r\n const resolvedAdapter = (adapter ?? anthropicAdapter) as ToolAdapter<TDef>\r\n const definition = resolvedAdapter(base)\r\n\r\n async function executeWrapped(rawInput: unknown): Promise<unknown> {\r\n if (rawInput == null || typeof rawInput !== \"object\" || Array.isArray(rawInput)) {\r\n return `Input for \"${name}\" must be an object. Received: ${typeof rawInput}. Retry with a valid JSON object.`\r\n }\r\n\r\n const args = rawInput as Record<string, unknown>\r\n const { result: withDefaults, applied } = applyRelationalDefaults(args, relationalDefaults)\r\n\r\n if (applied.length > 0) {\r\n logger?.info(\"tool_relational_defaults_applied\", {\r\n tool: name,\r\n defaults: applied.map((d) => ({ ifPresent: d.ifPresent, thenDefault: d.thenDefault })),\r\n })\r\n }\r\n\r\n const first = schema.safeParse(withDefaults)\r\n if (first.success) {\r\n return execute(first.data)\r\n }\r\n\r\n const { repaired, repairs } = repair(withDefaults, first.error, schema)\r\n const second = schema.safeParse(repaired)\r\n\r\n if (second.success) {\r\n logger?.info(\"tool_input_repaired\", { tool: name, repairs })\r\n return execute(second.data)\r\n }\r\n\r\n logger?.warn(\"tool_input_invalid\", {\r\n tool: name,\r\n issues: second.error.issues.map((i) => ({ path: i.path, message: i.message })),\r\n })\r\n\r\n return formatFailureMessage(name, second.error)\r\n }\r\n\r\n return { definition, execute: executeWrapped }\r\n}\r\n\r\nfunction formatFailureMessage(tool: string, error: z.ZodError): string {\r\n const lines = error.issues.map((i) => {\r\n const path = i.path.length > 0 ? i.path.join(\".\") : \"(root)\"\r\n return ` - ${path}: ${i.message}`\r\n })\r\n return [\r\n `Input for \"${tool}\" could not be parsed after repair attempts.`,\r\n `Issues:`,\r\n ...lines,\r\n `Retry with corrected input.`,\r\n ].join(\"\\n\")\r\n}\r\n","import { ZodError, ZodObject, ZodRawShape } from \"zod\"\n\nexport type RepairType = \"stringify_parse\" | \"string_to_array\" | \"null_drop\" | \"object_to_array\"\n\nfunction getAtPath(obj: unknown, path: (string | number)[]): unknown {\n let cur: unknown = obj\n for (const key of path) {\n if (cur == null || typeof cur !== \"object\") return undefined\n cur = (cur as Record<string | number, unknown>)[key]\n }\n return cur\n}\n\nfunction setAtPath(obj: unknown, path: (string | number)[], value: unknown): void {\n let cur: unknown = obj\n for (const key of path.slice(0, -1)) {\n if (cur == null || typeof cur !== \"object\") return\n cur = (cur as Record<string | number, unknown>)[key]\n }\n const last = path[path.length - 1]\n if (cur != null && typeof cur === \"object\") {\n ;(cur as Record<string | number, unknown>)[last] = value\n }\n}\n\nfunction deleteAtPath(obj: unknown, path: (string | number)[]): void {\n let cur: unknown = obj\n for (const key of path.slice(0, -1)) {\n if (cur == null || typeof cur !== \"object\") return\n cur = (cur as Record<string | number, unknown>)[key]\n }\n const last = path[path.length - 1]\n if (cur != null && typeof cur === \"object\") {\n delete (cur as Record<string | number, unknown>)[last]\n }\n}\n\nfunction isOptionalField(schema: ZodObject<ZodRawShape>, path: (string | number)[]): boolean {\n if (path.length !== 1 || typeof path[0] !== \"string\") return false\n return schema.shape[path[0]]?.isOptional() ?? false\n}\n\nexport function repair(\n args: Record<string, unknown>,\n error: ZodError,\n schema: ZodObject<ZodRawShape>,\n): { repaired: Record<string, unknown>; repairs: RepairType[] } {\n const repaired = structuredClone(args) as Record<string, unknown>\n const repairs: RepairType[] = []\n\n for (const issue of error.issues) {\n if (issue.path.length === 0) continue\n\n const value = getAtPath(repaired, issue.path)\n\n // #2: stringified JSON array — must run before #4\n if (issue.code === \"invalid_type\" && issue.expected === \"array\" && typeof value === \"string\") {\n try {\n const parsed = JSON.parse(value)\n if (Array.isArray(parsed)) {\n setAtPath(repaired, issue.path, parsed)\n repairs.push(\"stringify_parse\")\n continue\n }\n } catch {\n // not valid JSON, fall through to #4\n }\n }\n\n // #4: bare string where array expected\n if (issue.code === \"invalid_type\" && issue.expected === \"array\" && typeof value === \"string\") {\n setAtPath(repaired, issue.path, [value])\n repairs.push(\"string_to_array\")\n continue\n }\n\n // #1: null for optional field only — dropping null from a required field just\n // changes the error from \"wrong type\" to \"missing required\", no net benefit\n if (value === null && isOptionalField(schema, issue.path)) {\n deleteAtPath(repaired, issue.path)\n repairs.push(\"null_drop\")\n continue\n }\n\n // #3: single object where array expected\n if (\n issue.code === \"invalid_type\" &&\n issue.expected === \"array\" &&\n value !== null &&\n typeof value === \"object\" &&\n !Array.isArray(value)\n ) {\n setAtPath(repaired, issue.path, [value])\n repairs.push(\"object_to_array\")\n continue\n }\n }\n\n return { repaired, repairs }\n}\n","import type { RelationalDefault } from \"./types\"\n\nexport function applyRelationalDefaults(\n args: Record<string, unknown>,\n defaults: RelationalDefault[],\n): { result: Record<string, unknown>; applied: RelationalDefault[] } {\n const result = { ...args }\n const applied: RelationalDefault[] = []\n\n for (const def of defaults) {\n if (result[def.ifPresent] !== undefined) {\n let anyApplied = false\n for (const [key, value] of Object.entries(def.thenDefault)) {\n if (result[key] === undefined) {\n result[key] = value\n anyApplied = true\n }\n }\n if (anyApplied) applied.push(def)\n }\n }\n\n return { result, applied }\n}\n","import type { BaseDefinition, ToolAdapter } from \"./types\"\r\n\r\nexport interface AnthropicToolDefinition {\r\n name: string\r\n description: string\r\n input_schema: {\r\n type: \"object\"\r\n properties?: Record<string, unknown>\r\n required?: string[]\r\n [key: string]: unknown\r\n }\r\n}\r\n\r\nexport interface OpenAIToolDefinition {\r\n type: \"function\"\r\n function: {\r\n name: string\r\n description: string\r\n parameters: Record<string, unknown>\r\n }\r\n}\r\n\r\nexport const anthropicAdapter: ToolAdapter<AnthropicToolDefinition> = (base: BaseDefinition) => ({\r\n name: base.name,\r\n description: base.description,\r\n input_schema: base.inputSchema as AnthropicToolDefinition[\"input_schema\"],\r\n})\r\n\r\nexport const openaiAdapter: ToolAdapter<OpenAIToolDefinition> = (base: BaseDefinition) => ({\r\n type: \"function\",\r\n function: {\r\n name: base.name,\r\n description: base.description,\r\n parameters: base.inputSchema,\r\n },\r\n})\r\n","import { z } from \"zod\"\r\n\r\nexport interface RelationalDefault {\r\n ifPresent: string\r\n thenDefault: Record<string, unknown>\r\n}\r\n\r\nexport interface Logger {\r\n info(msg: string, meta?: Record<string, unknown>): void\r\n warn(msg: string, meta?: Record<string, unknown>): void\r\n}\r\n\r\nexport interface BaseDefinition {\r\n name: string\r\n description: string\r\n inputSchema: Record<string, unknown>\r\n}\r\n\r\nexport type ToolAdapter<TDef> = (base: BaseDefinition) => TDef\r\n\r\n// Unwraps markdown auto-links that bleed through from chat prior:\r\n// \"[notes.md](http://notes.md)\" → \"notes.md\"\r\nexport const pathString = () =>\r\n z.string().transform((val) => {\r\n const match = val.match(/^\\[.*?\\]\\((.*?)\\)$/)\r\n return match ? match[1] : val\r\n })\r\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z, ZodObject, ZodRawShape } from 'zod';
|
|
2
|
+
|
|
3
|
+
interface RelationalDefault {
|
|
4
|
+
ifPresent: string;
|
|
5
|
+
thenDefault: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
interface Logger {
|
|
8
|
+
info(msg: string, meta?: Record<string, unknown>): void;
|
|
9
|
+
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
10
|
+
}
|
|
11
|
+
interface BaseDefinition {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputSchema: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
type ToolAdapter<TDef> = (base: BaseDefinition) => TDef;
|
|
17
|
+
declare const pathString: () => z.ZodEffects<z.ZodString, string, string>;
|
|
18
|
+
|
|
19
|
+
interface AnthropicToolDefinition {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
input_schema: {
|
|
23
|
+
type: "object";
|
|
24
|
+
properties?: Record<string, unknown>;
|
|
25
|
+
required?: string[];
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
interface OpenAIToolDefinition {
|
|
30
|
+
type: "function";
|
|
31
|
+
function: {
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
parameters: Record<string, unknown>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
declare const anthropicAdapter: ToolAdapter<AnthropicToolDefinition>;
|
|
38
|
+
declare const openaiAdapter: ToolAdapter<OpenAIToolDefinition>;
|
|
39
|
+
|
|
40
|
+
interface WrapOptions<S extends ZodObject<ZodRawShape>> {
|
|
41
|
+
name: string;
|
|
42
|
+
description: string;
|
|
43
|
+
schema: S;
|
|
44
|
+
execute: (input: z.infer<S>) => Promise<unknown>;
|
|
45
|
+
relationalDefaults?: RelationalDefault[];
|
|
46
|
+
logger?: Logger;
|
|
47
|
+
}
|
|
48
|
+
interface WrappedTool<TDef> {
|
|
49
|
+
definition: TDef;
|
|
50
|
+
execute: (rawInput: unknown) => Promise<unknown>;
|
|
51
|
+
}
|
|
52
|
+
declare function wrap<S extends ZodObject<ZodRawShape>>(options: WrapOptions<S> & {
|
|
53
|
+
adapter?: undefined;
|
|
54
|
+
}): WrappedTool<AnthropicToolDefinition>;
|
|
55
|
+
declare function wrap<S extends ZodObject<ZodRawShape>, TDef>(options: WrapOptions<S> & {
|
|
56
|
+
adapter: ToolAdapter<TDef>;
|
|
57
|
+
}): WrappedTool<TDef>;
|
|
58
|
+
|
|
59
|
+
export { type AnthropicToolDefinition, type BaseDefinition, type Logger, type OpenAIToolDefinition, type RelationalDefault, type ToolAdapter, type WrapOptions, type WrappedTool, anthropicAdapter, openaiAdapter, pathString, wrap };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z, ZodObject, ZodRawShape } from 'zod';
|
|
2
|
+
|
|
3
|
+
interface RelationalDefault {
|
|
4
|
+
ifPresent: string;
|
|
5
|
+
thenDefault: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
interface Logger {
|
|
8
|
+
info(msg: string, meta?: Record<string, unknown>): void;
|
|
9
|
+
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
10
|
+
}
|
|
11
|
+
interface BaseDefinition {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputSchema: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
type ToolAdapter<TDef> = (base: BaseDefinition) => TDef;
|
|
17
|
+
declare const pathString: () => z.ZodEffects<z.ZodString, string, string>;
|
|
18
|
+
|
|
19
|
+
interface AnthropicToolDefinition {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
input_schema: {
|
|
23
|
+
type: "object";
|
|
24
|
+
properties?: Record<string, unknown>;
|
|
25
|
+
required?: string[];
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
interface OpenAIToolDefinition {
|
|
30
|
+
type: "function";
|
|
31
|
+
function: {
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
parameters: Record<string, unknown>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
declare const anthropicAdapter: ToolAdapter<AnthropicToolDefinition>;
|
|
38
|
+
declare const openaiAdapter: ToolAdapter<OpenAIToolDefinition>;
|
|
39
|
+
|
|
40
|
+
interface WrapOptions<S extends ZodObject<ZodRawShape>> {
|
|
41
|
+
name: string;
|
|
42
|
+
description: string;
|
|
43
|
+
schema: S;
|
|
44
|
+
execute: (input: z.infer<S>) => Promise<unknown>;
|
|
45
|
+
relationalDefaults?: RelationalDefault[];
|
|
46
|
+
logger?: Logger;
|
|
47
|
+
}
|
|
48
|
+
interface WrappedTool<TDef> {
|
|
49
|
+
definition: TDef;
|
|
50
|
+
execute: (rawInput: unknown) => Promise<unknown>;
|
|
51
|
+
}
|
|
52
|
+
declare function wrap<S extends ZodObject<ZodRawShape>>(options: WrapOptions<S> & {
|
|
53
|
+
adapter?: undefined;
|
|
54
|
+
}): WrappedTool<AnthropicToolDefinition>;
|
|
55
|
+
declare function wrap<S extends ZodObject<ZodRawShape>, TDef>(options: WrapOptions<S> & {
|
|
56
|
+
adapter: ToolAdapter<TDef>;
|
|
57
|
+
}): WrappedTool<TDef>;
|
|
58
|
+
|
|
59
|
+
export { type AnthropicToolDefinition, type BaseDefinition, type Logger, type OpenAIToolDefinition, type RelationalDefault, type ToolAdapter, type WrapOptions, type WrappedTool, anthropicAdapter, openaiAdapter, pathString, wrap };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// src/wrap.ts
|
|
2
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
3
|
+
|
|
4
|
+
// src/repair.ts
|
|
5
|
+
function getAtPath(obj, path) {
|
|
6
|
+
let cur = obj;
|
|
7
|
+
for (const key of path) {
|
|
8
|
+
if (cur == null || typeof cur !== "object") return void 0;
|
|
9
|
+
cur = cur[key];
|
|
10
|
+
}
|
|
11
|
+
return cur;
|
|
12
|
+
}
|
|
13
|
+
function setAtPath(obj, path, value) {
|
|
14
|
+
let cur = obj;
|
|
15
|
+
for (const key of path.slice(0, -1)) {
|
|
16
|
+
if (cur == null || typeof cur !== "object") return;
|
|
17
|
+
cur = cur[key];
|
|
18
|
+
}
|
|
19
|
+
const last = path[path.length - 1];
|
|
20
|
+
if (cur != null && typeof cur === "object") {
|
|
21
|
+
;
|
|
22
|
+
cur[last] = value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function deleteAtPath(obj, path) {
|
|
26
|
+
let cur = obj;
|
|
27
|
+
for (const key of path.slice(0, -1)) {
|
|
28
|
+
if (cur == null || typeof cur !== "object") return;
|
|
29
|
+
cur = cur[key];
|
|
30
|
+
}
|
|
31
|
+
const last = path[path.length - 1];
|
|
32
|
+
if (cur != null && typeof cur === "object") {
|
|
33
|
+
delete cur[last];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function isOptionalField(schema, path) {
|
|
37
|
+
if (path.length !== 1 || typeof path[0] !== "string") return false;
|
|
38
|
+
return schema.shape[path[0]]?.isOptional() ?? false;
|
|
39
|
+
}
|
|
40
|
+
function repair(args, error, schema) {
|
|
41
|
+
const repaired = structuredClone(args);
|
|
42
|
+
const repairs = [];
|
|
43
|
+
for (const issue of error.issues) {
|
|
44
|
+
if (issue.path.length === 0) continue;
|
|
45
|
+
const value = getAtPath(repaired, issue.path);
|
|
46
|
+
if (issue.code === "invalid_type" && issue.expected === "array" && typeof value === "string") {
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(value);
|
|
49
|
+
if (Array.isArray(parsed)) {
|
|
50
|
+
setAtPath(repaired, issue.path, parsed);
|
|
51
|
+
repairs.push("stringify_parse");
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (issue.code === "invalid_type" && issue.expected === "array" && typeof value === "string") {
|
|
58
|
+
setAtPath(repaired, issue.path, [value]);
|
|
59
|
+
repairs.push("string_to_array");
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (value === null && isOptionalField(schema, issue.path)) {
|
|
63
|
+
deleteAtPath(repaired, issue.path);
|
|
64
|
+
repairs.push("null_drop");
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (issue.code === "invalid_type" && issue.expected === "array" && value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
68
|
+
setAtPath(repaired, issue.path, [value]);
|
|
69
|
+
repairs.push("object_to_array");
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { repaired, repairs };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/relational.ts
|
|
77
|
+
function applyRelationalDefaults(args, defaults) {
|
|
78
|
+
const result = { ...args };
|
|
79
|
+
const applied = [];
|
|
80
|
+
for (const def of defaults) {
|
|
81
|
+
if (result[def.ifPresent] !== void 0) {
|
|
82
|
+
let anyApplied = false;
|
|
83
|
+
for (const [key, value] of Object.entries(def.thenDefault)) {
|
|
84
|
+
if (result[key] === void 0) {
|
|
85
|
+
result[key] = value;
|
|
86
|
+
anyApplied = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (anyApplied) applied.push(def);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { result, applied };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/adapters.ts
|
|
96
|
+
var anthropicAdapter = (base) => ({
|
|
97
|
+
name: base.name,
|
|
98
|
+
description: base.description,
|
|
99
|
+
input_schema: base.inputSchema
|
|
100
|
+
});
|
|
101
|
+
var openaiAdapter = (base) => ({
|
|
102
|
+
type: "function",
|
|
103
|
+
function: {
|
|
104
|
+
name: base.name,
|
|
105
|
+
description: base.description,
|
|
106
|
+
parameters: base.inputSchema
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// src/wrap.ts
|
|
111
|
+
function wrap(options) {
|
|
112
|
+
const { name, description, schema, execute, relationalDefaults = [], logger, adapter } = options;
|
|
113
|
+
const { $schema: _$schema, ...inputSchema } = zodToJsonSchema(schema);
|
|
114
|
+
const base = { name, description, inputSchema };
|
|
115
|
+
const resolvedAdapter = adapter ?? anthropicAdapter;
|
|
116
|
+
const definition = resolvedAdapter(base);
|
|
117
|
+
async function executeWrapped(rawInput) {
|
|
118
|
+
if (rawInput == null || typeof rawInput !== "object" || Array.isArray(rawInput)) {
|
|
119
|
+
return `Input for "${name}" must be an object. Received: ${typeof rawInput}. Retry with a valid JSON object.`;
|
|
120
|
+
}
|
|
121
|
+
const args = rawInput;
|
|
122
|
+
const { result: withDefaults, applied } = applyRelationalDefaults(args, relationalDefaults);
|
|
123
|
+
if (applied.length > 0) {
|
|
124
|
+
logger?.info("tool_relational_defaults_applied", {
|
|
125
|
+
tool: name,
|
|
126
|
+
defaults: applied.map((d) => ({ ifPresent: d.ifPresent, thenDefault: d.thenDefault }))
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const first = schema.safeParse(withDefaults);
|
|
130
|
+
if (first.success) {
|
|
131
|
+
return execute(first.data);
|
|
132
|
+
}
|
|
133
|
+
const { repaired, repairs } = repair(withDefaults, first.error, schema);
|
|
134
|
+
const second = schema.safeParse(repaired);
|
|
135
|
+
if (second.success) {
|
|
136
|
+
logger?.info("tool_input_repaired", { tool: name, repairs });
|
|
137
|
+
return execute(second.data);
|
|
138
|
+
}
|
|
139
|
+
logger?.warn("tool_input_invalid", {
|
|
140
|
+
tool: name,
|
|
141
|
+
issues: second.error.issues.map((i) => ({ path: i.path, message: i.message }))
|
|
142
|
+
});
|
|
143
|
+
return formatFailureMessage(name, second.error);
|
|
144
|
+
}
|
|
145
|
+
return { definition, execute: executeWrapped };
|
|
146
|
+
}
|
|
147
|
+
function formatFailureMessage(tool, error) {
|
|
148
|
+
const lines = error.issues.map((i) => {
|
|
149
|
+
const path = i.path.length > 0 ? i.path.join(".") : "(root)";
|
|
150
|
+
return ` - ${path}: ${i.message}`;
|
|
151
|
+
});
|
|
152
|
+
return [
|
|
153
|
+
`Input for "${tool}" could not be parsed after repair attempts.`,
|
|
154
|
+
`Issues:`,
|
|
155
|
+
...lines,
|
|
156
|
+
`Retry with corrected input.`
|
|
157
|
+
].join("\n");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/types.ts
|
|
161
|
+
import { z } from "zod";
|
|
162
|
+
var pathString = () => z.string().transform((val) => {
|
|
163
|
+
const match = val.match(/^\[.*?\]\((.*?)\)$/);
|
|
164
|
+
return match ? match[1] : val;
|
|
165
|
+
});
|
|
166
|
+
export {
|
|
167
|
+
anthropicAdapter,
|
|
168
|
+
openaiAdapter,
|
|
169
|
+
pathString,
|
|
170
|
+
wrap
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/wrap.ts","../src/repair.ts","../src/relational.ts","../src/adapters.ts","../src/types.ts"],"sourcesContent":["import { z, ZodObject, ZodRawShape } from \"zod\"\r\nimport { zodToJsonSchema } from \"zod-to-json-schema\"\r\nimport { repair } from \"./repair\"\r\nimport { applyRelationalDefaults } from \"./relational\"\r\nimport type { RelationalDefault, Logger, BaseDefinition, ToolAdapter } from \"./types\"\r\nimport { anthropicAdapter, type AnthropicToolDefinition } from \"./adapters\"\r\n\r\nexport interface WrapOptions<S extends ZodObject<ZodRawShape>> {\r\n name: string\r\n description: string\r\n schema: S\r\n execute: (input: z.infer<S>) => Promise<unknown>\r\n relationalDefaults?: RelationalDefault[]\r\n logger?: Logger\r\n}\r\n\r\nexport interface WrappedTool<TDef> {\r\n definition: TDef\r\n execute: (rawInput: unknown) => Promise<unknown>\r\n}\r\n\r\nexport function wrap<S extends ZodObject<ZodRawShape>>(\r\n options: WrapOptions<S> & { adapter?: undefined }\r\n): WrappedTool<AnthropicToolDefinition>\r\n\r\nexport function wrap<S extends ZodObject<ZodRawShape>, TDef>(\r\n options: WrapOptions<S> & { adapter: ToolAdapter<TDef> }\r\n): WrappedTool<TDef>\r\n\r\nexport function wrap<S extends ZodObject<ZodRawShape>, TDef = AnthropicToolDefinition>(\r\n options: WrapOptions<S> & { adapter?: ToolAdapter<TDef> }\r\n): WrappedTool<TDef> {\r\n const { name, description, schema, execute, relationalDefaults = [], logger, adapter } = options\r\n\r\n const { $schema: _$schema, ...inputSchema } = zodToJsonSchema(schema) as Record<string, unknown>\r\n\r\n const base: BaseDefinition = { name, description, inputSchema }\r\n const resolvedAdapter = (adapter ?? anthropicAdapter) as ToolAdapter<TDef>\r\n const definition = resolvedAdapter(base)\r\n\r\n async function executeWrapped(rawInput: unknown): Promise<unknown> {\r\n if (rawInput == null || typeof rawInput !== \"object\" || Array.isArray(rawInput)) {\r\n return `Input for \"${name}\" must be an object. Received: ${typeof rawInput}. Retry with a valid JSON object.`\r\n }\r\n\r\n const args = rawInput as Record<string, unknown>\r\n const { result: withDefaults, applied } = applyRelationalDefaults(args, relationalDefaults)\r\n\r\n if (applied.length > 0) {\r\n logger?.info(\"tool_relational_defaults_applied\", {\r\n tool: name,\r\n defaults: applied.map((d) => ({ ifPresent: d.ifPresent, thenDefault: d.thenDefault })),\r\n })\r\n }\r\n\r\n const first = schema.safeParse(withDefaults)\r\n if (first.success) {\r\n return execute(first.data)\r\n }\r\n\r\n const { repaired, repairs } = repair(withDefaults, first.error, schema)\r\n const second = schema.safeParse(repaired)\r\n\r\n if (second.success) {\r\n logger?.info(\"tool_input_repaired\", { tool: name, repairs })\r\n return execute(second.data)\r\n }\r\n\r\n logger?.warn(\"tool_input_invalid\", {\r\n tool: name,\r\n issues: second.error.issues.map((i) => ({ path: i.path, message: i.message })),\r\n })\r\n\r\n return formatFailureMessage(name, second.error)\r\n }\r\n\r\n return { definition, execute: executeWrapped }\r\n}\r\n\r\nfunction formatFailureMessage(tool: string, error: z.ZodError): string {\r\n const lines = error.issues.map((i) => {\r\n const path = i.path.length > 0 ? i.path.join(\".\") : \"(root)\"\r\n return ` - ${path}: ${i.message}`\r\n })\r\n return [\r\n `Input for \"${tool}\" could not be parsed after repair attempts.`,\r\n `Issues:`,\r\n ...lines,\r\n `Retry with corrected input.`,\r\n ].join(\"\\n\")\r\n}\r\n","import { ZodError, ZodObject, ZodRawShape } from \"zod\"\n\nexport type RepairType = \"stringify_parse\" | \"string_to_array\" | \"null_drop\" | \"object_to_array\"\n\nfunction getAtPath(obj: unknown, path: (string | number)[]): unknown {\n let cur: unknown = obj\n for (const key of path) {\n if (cur == null || typeof cur !== \"object\") return undefined\n cur = (cur as Record<string | number, unknown>)[key]\n }\n return cur\n}\n\nfunction setAtPath(obj: unknown, path: (string | number)[], value: unknown): void {\n let cur: unknown = obj\n for (const key of path.slice(0, -1)) {\n if (cur == null || typeof cur !== \"object\") return\n cur = (cur as Record<string | number, unknown>)[key]\n }\n const last = path[path.length - 1]\n if (cur != null && typeof cur === \"object\") {\n ;(cur as Record<string | number, unknown>)[last] = value\n }\n}\n\nfunction deleteAtPath(obj: unknown, path: (string | number)[]): void {\n let cur: unknown = obj\n for (const key of path.slice(0, -1)) {\n if (cur == null || typeof cur !== \"object\") return\n cur = (cur as Record<string | number, unknown>)[key]\n }\n const last = path[path.length - 1]\n if (cur != null && typeof cur === \"object\") {\n delete (cur as Record<string | number, unknown>)[last]\n }\n}\n\nfunction isOptionalField(schema: ZodObject<ZodRawShape>, path: (string | number)[]): boolean {\n if (path.length !== 1 || typeof path[0] !== \"string\") return false\n return schema.shape[path[0]]?.isOptional() ?? false\n}\n\nexport function repair(\n args: Record<string, unknown>,\n error: ZodError,\n schema: ZodObject<ZodRawShape>,\n): { repaired: Record<string, unknown>; repairs: RepairType[] } {\n const repaired = structuredClone(args) as Record<string, unknown>\n const repairs: RepairType[] = []\n\n for (const issue of error.issues) {\n if (issue.path.length === 0) continue\n\n const value = getAtPath(repaired, issue.path)\n\n // #2: stringified JSON array — must run before #4\n if (issue.code === \"invalid_type\" && issue.expected === \"array\" && typeof value === \"string\") {\n try {\n const parsed = JSON.parse(value)\n if (Array.isArray(parsed)) {\n setAtPath(repaired, issue.path, parsed)\n repairs.push(\"stringify_parse\")\n continue\n }\n } catch {\n // not valid JSON, fall through to #4\n }\n }\n\n // #4: bare string where array expected\n if (issue.code === \"invalid_type\" && issue.expected === \"array\" && typeof value === \"string\") {\n setAtPath(repaired, issue.path, [value])\n repairs.push(\"string_to_array\")\n continue\n }\n\n // #1: null for optional field only — dropping null from a required field just\n // changes the error from \"wrong type\" to \"missing required\", no net benefit\n if (value === null && isOptionalField(schema, issue.path)) {\n deleteAtPath(repaired, issue.path)\n repairs.push(\"null_drop\")\n continue\n }\n\n // #3: single object where array expected\n if (\n issue.code === \"invalid_type\" &&\n issue.expected === \"array\" &&\n value !== null &&\n typeof value === \"object\" &&\n !Array.isArray(value)\n ) {\n setAtPath(repaired, issue.path, [value])\n repairs.push(\"object_to_array\")\n continue\n }\n }\n\n return { repaired, repairs }\n}\n","import type { RelationalDefault } from \"./types\"\n\nexport function applyRelationalDefaults(\n args: Record<string, unknown>,\n defaults: RelationalDefault[],\n): { result: Record<string, unknown>; applied: RelationalDefault[] } {\n const result = { ...args }\n const applied: RelationalDefault[] = []\n\n for (const def of defaults) {\n if (result[def.ifPresent] !== undefined) {\n let anyApplied = false\n for (const [key, value] of Object.entries(def.thenDefault)) {\n if (result[key] === undefined) {\n result[key] = value\n anyApplied = true\n }\n }\n if (anyApplied) applied.push(def)\n }\n }\n\n return { result, applied }\n}\n","import type { BaseDefinition, ToolAdapter } from \"./types\"\r\n\r\nexport interface AnthropicToolDefinition {\r\n name: string\r\n description: string\r\n input_schema: {\r\n type: \"object\"\r\n properties?: Record<string, unknown>\r\n required?: string[]\r\n [key: string]: unknown\r\n }\r\n}\r\n\r\nexport interface OpenAIToolDefinition {\r\n type: \"function\"\r\n function: {\r\n name: string\r\n description: string\r\n parameters: Record<string, unknown>\r\n }\r\n}\r\n\r\nexport const anthropicAdapter: ToolAdapter<AnthropicToolDefinition> = (base: BaseDefinition) => ({\r\n name: base.name,\r\n description: base.description,\r\n input_schema: base.inputSchema as AnthropicToolDefinition[\"input_schema\"],\r\n})\r\n\r\nexport const openaiAdapter: ToolAdapter<OpenAIToolDefinition> = (base: BaseDefinition) => ({\r\n type: \"function\",\r\n function: {\r\n name: base.name,\r\n description: base.description,\r\n parameters: base.inputSchema,\r\n },\r\n})\r\n","import { z } from \"zod\"\r\n\r\nexport interface RelationalDefault {\r\n ifPresent: string\r\n thenDefault: Record<string, unknown>\r\n}\r\n\r\nexport interface Logger {\r\n info(msg: string, meta?: Record<string, unknown>): void\r\n warn(msg: string, meta?: Record<string, unknown>): void\r\n}\r\n\r\nexport interface BaseDefinition {\r\n name: string\r\n description: string\r\n inputSchema: Record<string, unknown>\r\n}\r\n\r\nexport type ToolAdapter<TDef> = (base: BaseDefinition) => TDef\r\n\r\n// Unwraps markdown auto-links that bleed through from chat prior:\r\n// \"[notes.md](http://notes.md)\" → \"notes.md\"\r\nexport const pathString = () =>\r\n z.string().transform((val) => {\r\n const match = val.match(/^\\[.*?\\]\\((.*?)\\)$/)\r\n return match ? match[1] : val\r\n })\r\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tool-wrap",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Runtime repair layer for Anthropic tool-calling failures",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/venotyh/toolharness.git"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["anthropic", "openai", "deepseek", "tool-calling", "zod", "llm"],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.cjs",
|
|
20
|
+
"module": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"files": ["dist"],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"dev": "vitest"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"zod": ">=3.0.0"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"zod-to-json-schema": "^3.23.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@anthropic-ai/sdk": "^0.52.0",
|
|
36
|
+
"tsup": "^8.0.0",
|
|
37
|
+
"typescript": "^5.0.0",
|
|
38
|
+
"vitest": "^3.0.0",
|
|
39
|
+
"zod": "^3.23.0"
|
|
40
|
+
}
|
|
41
|
+
}
|