mirror of
https://github.com/ComposioHQ/composio.git
synced 2026-09-22 11:46:35 +08:00
507318d56a
This PR (builds on https://github.com/ComposioHQ/composio/pull/3649, already merged) hardens the Anthropic tool property-key sanitizer from a multi-agent review, then extracts the mechanism into a reusable core util. **Touches two packages:** `@composio/core` (new util) and `@composio/anthropic` (consumes it). ### Correctness / robustness - **Prototype-safe restoration** — reverse maps and the rebuilt argument object are `Object.create(null)`, so a tool call using a key matching an `Object.prototype` member (`__proto__`, `constructor`, `toString`, `hasOwnProperty`, …) no longer throws a `TypeError` or silently corrupts the backend payload, and `Object.prototype` is never mutated. - **Full schema coverage** — illegal keys are sanitized wherever they appear: `allOf`/`anyOf`/`oneOf`, `not`/`if`/`then`/`else`, `prefixItems`, `additionalProperties`/`patternProperties`, `$defs`/`definitions`. Renames inside composition keywords fold into the value level they share and restore correctly (a mixed array/object `anyOf` resolves by value type at restore time). - **`$ref` keys restored** — `wrapTool` now dereferences internal `$ref`/`$defs` (lenient `sentinel` mode) before sanitizing, so keys reachable only through a reference are both made compliant and restored. - **Depth cap (512)** on sanitization and restoration — a pathologically deep schema/argument fails with a clear error instead of overflowing the stack. - **Empty / all-illegal keys** become a deterministic non-empty alias instead of `''` (which would itself violate `{1,64}`). - **Collision loop** — replaced a latent non-terminating fixed point with a counter that always makes progress. ### Architecture - **Extracted to core** — `sanitizeSchemaPropertyKeys(schema, policy)` / `restoreOriginalKeys` / `mappingHasRenames` (+ `KeyMapping` / `KeySanitizationPolicy`) now live in `@composio/core`, alongside `normalizeToolArguments`. The traversal, collision handling, prototype guard and depth cap are provider-agnostic; the Anthropic constraint is injected as a `KeySanitizationPolicy` defined in the provider's `sanitize-keys.ts`, which also re-exports a policy-bound `sanitizeSchemaPropertyKeys` plus core's restore side so call sites import from one place. - **`wrapTool` → `executeToolCall` contract** documented (same provider instance) and instrumented with `debug` logs on the rewrite/restore paths; re-wrapping a slug refreshes or clears its mapping. - Dropped the inaccurate "mirrors the Python SDK" comment and two `as unknown as` casts. ### Known limitations Both cases below leave a value reaching the backend un-restored — still strictly better than the whole `tools` array 400-ing. - **Dynamic-key positions.** After dereferencing, only genuinely dynamic-key positions remain rewrite-only (not restored): `additionalProperties`, `patternProperties`, and `contains`. These can't be tied to a concrete value location without per-key schema association. - **Composition-branch key collisions.** When sibling `allOf`/`anyOf`/`oneOf` branches fold into their shared value level, two branches that sanitize different illegal keys to the *same* alias — or one branch whose alias equals a legitimately-named property in another branch — keep only the first-seen reverse mapping, so an argument satisfying the other branch restores to the wrong original name. Recovering which branch a runtime value matched needs full schema evaluation against the value; these collisions are rare in OpenAPI-derived schemas. (Documented on `mergeMappingInto`.) ### Testing Locally verified: `tsc --noEmit` clean on the core util and provider sources (0 own-file diagnostics); ESLint clean; Prettier-formatted; the full `@composio/core` (951) and `@composio/anthropic` (51) vitest suites pass after a fresh `pnpm --filter @composio/core build` (the earlier `picocolors` caveat from #3649 was a stale `@composio/core/dist`; rebuilding core clears it). **Please confirm CI is green before merging.**
Changesets
Hello and welcome! This folder has been automatically generated by @changesets/cli, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it in our repository
We have a quick list of common questions to get you started engaging with this project in our documentation