@ziggs-ai/agent-sdk 0.10.2 → 0.10.4

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.
@@ -53,6 +53,25 @@ function parametersToZodShape(parameters) {
53
53
  }
54
54
  return shape;
55
55
  }
56
+ /**
57
+ * ZIG-1215 — the Claude brain's args are validated here, before `eff` hands
58
+ * them to the shared executor, so a plain shape means an unknown param is
59
+ * stripped at this layer and ToolManager's own check never sees it. Closing
60
+ * the object is what makes the rejection reach the brain: the call fails with
61
+ * the offending key named, instead of succeeding on a request it silently
62
+ * altered. The published schema also carries `additionalProperties: false`,
63
+ * so the typo is usually caught before the call is made at all.
64
+ */
65
+ function strictParams(toolName, shape) {
66
+ const accepted = Object.keys(shape).join(', ') || '(none)';
67
+ return z.strictObject(shape, {
68
+ error: (issue) => issue.code === 'unrecognized_keys'
69
+ ? `Unknown parameter(s) ${issue.keys.map((k) => `"${k}"`).join(', ')} — ` +
70
+ `${toolName} did not run. Accepted params: ${accepted}. ` +
71
+ `Check for a typo and call again; nothing was changed.`
72
+ : undefined,
73
+ });
74
+ }
56
75
  function toCallToolResult(value) {
57
76
  const text = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
58
77
  return { content: [{ type: 'text', text }] };
@@ -71,9 +90,10 @@ export function buildSdkToolDefs(toolDefs, getContext) {
71
90
  const name = String(fn['name'] ?? '');
72
91
  const description = String(fn['description'] ?? name);
73
92
  const shape = parametersToZodShape(fn['parameters']);
74
- const inputSchema = Object.keys(shape).length > 0
75
- ? shape
76
- : { _: z.record(z.string(), z.unknown()).optional() };
93
+ // A zero-param tool keeps the `_` envelope the Claude SDK needs, but the
94
+ // envelope itself is closed too — its free-form payload is still checked
95
+ // against the tool's own schema downstream in ToolManager.
96
+ const inputSchema = strictParams(name, Object.keys(shape).length > 0 ? shape : { _: z.record(z.string(), z.unknown()).optional() });
77
97
  return {
78
98
  name,
79
99
  description,
@@ -9,6 +9,25 @@ function makeError(message, type, extra) {
9
9
  Object.assign(err, extra);
10
10
  return err;
11
11
  }
12
+ /**
13
+ * ZIG-1215 — Ajv's own wording for a rejected unknown param is "must NOT have
14
+ * additional properties", which names neither the offending key nor the ones
15
+ * that would have worked. An agent reading that has nothing to correct against,
16
+ * so the typo is named alongside the menu it should have come from.
17
+ */
18
+ function describeErrors(parameters, errors) {
19
+ const unknown = (errors ?? [])
20
+ .map((e) => e)
21
+ .filter((e) => e.keyword === 'additionalProperties')
22
+ .map((e) => e.params?.additionalProperty)
23
+ .filter((k) => typeof k === 'string');
24
+ if (unknown.length === 0)
25
+ return ajv.errorsText(errors);
26
+ const props = parameters.properties ?? {};
27
+ const accepted = Object.keys(props).join(', ') || '(none)';
28
+ return (`unknown parameter(s) ${unknown.map((k) => `"${k}"`).join(', ')} — ` +
29
+ `accepted params: ${accepted}. Check for a typo and call again; nothing was changed.`);
30
+ }
12
31
  function stripNullish(args) {
13
32
  if (!args || typeof args !== 'object' || Array.isArray(args))
14
33
  return args;
@@ -68,7 +87,7 @@ export class ToolManager extends ToolProvider {
68
87
  const validate = ajv.compile(parameters);
69
88
  if (!validate(args)) {
70
89
  const name = tool.schema.function?.['name'];
71
- throw makeError(`Invalid args for "${name}": ${ajv.errorsText(validate.errors)}`, 'validation_error', { data: { errors: validate.errors } });
90
+ throw makeError(`Invalid args for "${name}": ${describeErrors(parameters, validate.errors)}`, 'validation_error', { data: { errors: validate.errors } });
72
91
  }
73
92
  }
74
93
  }
@@ -48,7 +48,14 @@ export function defineTool(name, params, handler, options = {}) {
48
48
  throw new Error('defineTool: name is required');
49
49
  if (!handler || typeof handler !== 'function')
50
50
  throw new Error('defineTool: handler function is required');
51
- const parameters = convertObject(params || {});
51
+ // ZIG-1215 — the top level is closed so a misspelled param is rejected by
52
+ // ToolManager's Ajv pass instead of being dropped on the floor. JSON Schema
53
+ // allows unknown keys by default, so without this the validation ran and
54
+ // passed while the handler quietly ignored what the model asked for.
55
+ // Nested objects stay open: the typo class this guards against is at the
56
+ // call surface the model writes, and closing nested payloads (task result,
57
+ // plan) would reject shapes the backend accepts.
58
+ const parameters = { ...convertObject(params || {}), additionalProperties: false };
52
59
  const fn = { name, parameters };
53
60
  if (options.description)
54
61
  fn['description'] = options.description;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/agent-sdk",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "description": "Agent framework SDK for building autonomous agents on the Ziggs platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,7 +26,7 @@
26
26
  "@anthropic-ai/claude-agent-sdk": "^0.3.204",
27
27
  "@anthropic-ai/sdk": "^0.110.0",
28
28
  "@modelcontextprotocol/sdk": "^1.29.0",
29
- "@ziggs-ai/api-client": "^0.9.1",
29
+ "@ziggs-ai/api-client": "^0.9.3",
30
30
  "ajv": "^8.17.1",
31
31
  "dotenv": "^17.2.3",
32
32
  "openai": "^4.0.0",