Commit Graph

370 Commits

Author SHA1 Message Date
Alberto Schiabel 705591451c chore(deps): upgrade CI actions and every outdated dependency (#4381)
This PR:

- upgrades every CI action to its latest release (only
`changesets/action` had one: v2.1.1 -> v2.1.2, SHA-pinned) and every
outdated dependency across the pnpm workspace, the docs bun workspace,
and all three `uv.lock` files
- moves zod to 4.5.4 everywhere first-party — catalog, docs,
`@composio/json-schema-to-zod`, `@composio/claude-agent-sdk` and the
zod-v4 e2e fixtures; the `*-zod-v3` fixtures stay on 3.25.76 because
that is what they exercise
- moves `@mastra/core` 1.52.1 -> 1.53.0, which is the ceiling rather
than a preference: bisecting `ts/examples/mastra`'s `cf:dry-run` shows
1.54.0 moved the workspace/sandbox subsystem behind
`@mastra/core/agent`, which drags execa (-> `npm-run-path` ->
`unicorn-magic`) into the Workers bundle where esbuild cannot link it.
`@mastra/mcp` is capped at 1.17.2 for the same reason — 1.17.3 wants
`@mastra/core` >=1.64. The docs bun workspace mirrors that cap as an
explicit devDependency plus `overrides` entry, because bun does not
apply overrides to auto-installed peers
- clears every production advisory that has a published fix, so the
audit gate can run without `--ignore`, which does not filter a single
run: it writes the advisory into `auditConfig` and exits 0 whatever else
is outstanding, so the gate was passing over nine advisories
- `qs` -> >=6.16.0, `fast-uri` -> >=3.1.6, `toml` -> the 4.x line, all
via overrides in the existing `# temporary: … drop when` style
- `extract-zip` (GHSA-jmr9-qjv8-65gv) has no fixed version to move to —
2.0.1 is the newest release and GitHub records `first_patched_version`
as null — so it moves to `auditConfig.ignoreGhsas` pointing at the
`extractZipSafely` mitigation that already covers it
- GHSA-866g-f22w-33x8 (`@ai-sdk/provider-utils` 3.x, low) also has
nothing to move to: the advisory names 3.0.98 as patched but the 3.x
line stopped at 3.0.30 and GitHub records no fixed version. It only
enters the tree through `@mastra/core`, which is a peer or dev
dependency of every published package, so all flagged paths are private
examples and e2e fixtures. It goes in `ignoreGhsas` with that rationale
so the un-levelled `pnpm audit --prod` step stops posting a warning
comment on every PR
- widens `@composio/anthropic`'s `@anthropic-ai/sdk` peer range to
include `^0.124.0`, the line its devDependency now tests against (for a
`0.x` caret, `^0.120.0` excluded it); the package is in the changeset
for that reason
- adapts three call sites that upstream broke: `eve` 0.52 moved
`ApprovalContext` to `eve/tools/approval`, `@pierre/diffs` 1.4 gave
`FileDiffProps` a second type parameter, and `fumadocs-openapi` 11.4
fixed the undeclared-tag drop that a docs guard test asserted (the guard
now also asserts the page positively, so it cannot pass vacuously)
- drops the stale `hono` `minimumReleaseAgeExclude` entry (its comment
said to after 2026-08-06) and adds an `undici` `peerDependencyRules`
allowance for openai 7.10's new optional peer

## Context

Some upgrades were deliberately declined, each for a reason recorded
next to the pin:

- `vitest`/`@vitest/ui` stay on 4.1.11 —
`@cloudflare/vitest-pool-workers@0.22.0` (latest) peers on `vitest
^4.1.0`
- `undici` stays on `^7` in core — `pinnedDispatcher.node.ts` documents
that Node's `fetch` rejects undici 8 dispatchers
- the `pnpm` catalog entry stays on `^11` to match the mise-owned
toolchain
- `eve` stays on 0.27.6 in docs — 0.52 changes the `defineAgent` model
definition and the `useEveAgent` helpers, so `agent/agent.ts` and
`components/eve-chat.tsx` fail `types:check`; migrating the docs agent
is its own PR
- `@earendil-works/pi-coding-agent` stays on 0.84.4 — 0.85.x imports
`@earendil-works/pi-server` without declaring it, so `test/pi.test.ts`
fails to load

`declareOperationTags` is kept as a safety net rather than retired, even
though `fumadocs-openapi` 11.4 makes it redundant: removing it changes
how specs are normalised at sync time and is worth its own PR.

Verified locally: `pnpm build:packages`, `pnpm typecheck`, `pnpm test`,
`pnpm typecheck:examples`, `pnpm lint:examples`, `turbo cf:dry-run
--filter='./ts/examples/*'`, `pnpm peers check`, `pnpm audit --prod
--audit-level=high` (exit 0), frozen-lockfile installs for pnpm and bun,
docs `types:check` + 542 static tests, and Python `make chk` + `make
tst` (1790 passed).

https://claude.ai/code/session_018evFic47PFPXuB95uRE1aw
EOF -R ComposioHQ/composio
2026-09-08 16:15:34 +02:00
Alberto Schiabel 20aaa95c96 ci(ts): verify packed provider compatibility (#4355)
This PR:

- adds a clean consumer harness that packs core, its internal JSON
Schema dependency, and all ten TypeScript providers
- verifies tarball contents, npm installation, named public exports,
consumer typechecking, provider construction, and a credential-free
`wrapTool` conversion
- covers the current workspace core, one verified minimum-core lane per
provider, and the packed workspace core presented as `1.0.0-beta.0`
- preserves existing 0.x minimum peer ranges while recording the
verified floors separately for the future breaking release
- additively accepts core 1.0 prereleases without claiming stable 1.x
support yet
- widens the Anthropic and OpenAI Agents peer ranges to include the
upstream versions already used by this repository
- runs the gate in TypeScript CI and immediately before Changesets
publishing

The release guard fails before publication and its regression test
verifies build -> compatibility -> publish ordering plus failure
propagation.

## Non-breaking scope

No public API is removed or renamed, and the existing 0.x core peer
floors remain unchanged. All peer-range changes are additive. The gate
reports the nine floor corrections that should be made with the planned
breaking release.

## Validation

- `pnpm run check:provider-compatibility` (12 packed consumer lanes)
- `pnpm run test:provider-compatibility`
- `pnpm run test:release-workflow`
- `pnpm run build:packages` (19 packages)
- focused TypeScript compile and Oxlint checks
- Prettier, Changesets validation, and `git diff --check`
2026-09-07 12:33:53 +02:00
sdkrelease[bot] 2573c64d97 Release: update version (#4285)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to next, this PR will
be updated.


# Releases
## @composio/claude-agent-sdk@0.12.0

### Minor Changes

- 9447932: Dereference internal $ref/$defs in tool input schemas before
provider translation, so properties reachable only through a reference
keep their types and validation instead of degrading to untyped (z.any)
or being emitted as a dangling reference.

This changes the JSON Schema these providers emit for $ref-using tools.
Downstream snapshot tests on tool definitions will see diffs. Schemas
the Composio API ships with a $ref but no $defs block (e.g.
GMAIL_FETCH_EMAILS) degrade to a permissive object schema rather than
throwing. The strict-structured-outputs path of @composio/openai-agents
is unchanged — OpenAI supports $defs/$ref natively, including recursion.
## @composio/google@0.11.0

### Minor Changes

- 9447932: Dereference internal $ref/$defs in tool input schemas before
provider translation, so properties reachable only through a reference
keep their types and validation instead of degrading to untyped (z.any)
or being emitted as a dangling reference.

This changes the JSON Schema these providers emit for $ref-using tools.
Downstream snapshot tests on tool definitions will see diffs. Schemas
the Composio API ships with a $ref but no $defs block (e.g.
GMAIL_FETCH_EMAILS) degrade to a permissive object schema rather than
throwing. The strict-structured-outputs path of @composio/openai-agents
is unchanged — OpenAI supports $defs/$ref natively, including recursion.
## @composio/langchain@0.11.0

### Minor Changes

- 9447932: Dereference internal $ref/$defs in tool input schemas before
provider translation, so properties reachable only through a reference
keep their types and validation instead of degrading to untyped (z.any)
or being emitted as a dangling reference.

This changes the JSON Schema these providers emit for $ref-using tools.
Downstream snapshot tests on tool definitions will see diffs. Schemas
the Composio API ships with a $ref but no $defs block (e.g.
GMAIL_FETCH_EMAILS) degrade to a permissive object schema rather than
throwing. The strict-structured-outputs path of @composio/openai-agents
is unchanged — OpenAI supports $defs/$ref natively, including recursion.
## @composio/llamaindex@0.11.0

### Minor Changes

- 9447932: Dereference internal $ref/$defs in tool input schemas before
provider translation, so properties reachable only through a reference
keep their types and validation instead of degrading to untyped (z.any)
or being emitted as a dangling reference.

This changes the JSON Schema these providers emit for $ref-using tools.
Downstream snapshot tests on tool definitions will see diffs. Schemas
the Composio API ships with a $ref but no $defs block (e.g.
GMAIL_FETCH_EMAILS) degrade to a permissive object schema rather than
throwing. The strict-structured-outputs path of @composio/openai-agents
is unchanged — OpenAI supports $defs/$ref natively, including recursion.
## @composio/openai-agents@0.11.0

### Minor Changes

- 9447932: Dereference internal $ref/$defs in tool input schemas before
provider translation, so properties reachable only through a reference
keep their types and validation instead of degrading to untyped (z.any)
or being emitted as a dangling reference.

This changes the JSON Schema these providers emit for $ref-using tools.
Downstream snapshot tests on tool definitions will see diffs. Schemas
the Composio API ships with a $ref but no $defs block (e.g.
GMAIL_FETCH_EMAILS) degrade to a permissive object schema rather than
throwing. The strict-structured-outputs path of @composio/openai-agents
is unchanged — OpenAI supports $defs/$ref natively, including recursion.
## @composio/vercel@0.12.0

### Minor Changes

- 9447932: Dereference internal $ref/$defs in tool input schemas before
provider translation, so properties reachable only through a reference
keep their types and validation instead of degrading to untyped (z.any)
or being emitted as a dangling reference.

This changes the JSON Schema these providers emit for $ref-using tools.
Downstream snapshot tests on tool definitions will see diffs. Schemas
the Composio API ships with a $ref but no $defs block (e.g.
GMAIL_FETCH_EMAILS) degrade to a permissive object schema rather than
throwing. The strict-structured-outputs path of @composio/openai-agents
is unchanged — OpenAI supports $defs/$ref natively, including recursion.
## @composio/core@0.18.1

### Patch Changes

- 8a56383: Fix: automatic S3 file downloads are now capped at 100 MiB
(configurable per call) to prevent memory exhaustion from oversized or
streaming responses.
- 7420927: Fix custom toolkit child slug mapping: reject response tools
that have local handles but no exact toolkit match instead of silently
dropping them or binding another toolkit's handler, derive bare-slug
ambiguity from local definitions, and only reuse a same-toolkit bare
alias in customToolkits().
- 1d31c80: Redact credential-shaped values at the SDK log boundary.
- 95f9d32: Expose the runtime-conditional SSRF-safe fetch helper for
protected URL upload consumers.
- 0d28bef: Map file-download transport failures to the SDK error
contract and bound streamed response bodies.
- 52efb5b: Fix trigger subscriptions ignoring the `authConfigId` filter.
- Updated dependencies [ab289d6]
  - @composio/json-schema-to-zod@0.3.2
## @composio/experimental@0.2.4

### Patch Changes

- 4e633d1: Update TypeBox to 1.3.18.
## @composio/json-schema-to-zod@0.3.2

### Patch Changes

- ab289d6: Preserve Draft 7 acceptance across primitive, composed,
referenced, conditional, and typeless schemas. Enforce sibling and
object/array assertions, retain positional tuple and `additionalItems`
behavior, and prevent native Zod materialization from rejecting values
already accepted by the source schema.
## @composio/openai@0.12.2

### Patch Changes

- 620075a: Fix: stop printing MCP server URLs (credential-bearing) to
stdout via console.log in the OpenAI Responses provider; log server
names via logger.debug instead.
## @composio/slim@0.18.1

### Patch Changes

- Updated dependencies [ab289d6]
  - @composio/json-schema-to-zod@0.3.2
## @e2e-tests/cf-workers-basic@0.0.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
## @e2e-tests/cf-workers-files@0.0.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
## @e2e-tests/cf-workers-tool-router-ai@0.0.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/vercel@0.12.0
## @e2e-tests/node-claude-agent-sdk@0.0.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/claude-agent-sdk@0.12.0
## @e2e-tests/node-custom-tools@0.0.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
## @e2e-tests/node-json-schema-to-zod-v3@0.0.2

### Patch Changes

- Updated dependencies [ab289d6]
  - @composio/json-schema-to-zod@0.3.2
## @e2e-tests/node-json-schema-to-zod-v4@0.0.2

### Patch Changes

- Updated dependencies [ab289d6]
  - @composio/json-schema-to-zod@0.3.2
## @e2e-tests/node-mastra-tool-router-zod-v3@0.0.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/mastra@0.10.4
## @e2e-tests/node-mastra-tool-router-zod-v4@0.0.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/mastra@0.10.4
## @e2e-tests/node-tool-router-files@0.0.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
## @e2e-tests/node-tool-router-pagination@0.0.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
## anthropic-example@0.1.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/claude-agent-sdk@0.12.0
  - @composio/anthropic@0.11.1
## connected-accounts-example@0.1.11

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
## error-handling-example@0.1.11

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
## file-handling-example@0.1.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
## google-example@0.1.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/google@0.11.0
## json-schema-to-zod-example@0.1.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/vercel@0.12.0
## langchain-example@0.1.11

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/langchain@0.11.0
## llamaindex-example@0.1.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/llamaindex@0.11.0
## mastra-example@0.1.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/mastra@0.10.4
## mcp-example@0.1.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/vercel@0.12.0
## modifiers-example@0.1.11

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/vercel@0.12.0
## openai-example@0.1.11

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [620075a]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/openai-agents@0.11.0
  - @composio/openai@0.12.2
## session-management-example@0.1.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
## tool-router-example@1.0.12

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/claude-agent-sdk@0.12.0
  - @composio/vercel@0.12.0
  - @composio/openai-agents@0.11.0
## toolkits-example@0.1.11

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/vercel@0.12.0
## tools-example@0.1.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/vercel@0.12.0
## triggers-example@0.1.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
## vercel-example@0.1.11

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [9447932]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
  - @composio/vercel@0.12.0
## versioning-example@0.1.2

### Patch Changes

- Updated dependencies [8a56383]
- Updated dependencies [7420927]
- Updated dependencies [1d31c80]
- Updated dependencies [95f9d32]
- Updated dependencies [0d28bef]
- Updated dependencies [52efb5b]
  - @composio/core@0.18.1
## @composio/json-schema-to-effect-schema@0.1.1

### Patch Changes

- ab289d6: Translate draft-4 boolean
`exclusiveMinimum`/`exclusiveMaximum` flags (as emitted by OpenAPI 3.0
exporters) into their Draft 7 numeric spelling so exclusive bounds are
enforced instead of silently ignored.

Co-authored-by: sdkrelease[bot] <294075132+sdkrelease[bot]@users.noreply.github.com>
2026-09-04 21:01:00 +02:00
Alberto Schiabel ab289d6224 fix(sdk): preserve primitive JSON Schema semantics (#4316)
## Summary

- preserve boolean, empty, null, type-array, enum, const, and
scalar-constraint semantics across every Python conversion entry point
- intersect Zod enum and const values with declared types and
constraints, including compound JSON values
- default unversioned exact validation to Draft 7 and apply inclusive
and numeric exclusive bounds independently
- run one byte-identical corpus through Python, Zod, and Effect so
accepted and rejected inputs stay aligned
- keep exact JSON Schema acceptance separate from Pydantic default
materialization

## Review follow-up (second push)

- Python: exact Draft 7 acceptance now wraps all three entry points
(`json_schema_to_pydantic_type`, `json_schema_to_model`,
`pydantic_model_from_param_schema`), so they can no longer disagree
- Python: draft-4 boolean `exclusiveMinimum`/`exclusiveMaximum` (OpenAPI
3.0 style) no longer crash conversion — exact validation falls back to
Draft 4, and the library input is translated to the numeric spelling
- Python: ECMA-only regex patterns (look-around) no longer crash
pydantic model builds — Rust-incompatible patterns fall back to Python
`re`
- Python: type arrays with sibling constraints no longer raise
`TypeError` on valid input — constraints are scoped per member before
the library sees them
- Python: integral floats satisfy `integer`, `const` intersects `enum`,
annotation-only schemas accept anything, and an optional property with
an empty `enum` tolerates absence
- Zod: typeless scalar constraints apply per instance type, and string
lengths count Unicode code points instead of UTF-16 code units
- Effect: draft-4 boolean exclusive bounds are enforced instead of
silently ignored
- `multipleOf` uses decimal scaling in all three converters (declared
`divergesFromJsonSchema` on the corpus case)
- shared corpus grows by 13 primitive cases; new property-based tests
check acceptance against real Draft 7 oracles (hypothesis + `jsonschema`
in Python, fast-check + Ajv in TypeScript)

## Verification

- Python `make chk` (ruff + mypy)
- Python pytest: 1,572 passed (5 langchain-extra tests need an env this
sandbox lacks; unchanged from base)
- `@composio/json-schema-to-zod`: 187 passed incl. 300-run fast-check
property test; typecheck + build
- `@composio/json-schema-to-effect-schema`: 133 passed; typecheck
- `@composio/core` corpus ingress tests: 61 passed
- shared Python/TypeScript corpus files are byte-identical
(shasum-verified)
- `git diff --check`

## Contributor context

This replaces four narrow proposals after independent local
reproduction:

- [#4301](https://github.com/ComposioHQ/composio/pull/4301) ·
[Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4301)
- [#4302](https://github.com/ComposioHQ/composio/pull/4302) ·
[Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4302)
- [#4303](https://github.com/ComposioHQ/composio/pull/4303) ·
[Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4303)
- [#4307](https://github.com/ComposioHQ/composio/pull/4307) ·
[Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4307)

---------

Co-authored-by: simpleqt <89645338+simpleqt@users.noreply.github.com>
2026-09-04 14:19:02 +02:00
CoralGarden52 7420927183 fix(sdk): qualify custom toolkit child slug mapping across Python and TypeScript (#4311)
## Summary
The Python SDK treated a custom tool's `original_slug` as globally
unique, rejecting valid custom toolkits that reuse common child names
such as `SEARCH`, `VERSION`, or `GREP` even though the backend-assigned
final slugs are toolkit-qualified (`LOCAL_ALPHA_GREP`,
`LOCAL_BETA_GREP`).

This ports the toolkit-qualified lookup from #3360 to Python, then fixes
three response-mapping bugs found in review and applies the same fixes
to the TypeScript SDK so both stay in parity.

## Changes

### Python (`composio`)
- Scope custom-tool collision detection and response matching by toolkit
plus original slug.
- Keep bare original-slug aliases only when unambiguous;
`session.execute("GREP")` raises with the final slugs to use when the
slug is shared.
- Preserve toolkit-qualified final slugs in `custom_toolkits()`.
- `build_custom_tools_map_from_response`: raise when a response tool has
local handles but no exact toolkit match instead of silently dropping it
or binding another toolkit's handler; only fall back to a bare match
when the response carries no toolkit identity; reject duplicate
qualified response entries; derive bare-slug ambiguity from local
definitions so omitting a sibling in the response never makes the
survivor callable by bare name.
- `custom_toolkits()` only reuses a bare alias that belongs to the same
toolkit.
- Docstring and Python session reference page state that bare-slug
execution requires a unique original slug.

### TypeScript (`@composio/core`)
- Same four fixes in `buildCustomToolsMapFromResponse` and the same
guard in `customToolkits()`.
- JSDoc and TypeScript session reference page updated.
- Changeset: patch for `@composio/core`.

### Not changed
- `COMPOSIO_MULTI_EXECUTE_TOOL` still aborts the whole batch when one
item uses an ambiguous bare slug, matching current TS behavior.
Switching to per-item errors is a cross-SDK design change left for a
follow-up.

## Type of change
- [x] Bug fix
- [ ] New feature
- [ ] Refactor/Chore
- [ ] Documentation
- [ ] Breaking change

## How Has This Been Tested?
Python:
- `pytest tests/test_custom_tools.py tests/test_tool_router.py`: 181
passed.
- ruff (project config) clean; mypy reports no errors in the touched
files.
- New tests: sibling routing, multi-execute, preload rejection, listing
guard, and five response-mapping cases (no exact match, cross-toolkit
binding, standalone bare fallback, unknown response tools skipped,
ambiguity from local definitions, duplicate qualified entries).

TypeScript:
- `vitest run` in `ts/packages/core`: 53 files, 1251 passed, 2 expected
failures.
- `tsc --noEmit` clean; prettier and oxlint via pre-commit hook.
- New tests: cross-toolkit reuse in `buildCustomToolsMap` and a new
`buildCustomToolsMapFromResponse` block mirroring the Python cases.

Python and TypeScript CI do not run automatically on this fork PR; a
maintainer needs to approve the workflow run.

## Checklist
- [x] I have read the Code of Conduct and this PR adheres to it
- [x] I ran linters/tests locally and they passed
- [x] I updated documentation as needed
- [x] I added tests or explain why not applicable
- [x] I added a changeset if this change affects published packages

## Additional context
Reviewed with a second opinion from Codex (gpt-5.6-sol), which flagged
the wrong-handler binding and response-derived ambiguity bugs fixed in
the follow-up commits.

https://claude.ai/code/session_01Y7Ni3QEBDGShSrEtwQS5bA
EOF -R ComposioHQ/composio

---------

Signed-off-by: CoralGarden52 <2193436736@qq.com>
Co-authored-by: jkomyno <alberto@composio.dev>
Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
2026-09-03 14:08:48 +02:00
CoralGarden52 52efb5b833 fix(core): respect authConfigId in trigger subscriptions (#4298)
## Summary

- Apply the existing authConfigId subscription filter to incoming
trigger events.
- Add a V3 regression test for mismatched auth configurations.
- Add a patch changeset for @composio/core.

Previously, a subscription filtered by authConfigId still invoked its
callback for events belonging to a different auth configuration.

## Verification

- Vitest targeted tests: 76 passed
- Vitest core suite: 1244 passed, 2 expected failures
- TypeScript typecheck and Prettier passed

Fixes the missing authConfigId filtering in trigger subscriptions.

---------

Co-authored-by: jkomyno <alberto@composio.dev>
2026-09-03 12:15:46 +02:00
Alberto Schiabel 0d28befb14 fix(sdk): map streamed file transport failures (#4321)
## Summary

- map Python file-fetch failures that occur after response headers into
the documented upload and download errors
- map TypeScript RemoteFile connection and streamed-body failures into
RemoteFileDownloadError while preserving blocked-URL errors
- close or cancel response bodies on every exit and apply the shared 100
MiB response limit to TypeScript RemoteFile downloads

This supersedes the Python-only proposal in #4305 and carries the same
failure category across both SDKs.

## Independent reproduction

A response double returned one chunk and then raised a connection-reset
error. On current next:

- Python _fetch_file_from_url leaked ConnectionError, although it did
close the response
- Python Tool Router URL fetch leaked ConnectionError and left the
response open
- TypeScript RemoteFile leaked the native fetch/body TypeError instead
of RemoteFileDownloadError

## Verification

- Python make chk
- Python make tst: 1,490 passed
- TypeScript core typecheck
- TypeScript core tests: 1,245 passed, 2 expected failures
- TypeScript package build: 19 packages
- focused Python regression tests: 3 passed
- focused TypeScript RemoteFile tests: 17 passed
2026-09-03 12:06:37 +02:00
Alberto Schiabel 95f9d3295f fix(cli): guard URL file uploads against SSRF (#4319)
## Summary

- route attacker-controlled URL sources and API-provided presigned
upload destinations through the runtime-conditional core SSRF guard
- expose that guard through a Node/workerd-aware core subpath
- validate and revalidate DNS on redirects, pin Node and Bun connections
to validated addresses, and preserve configured proxy routes
- fail closed for user-chosen URL uploads in edge runtimes
- cancel ignored response bodies and cover both upload boundaries at the
public CLI pipeline
- avoid adding `Content-Length: 0` to bodyless Bun GET requests

## Local reproduction

On `next`, the CLI pipeline used bare `fetch` for both targets. A local
loopback source and an internal presigned destination reached the
network path. With this branch, the real `uploadToolInputFiles` pipeline
blocks a `127.0.0.1` source before presigning and a `169.254.169.254`
destination before sending bytes.

A direct Bun proof also confirmed the pinned transport reaches a
validated address without calling native `fetch`, while retaining the
original Host header. Focused tests preserve native Bun fetch when an
environment proxy is configured.

## Cross-SDK parity

Python already applies public-address validation, redirect checks, DNS
pinning, and response limits to URL uploads. Its focused URL-safety and
upload suite remains green: 62 passed. No Python behavior change was
needed.

## Verification

- `pnpm build:packages`: 19 packages built
- root `pnpm typecheck`: 14 package checks passed
- TypeScript core: 53 files, 1,246 passed and 2 expected failures
- focused core SSRF and pinned-transport tests: 31 passed
- Python URL-safety/upload tests: 62 passed
- real Bun execution of both CLI upload boundaries: blocked before
presign/send
- `git diff --check`

The CLI Effect test file contains source and presigned-destination
regressions and typechecks. Its local runner is blocked on current
`next` by the repository-wide `@effect/vitest` config failure; hosted
CLI checks exercise that boundary.

## Contributor context

Supersedes [#4299](https://github.com/ComposioHQ/composio/pull/4299) ·
[Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4299) after
independently reproducing the attack path. The report is valid and
useful, but the proposed root export hard-coded a Node module into an
edge-capable package, its happy-path test did not execute the returned
Effect, and it omitted destination protection, response cleanup, and Bun
address pinning.
2026-09-03 11:51:30 +02:00
Alberto Schiabel 1d31c80eff fix(sdk): keep credentials private in storage and logs (#4318)
## Summary

- write CLI user data, pending login sessions, and agent identities
through one atomic `0600` helper
- repair `0644` credential files created by older CLI versions before
reading them
- redact credential-shaped structured values from CLI user-context
diagnostics
- redact secret-shaped text at both TypeScript and Python SDK log-output
boundaries, including Pusher `auth` responses and exception tracebacks
- preserve Python logger compatibility: errors remain untruncated,
disabled levels remain lazy, and malformed placeholders cannot expose
arguments

## Local reproduction

Under the normal `022` umask, `next` created a plaintext credential file
with mode `0644`. The pre-fix CLI user-context and TypeScript SDK debug
paths also emitted sentinel credentials. The private atomic writer
changes an existing `0644` target to `0600`, and the upgrade tests now
prove all three legacy credential files are tightened without changing
their contents.

## Verification

- CLI permission upgrade tests: 31 passed across user data, pending
login, and agent identity paths
- CLI source and test typechecks passed
- TypeScript core logging, redaction, and Pusher tests: 17 passed
- TypeScript core source and type-test typechecks passed
- Python logging regression tests: 5 passed
- focused Ruff, Prettier, Oxlint, and `git diff --check` passed

The focused CLI runner needed a temporary local alias for the
pre-existing missing `#ssrf_guard` mapping in the CLI Vitest config. The
alias was removed after verification and is not part of this PR.

## Contributor context

Credit to **Syed Anas Mohiuddin**, independent security researcher, for
reporting the legacy CLI credential-file permission issue.

Supersedes [#4300](https://github.com/ComposioHQ/composio/pull/4300) ·
[Glen review](https://app.tryglen.com/ComposioHQ/composio/pull/4300).
The implementation also covers agent credentials, retains atomic writes,
and applies redaction at the shared SDK logging boundary.
2026-09-03 01:45:11 +02:00
jkomyno 4e633d1fe4 chore(changeset): record experimental dependency update 2026-08-28 15:14:54 +02:00
Alberto Schiabel 1157faf0a1 fix(providers): dereference $ref/$defs before schema translation (#4288)
This PR:
- resolves internal `$ref`/`$defs` in tool input schemas before
translation in `langchain`, `llamaindex`, `claude-agent-sdk`, `vercel`,
`google`, and `openai-agents` (`onUnresolved: 'sentinel'`)
- previously `$ref`-typed properties degraded to `z.any()` — the Zod
converter has no `$ref` branch — or were emitted as dangling references
after the root rebuild (google, openai-agents fallback)
- keeps `openai-agents`' strict-structured-outputs path untouched:
OpenAI resolves `$defs`/`$ref` natively including recursion, pinned by a
guard test
- adds per-provider `$ref` regression suites for all six providers,
including dangling-`$defs` (`GMAIL_FETCH_EMAILS`) and recursive-schema
cases
- adds a cross-provider contract test that fails when a new provider
ships without a `$ref` classification, plus property tests for
`dereferenceJsonSchema` (Python counterparts land with the Python-side
fix)
- changes the vendor-visible schema shape for `$ref`-using tools;
changeset is `minor`

## Context

The same bug was fixed locally twice before (mastra, anthropic) without
surfacing the other six providers — nothing enumerated providers and
asked the `$ref` question. The new contract test does exactly that, so
provider #11 cannot ship unclassified. Python mirrors exist already; the
Python-side provider fix follows separately.
2026-08-28 14:42:35 +02:00
jkomyno 9447932d98 fix(providers): dereference $ref/$defs schemas before translation
jsonSchemaToZodSchema has no $ref branch, so a $ref node degrades to
z.any() for langchain, llamaindex, claude-agent-sdk, and vercel's
default path. google and openai-agents' non-strict fallback rebuild
the root from properties/required, discarding $defs while dangling
$ref pointers survive.

Dereference internal $ref/$defs before translation in all six, using
onUnresolved: 'sentinel' so a $ref into an undeclared $defs block
(e.g. GMAIL_FETCH_EMAILS) degrades to a permissive schema instead of
throwing. The mastra and anthropic providers already had this fix;
openai-agents' strict branch is untouched since OpenAI's structured
outputs support $defs/$ref natively, including recursion, and
google's rebuild still drops additionalProperties/title/root
oneOf-anyOf-allOf beyond the dangling-$ref class this fixes.
2026-08-28 11:40:49 +02:00
jkomyno 620075a5de fix(openai): stop logging MCP server URLs to stdout 2026-08-28 11:38:12 +02:00
jkomyno 8a56383b24 fix(core): cap automatic S3 download size at 100 MiB
`downloadFileFromS3` buffered the whole response with `arrayBuffer()`. The
`s3Url` it fetches is a tool-execution response field — the same untrusted
input the SSRF guard already defends against — so an oversized or endlessly
streaming body could exhaust the host process's heap.

Route the body through the existing `readResponseBodyWithLimit` guard, which
pre-checks `Content-Length` and counts streamed bytes (the header can be
absent or dishonest). The 100 MiB default matches the upload-from-URL sibling
in the same module; `maxDownloadBytes` overrides it per call.

Claude-Session: https://claude.ai/code/session_01K1hH9PMmd6KPKdkACX553z
2026-08-28 09:50:57 +02:00
sdkrelease[bot] dbe5a63965 Release: update version 2026-08-27 18:07:50 +00:00
Alberto Schiabel 08306f8bc1 Merge branch 'next' into fix/error-subclass-names 2026-08-27 18:51:36 +02:00
Alberto Schiabel 3c7b938bd1 Merge branch 'next' into fix/telemetry-request-timeout 2026-08-27 18:27:32 +02:00
Alberto Schiabel 81631f83f4 Merge branch 'next' into fix/strict-mode-keep-optional-parameters 2026-08-27 15:14:24 +02:00
jkomyno 9692db5c0a fix(openai-agents): honor the strict option
OpenAIAgentsProvider accepted { strict } but always registered tools with
strict: false and additionalProperties: true. Strict mode now registers
tools with strict: true and a schema normalized by toStrictJsonSchema
(optional parameters required-nullable), drops null arguments the tool
schema rejects before execution, and registers tools strict mode cannot
express without strict mode with a warning.

Co-authored-by: AseemPrasad <aseemprasad0520@gmail.com>

Claude-Session: https://claude.ai/code/session_01TDrxCHn2hg51HmxVstSUgs
2026-08-26 17:34:08 +02:00
jkomyno 3c3b4dae94 fix(mastra): keep optional parameters under strict mode
MastraProvider strict mode used the root-only, input-mutating
removeNonRequiredProperties, so "strict" meant something different from
the OpenAI providers. It now runs the same toStrictJsonSchema rewrite:
optional parameters become required-nullable, tools strict mode cannot
express keep their original schema with a warning, and null arguments the
tool schema rejects are dropped before execution.

Co-authored-by: AseemPrasad <aseemprasad0520@gmail.com>

Claude-Session: https://claude.ai/code/session_01TDrxCHn2hg51HmxVstSUgs
2026-08-26 17:34:08 +02:00
jkomyno 4f69975475 fix(core): keep optional parameters under strict mode instead of dropping them
toStrictJsonSchema now follows the contract OpenAI documents for structured
outputs: every property becomes required and optional ones are widened to
accept null, so the model keeps every parameter it could pass before. Type
arrays stay as they are (the API accepts them and rejects type next to
anyOf), so nullable objects stay nullable. Constructs strict mode cannot
express (objects with arbitrary keys, allOf, prefixItems, unresolved $refs,
non-object roots) are reported in `unsupported` instead of being narrowed.

omitNullToolArguments drops a null argument only where the tool's own
schema rejects it, so nullable fields keep an explicit null. The keyword
taxonomy shared by the three schema walkers now lives in one place.

Co-authored-by: AseemPrasad <aseemprasad0520@gmail.com>

Claude-Session: https://claude.ai/code/session_01TDrxCHn2hg51HmxVstSUgs
2026-08-26 17:19:30 +02:00
Alberto Schiabel 9e958948c5 fix(core): block sensitive upload paths hidden behind a symlinked directory (#4218)
# Description

Found by the scheduled security audit, while checking whether a test
failure on my other PR was pre-existing. It was — and the reason it
fails is a real gap in a shipped security control.

`isBlockedSensitiveFileUploadPath` (the GHSA-hp3h-89pf-5q58 denylist)
matched deny segments **only against the symlink-resolved path**. That
catches a benign name pointing at a secret — `~/innocent-name ->
~/nested/.aws/creds`, which the existing test covers — but misses the
inverse:

| Layout | Written path | Resolved path | Blocked before? |
|---|---|---|---|
| `~/innocent -> ~/.aws/creds` | no `.aws` | **`.aws`** | yes |
| `~/.claude -> /state/claude` | **`.claude`** | no `.claude` | **no** |

`~/.claude/settings.json` resolves to `/state/claude/settings.json`,
which has no `.claude` segment, so it sailed through — and `.claude` is
on the denylist precisely because it *"may contain API keys and project
context read by assistants"*.

This layout is not exotic. Dotfile managers (chezmoi, stow, yadm) and
containerised home directories produce it routinely — Composio's own
agent sandbox image has `/home/zen/.claude -> /state/claude`. For every
user in that shape the control was silently inactive, which is the worst
failure mode for a denylist: no error, no warning, upload proceeds.

The same hiding trick applies to the basename check (`~/.env ->
/state/plain-config`), so that path is fixed too.

## Why CI never caught this

`ts/packages/core/test/utils/sensitiveFileUploadPaths.test.ts` **already
asserts** the blocked behaviour:

```ts
expect(isBlockedSensitiveFileUploadPath(path.join(os.homedir(), '.claude', 'settings.json'))).toBe(true);
```

That assertion has been failing on `next` on any machine where
`~/.claude` is a symlink. It passes in CI only because `~/.claude` does
not exist on the runners: `existsSync` is false, no `realpath` runs, and
the written path keeps its `.claude` segment. The test is
environment-dependent, so green CI was never evidence the control
worked.

## The fix

The TypeScript `normalizePath` helper now returns both the written and
resolved segments, and the segment scan and basename check each consider
both. The Python guard now applies the same rule. Either path can carry
the denied name, so both SDKs inspect both forms.

# How did I test this PR

**The TypeScript fix is gated by tests — 3 fail without it, 13/13 pass
with it.**

Without the `src` change (test file only):

```
× blocks common credential directory segments
× blocks a sensitive directory that is itself a symlink to a plain path
× blocks a denied basename whose symlink target is named innocuously
  Tests  3 failed | 10 passed (13)
```

With the fix:

```
  Test Files  1 passed (1)
        Tests  13 passed (13)
```

Note the first of those three is the **pre-existing** assertion quoted
above — this PR turns it green rather than adding it.

Three tests added, each building a real symlink in a temp dir:
- sensitive directory that is itself a symlink to a plain path (the
`~/.claude -> /state/claude` case), asserting both
`isBlockedSensitiveFileUploadPath` and that `assertSafeFileUploadPath`
throws
- denied basename whose symlink target is named innocuously (`.env ->
plain-config`)
- **negative case**: an ordinary file reached through a symlinked
directory (`docs/document.pdf`) is still allowed, so the fix does not
over-block

The Python parity change adds the same three cases. Before the Python
source change, the sensitive written directory and basename both
returned `False`; with the fix, all 11 focused Python tests pass.

**Full verification:**

| Command | Result |
|---|---|
| `vitest run` in `ts/packages/core` | **48 files, 1114 tests passed** |
| `pnpm typecheck` (workspace) | **14/14 tasks successful**, exit 0 |
| `oxlint` on both changed files | exit 0, clean |
| `prettier --check` on both changed files | "All matched files use
Prettier code style!" |
| `nox -s chk` in `python/` | Ruff and mypy passed |
| `nox -s tst` in `python/` | **1339 passed, 33 skipped**, exit 0 |

# Security

- No dependency changes, no new network calls, no new imports. The diff
is limited to the equivalent TypeScript and Python guards, their tests,
and the required `@composio/core` patch changeset.
- This **strengthens** an existing control and cannot weaken it: the
previous match set is a strict subset of the new one, so nothing that
was blocked before is allowed now. The added negative test pins that the
widening does not over-block ordinary files.
- **Grype** — `grype dir:ts/packages/core --only-fixed --fail-on medium`
→ reported below.
- **Socket** — could not run; `doppler secrets get SOCKET_API_TOKEN
--plain --project hermes --config dev_zen` returns empty in this cron
sandbox, so `socket ci` exits `Auth Error`. Reporting rather than
skipping silently.
- Unrelated pre-existing note: the repo's `pnpm audit --prod` comment
flags `extract-zip <=2.0.1` with `Patched versions >=2.0.2`, a version
that does not exist on npm. Details in #4217.

Origin: cron-48e51eab745f /
[zen-cron-44e260352d1a](https://zen.corp.composio.io/dashboard/#/chat/zen-cron-44e260352d1a)

Triggered by: saransh@composio.dev | Source: unknown
Session:
https://zen.corp.composio.io/dashboard/#/chat/zen-cron-44e260352d1a
2026-08-25 21:48:55 +02:00
jkomyno 449f4e128a chore: add changeset for symlink path protection 2026-08-25 21:02:36 +02:00
Alberto Schiabel f174b28c3a fix(sdk): omit empty file-uploadable arguments from tool execution (#4238)
This PR:

- closes https://github.com/ComposioHQ/composio/issues/4233
- stops forwarding `""` for a `file_uploadable` parameter (e.g. Gmail
`attachment`) to the backend, which rejected it with `Input should be a
valid dictionary or instance of FileUploadable`
- Python: parameterizes the upload walker on a `leaf` handler and adds
`FileHelper.drop_empty_file_uploads()`, run on both `Tools.execute`
paths when `dangerously_allow_auto_upload_download_files` is off (the
default)
- TypeScript: moves the walker into the runtime-neutral
`walkFileUploadableLeaves()` with a `DELETE_VALUE` sentinel and
`dropEmptyFileUploads()`, so the flag-off path also works on edge
runtimes; with the flag on, `''` is no longer attempted as an upload
(previously threw `Either path or blob must be provided`)
- TypeScript: `schemaHasFileProperty()` now looks through
`$defs`/`definitions`, so the flag-off pass (and the existing one-shot
warning) fire for `$ref`-based file schemas
- keeps each SDK's existing `null` semantics: Python omits `None` as
before, TypeScript still passes `null` through for schemas with a `null`
variant
- adds regression tests for both SDKs and a `@composio/core` changeset

## Context

Reproduced against staging with `composio==0.16.0` and the current SDK:
the live `GMAIL_CREATE_EMAIL_DRAFT` schema is `anyOf[FileUploadable,
array[FileUploadable]]` with no `null` variant, and `""` yields the
exact error from the issue on `no_auth` file tools
(`TEXT_TO_PDF_UPLOAD_FILE`). With this change the backend sees the
parameter as not provided, matching what the playground UI sends.
2026-08-25 16:01:54 +02:00
jkomyno fe66cbeb77 fix(sdk): omit empty file-uploadable arguments from tool execution
Both SDKs forwarded "" for a file_uploadable parameter (e.g. Gmail
attachment) verbatim to the backend, which rejected it with a Pydantic
validation error. Python only dropped it inside the opt-in auto-upload
walker; TypeScript never did, and with auto-upload on it tried to upload
the empty string.

Run a schema-aware, upload-free pass on the default execute path that
omits empty file values, and reuse the same walker for staging when
auto-upload is enabled.

Closes #4233
2026-08-25 01:58:52 +02:00
Alberto Schiabel 700327c2a6 Merge branch 'next' into chore/changesets-v3-migration 2026-08-25 01:18:03 +02:00
tgolob db7b576437 fix(ts): declare the supported Node.js engine floor (#4219)
## Summary

The ESM-only transition in #3494 established Node.js 22.22.3 as the
minimum supported runtime for the public TypeScript SDK packages, but
their published manifests still omit `engines.node`.

That leaves package managers without a package-level compatibility
signal before an older runtime encounters an ESM loading failure. For
example, the current `@composio/core@0.17.0` package fails with
`ERR_REQUIRE_ESM` when required on Node.js 22.0.0, while the same load
succeeds on Node.js 22.22.3.

This aligns the published metadata with the support floor already
documented and tested by the repository.

Related: #3494

## Changes

- Add `"engines": { "node": ">=22.22.3" }` to all 14 public TypeScript
release workspaces.
- Add a release-workflow invariant that discovers public TypeScript
workspaces from the root workspace configuration and rejects missing or
drifted Node.js engine ranges.
- Add a patch changeset covering exactly those 14 published packages.

<details>
<summary>Package scope</summary>

- Core packages: `@composio/core`, `@composio/slim`,
`@composio/experimental`, and `@composio/json-schema-to-zod`
- Providers: Anthropic, Claude Agent SDK, Cloudflare, Google, LangChain,
LlamaIndex, Mastra, OpenAI Agents, OpenAI, and Vercel
- Excluded as private/unpublished: CLI, CLI keyring, CLI local tools,
JSON Schema to Effect Schema, and TypeScript builders

</details>

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Refactor/Chore
- [ ] Documentation
- [ ] Breaking change

## How Has This Been Tested?

Validated with the repository-pinned Node.js 24.17.0, pnpm 11.8.0, and
Bun 1.4.0 toolchain:

- `pnpm install --frozen-lockfile`
- `pnpm run test:release-workflow`
- `pnpm validate:changesets`
- `pnpm exec changeset status --since=origin/next` (exactly 14 patch
releases)
- `pnpm build:packages` (19/19 packages)
- `pnpm typecheck` (14/14 tasks)
- `pnpm lint:packages` (successful; only pre-existing warnings in
untouched source files)
- Prettier over every touched file
- `git diff --check origin/next...HEAD`

The published-package probe also confirmed that `@composio/core@0.17.0`
has no `engines` metadata, CommonJS loading fails on Node.js 22.0.0, and
the same package loads successfully on Node.js 22.22.3.

No lockfiles, generated files, or runtime source files changed.

## Screenshots (if applicable)

Not applicable.

## Checklist

- [x] I have read the Code of Conduct and this PR adheres to it
- [x] I ran linters/tests locally and they passed
- [x] I updated documentation as needed
- [x] I added tests or explain why not applicable
- [x] I added a changeset if this change affects published packages

## Additional context

The runtime floor itself is not new: #3494 shipped and documented it as
a breaking change in the existing `0.x` line. This patch makes registry
metadata accurately reflect that existing support contract, so the
accompanying releases are patches.

---------

Co-authored-by: Tomas Golob <tgolob@users.noreply.github.com>
Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-08-25 01:11:05 +02:00
jkomyno e4189aeb57 chore(release): migrate to Changesets v3 2026-08-25 01:01:10 +02:00
AseemPrasad e57661755b Merge branch 'next' of https://github.com/ComposioHQ/composio into asimcomposio
# Conflicts:
#	python/composio/core/provider/_openai_responses.py
#	ts/packages/providers/openai/src/OpenAIResponsesProvider.ts
2026-08-23 01:19:42 +05:30
AseemPrasad 04817cb20d fix(openai): recursive strict-mode schema normalization for structured outputs (+ Python parity) 2026-08-23 00:17:57 +05:30
Alberto Schiabel d544006a25 fix(sdk): pin the validated address when fetching URLs (SSRF DNS rebinding) (#4172)
Fixes #4151.

## The problem

Both SDKs validated a URL by resolving its hostname, and then handed the
*hostname* to the HTTP client, which resolved it again when it opened
the socket. Two lookups, two answers: a short-TTL record under an
attacker's control answers publicly for the check and with
`169.254.169.254`, `127.0.0.1`, or RFC 1918 space for the connect. The
guard passes and the connection lands inside the network — classic
TOCTOU DNS rebinding, documented in both modules until now as a known
residual.

```mermaid
sequenceDiagram
    participant SDK
    participant DNS as Attacker DNS
    participant Meta as 169.254.169.254
    Note over SDK,Meta: before
    SDK->>DNS: resolve evil.example.com (validate)
    DNS-->>SDK: 93.184.216.34 — passes the guard
    SDK->>DNS: resolve evil.example.com (connect)
    DNS-->>SDK: 169.254.169.254
    SDK->>Meta: GET /latest/meta-data/…
    Meta-->>SDK: credentials
```

## The fix

Resolve once, validate every answer, then connect to the address that
was validated. There is no second lookup left to rebind.

- **Python** — `safe_get` / `safe_request` mount a transport adapter
that swaps the connect target for the duration of the socket connect
only. The `Host` header and TLS SNI keep the hostname, so certificate
verification is unchanged; rewriting `conn._dns_host` for the whole
connection would have sent `Host: <ip>` and offered the IP as SNI,
failing against every real origin. Every fetch call site now goes
through those two helpers, so no `requests.get` sits next to a bare
check any more:
- `_files.py::_fetch_file_from_url`,
`_files.py::FileDownloadable.download`
  - `tool_router_session_files.py::_fetch_url_bytes`
  - `safe_request`, per redirect hop
- **TypeScript** — `assertSafeFetchTarget` returns the validated address
and `ssrfSafeFetch` hands `fetch` a dispatcher pinned to it, re-pinned
per redirect hop. The dispatcher goes to the runtime's own `fetch`, so
callers that stub `globalThis.fetch` keep working. The pinned `lookup`
answers both shapes Node calls it with — the address *list* it uses for
Happy Eyeballs, and the single `(address, family)` it uses when
`autoSelectFamily` is off — since answering in the wrong shape is
rejected as an invalid address.
- A fail-closed peer assertion runs on the Python side before a byte is
written to the socket — redundant while pinning works, and a tripwire if
a urllib3 upgrade ever breaks it.
- `workerd` is unchanged: it already fails closed for user-supplied
URLs.

Redirect *validation* already existed in both SDKs (`safe_request` /
`ssrfSafeFetch`); what was missing was re-pinning each hop.

## Tests

The existing suites could not express this bug: they mock both the
resolver and the HTTP client, so check and use are the same mock. The
new tests use real sockets.

- `python/tests/test_url_safety_pinning.py` — two loopback servers and a
resolver that answers the first lookup with one endpoint and every later
one with another, which is what a short-TTL rebinding record does.
Asserts the rebound endpoint receives **zero** connections, and that
`Host` still carries the hostname. Both tests fail on `next` and pass
here.
- `ts/packages/core/test/utils/pinnedDispatcher.node.test.ts` — a real
server plus a hostname under `.invalid`, which RFC 2606 guarantees never
resolves. A request that arrives proves the connect used the pinned
address and never consulted DNS. The third case shows the contrast:
unpinned, the same fetch cannot resolve at all.
- `ssrfGuard.test.ts` gains assertions that each hop is pinned to that
hop's own validated address.
- `pinnedDispatcher.node.test.ts` also pins with
`setDefaultAutoSelectFamily(false)`, which is the branch Node takes for
the single-address callback.

## Notes

- Supersedes #4157, which diagnosed this correctly. Its post-response
peer check turned out not to hold: with an HTTP/1.0 or `Connection:
close` server, urllib3 detaches the socket (`conn.sock is None`) while
`r.content` still returns the full body, so the check fails open exactly
where exfiltration succeeds. That is why the assertion here runs at
connect time instead.
- The Python package now declares `urllib3>=2` directly. `url_safety`
imports it for `NameResolutionError`, which only exists from 2.0, and
the pinning adapter reaches into 2.x connection internals; `requests`
alone allows 1.x, where `import composio` would have failed outright.
- `@composio/core` gains an `undici` dependency, pinned to `^7`: undici
8 dispatchers are rejected by the `fetch` in every Node version this
package supports (22/24/25, verified). The real-socket test runs on the
full CI matrix, so a future incompatibility fails loudly instead of
silently un-pinning.
- `undici` is imported on first pinned request rather than at module
load: importing it installs a process-wide global dispatcher, which
would have handed the host application's own unrelated `fetch` calls
this package's undici merely because it imported `@composio/core`.
- Residuals, now documented in the modules:
- Requests routed through an environment proxy keep the pre-flight check
only. The proxy resolves the hostname itself and the SDK cannot see or
pin that resolution.
- A process that does perform a pinned fetch still ends up on this
package's `Agent` if nothing had claimed the global dispatcher slot yet.
undici defines that slot non-configurable, so it cannot be handed back —
assigning `undefined` leaves the runtime's own `fetch` asserting on a
missing dispatcher.
2026-08-20 13:34:37 +02:00
sdkrelease[bot] f031c27cd1 Release: update version (#4161)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to next, this PR will
be updated.


# Releases
## @composio/core@0.17.0

### Minor Changes

- 760f8d0: Allow OpenAI and Anthropic provider tool-call helpers to
execute through a supplied Tool Router session. Session meta-tools now
retain their session context while provider argument normalization
remains intact; existing user-ID calls continue to use direct execution.
Anthropic helper failures now preserve their error text in `{ error }`
results without changing successful payloads. Custom provider subclasses
overriding `executeToolCall` or `handleToolCalls` may require updates
because these methods now accept session targets.

### Patch Changes

- 6ba9179: Validate the URLs that come from API responses before
fetching them. Tool-execution downloads (`s3Url`), S3 presigned uploads
(`new_presigned_url`), Tool Router session file downloads
(`RemoteFile.buffer()` / `blob()` / `text()` / `save()`) and session
file uploads (`upload_url`) now go through the same SSRF guard that
already covered user-supplied URLs, so a response naming a private,
loopback, or link-local address is refused instead of fetched. Redirect
hops are re-validated. Edge runtimes keep their current behavior:
session file transfers are not blocked there, since a Worker cannot
resolve DNS to check and its `fetch` does not originate inside the
caller's network.
## @composio/anthropic@0.11.0

### Minor Changes

- 760f8d0: Allow OpenAI and Anthropic provider tool-call helpers to
execute through a supplied Tool Router session. Session meta-tools now
retain their session context while provider argument normalization
remains intact; existing user-ID calls continue to use direct execution.
Anthropic helper failures now preserve their error text in `{ error }`
results without changing successful payloads. Custom provider subclasses
overriding `executeToolCall` or `handleToolCalls` may require updates
because these methods now accept session targets.
## @composio/openai@0.12.0

### Minor Changes

- 760f8d0: Allow OpenAI and Anthropic provider tool-call helpers to
execute through a supplied Tool Router session. Session meta-tools now
retain their session context while provider argument normalization
remains intact; existing user-ID calls continue to use direct execution.
Anthropic helper failures now preserve their error text in `{ error }`
results without changing successful payloads. Custom provider subclasses
overriding `executeToolCall` or `handleToolCalls` may require updates
because these methods now accept session targets.
## @composio/slim@0.17.0

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-19 00:37:19 +02:00
Soumya Medapati 760f8d0367 fix(sdk): route provider tool calls through sessions (#4098)
## Problem

Provider tool-call helpers always used the globally injected direct
`Tools.execute` function. When a model received tools from
`session.tools()`, calling `handleToolCalls` or `handle_tool_calls`
therefore discarded the Tool Router session context and caused session
meta-tools such as `COMPOSIO_SEARCH_TOOLS` to fail.

Calling `session.execute()` manually preserved the session, but bypassed
provider behavior such as Anthropic input normalization and schema-alias
restoration.

## Root fix

- Add an explicit execution target to the non-agentic provider helpers:
- TypeScript: `handleToolCalls(session, response)` and
`executeToolCall(session, call)`
- Python: `handle_tool_calls(response=response, session=session)` and
`execute_tool_call(tool_call=call, session=session)`
- Route normalized provider arguments through the supplied Tool Router
session.
- Map session responses back to each helper's existing result shape.
- Keep provider-specific normalization before execution, including
Anthropic schema-alias restoration.
- Reject direct-only options and modifiers when the selected target is a
session, including plain JavaScript calls that bypass the TypeScript
overloads.
- Update OpenAI and Anthropic examples to use the session-aware helpers.
- Harden the docs policy test so setup and execution split across fences
in one sample are still detected.

## Docs review follow-ups

- Reword the concepts-page prohibition so it forbids user-ID-bound
helper calls, not the helpers themselves, matching the provider pages in
this PR.
- Add minimum-version callouts to the OpenAI and Anthropic provider
pages (Python `composio` newer than 0.19.0; TypeScript `@composio/core`
≥ 0.17.0 with `@composio/openai` ≥ 0.12.0 / `@composio/anthropic` ≥
0.11.0), pointing older versions at `session.execute()`.
- Bump `docs/package.json` to `@composio/core` `^0.15.0` and
`@composio/openai` `^0.11.0` (the published majors at the time of the
bump; `@composio/core` 0.16.0 and `composio` 0.19.0 have since released
from `next` without this PR, so its changeset will publish core 0.17.0
and the next Python minor) and annotate each `@errors: 2345` Twoslash
marker with a TODO naming the minor version that retires it; since this
changeset releases minors, all three pins need a manual range bump to
retire the markers. This version of twoslash only throws on *unlisted*
errors, so a stale marker cannot break the build — it would only mask
future TS2345s, which the TODOs now track.
- Update `SESSION_GUARDRAILS` (the block appended to `.md` responses for
agents): add a session-execution bullet (scoped to the OpenAI and
Anthropic helpers, with `session.execute()` for every other provider)
and qualify the direct-execution list with "with a user ID". The
session-execution static test now scans the guardrail blocks like the
execute-version test already did.
- Tighten the docs detector: the Python branch is bounded to the helper
call's argument list (tolerating one level of nested calls) instead of
running past the closing paren, and the TypeScript branch catches whole
user-ID identifiers (`userId`, `user_id`, `uid`) without flagging
session variables like `userSession` — each edge has a regression test.
- Note on the Google provider page that its `executeToolCall` is not
session-aware yet.

## Compatibility and release

Existing user-ID calls remain unchanged and continue to use direct tool
execution. The new session call forms are additive.

The changeset applies minor releases to `@composio/core`,
`@composio/openai`, and `@composio/anthropic` — the new session
overloads are a type-level break for provider subclasses, so patch was
too small. The configured fixed group also includes `@composio/slim`.

The docs site intentionally checks examples against currently published
SDK declarations. The three new TypeScript calls therefore carry exact
Twoslash `TS2345` release-skew annotations; remove them (per the inline
TODOs) once `docs/package.json` picks up `@composio/core` ≥ 0.17.0,
`@composio/openai` ≥ 0.12.0, and `@composio/anthropic` ≥ 0.11.0.

## Verification

- `@composio/core`: 1,061 tests passed; typecheck passed
- `@composio/openai`: 34 tests passed; typecheck passed
- `@composio/anthropic`: 53 tests passed; typecheck passed
- Python provider and aliasing suites: 40 passed, 4 skipped
- Focused Python mypy and Ruff checks passed
- Docs static suite: 208 tests passed (including the new guardrail-scan
and detector cases)
- Docs production build passed with the bumped `@composio/core` 0.15.0 /
`@composio/openai` 0.11.0, including Twoslash, TypeScript, and all
generated pages
- Docs lint passed; lint reports only existing warnings
- Changeset status reports the expected minor packages

---------

Co-authored-by: Soumya Medapati <soumyamedapati@soumyas-air.local.meter>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-08-18 23:55:11 +02:00
Alberto Schiabel 6ba9179b48 fix(files): validate URLs from API responses before fetching them (#4146)
This PR:

- builds on top of https://github.com/ComposioHQ/composio/pull/4144
- routes every fetch whose URL comes from an API response through the
SSRF guards that already existed — ten sinks, five per SDK: the
tool-execution download, both S3 presigned uploads,
`RemoteFile.buffer()`/`blob()`, and the Tool Router session file upload
- adds `safe_request()` (Python), which follows redirects itself and
re-validates each hop, so a validated URL cannot 302 into private space
and an S3 307 region redirect still works
- makes `RemoteFile.buffer()` (Python) share `_fetch_url_bytes` with the
user-supplied-URL path instead of duplicating it — it previously read
`response.content` with no size cap, no redirect control, and no target
validation
- adds `ssrfSafeFetchWhereSupported` (TypeScript): the full guard on
Node, a plain `fetch` on workerd, so Tool Router session file transfers
keep working in edge runtimes rather than failing closed
- documents DNS rebinding as a known residual in both guards
- adds tests that assert the guard *runs* — blocked URL, sink never
called, nothing written — rather than that a transfer succeeds

## Context

`python/AGENTS.md` states the trust boundary: every field of an API
response is untrusted input, because the backend may be compromised or
the connection MITM'd. Both SDKs enforced that for URLs a *user* passes
in (`composio.utils.url_safety`, `ssrfGuard.node.ts`) and left the URLs
an API *response* supplies unguarded — backwards relative to the stated
model. A response naming an internal address turned the SDK into a
request proxy for it, and the fetched bytes were written to disk or
returned to the caller, typically into an LLM context.

`RemoteFile.buffer()` was the weakest of the ten: four lines above it,
`_fetch_from_url` validated its target, refused redirects, and streamed
against a 100 MiB cap; `buffer()` did none of the three. The only
difference between them was which side of the trust boundary the URL
arrived from.

Three decisions worth review:

- **The guard is unconditional.** Presigned URLs are public, so nothing
legitimate should resolve to private space. There is no escape hatch,
and no new configuration surface.
- **The tool-execution download stays uncapped.** It streams straight to
disk, so `_MAX_RESPONSE_SIZE` — a memory-exhaustion bound — does not
apply, and tool attachments legitimately exceed it.
`RemoteFile.buffer()` *is* capped, because it buffers in memory.
- **Uploads follow redirects with re-validation rather than refusing
them**, since S3 can answer a PUT with a 307 region redirect. Downloads
keep `allow_redirects=False`, matching the existing fetch paths.

Python now matches the TypeScript guard's per-hop re-validation, which
was the better of the two implementations.

Verified locally: `make chk` and `make tst` clean on the Python side;
`pnpm -C packages/core test` 1095 passed, typecheck and lint clean.
2026-08-18 13:25:01 +02:00
Rohit c0f16093ed fix(errors): set this.name on three ComposioError subclasses
ComposioToolVersionRequiredError, JsonSchemaToZodError, and
JsonSchemaRefResolutionError omitted the this.name assignment their sibling
subclasses perform, so instances inherited the base class field value and
reported name 'ComposioError'. error.name is forwarded to error telemetry,
which mis-grouped these three distinct error types under the base name.
2026-08-13 14:19:49 +05:30
Rohit 9545806a62 fix(telemetry): bound telemetry requests with an AbortSignal timeout
sendMetric and sendErrorLog awaited fetch() with no timeout, so a stalled
telemetry endpoint could leave an awaited SDK call pending indefinitely.
Bound both best-effort requests with an AbortController and a cleared timer
via a shared postWithTimeout helper, mirroring the npm version-check bound in
utils/version.ts. The existing catch still swallows the abort, preserving the
never-throws contract.
2026-08-13 13:42:01 +05:30
sdkrelease[bot] 02196de0b5 Release: update version (#4104)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to next, this PR will
be updated.


# Releases
## @composio/core@0.16.0

### Minor Changes

- 5e57815: Keep free-form object roots, `patternProperties`, and
`additionalProperties` when parsing a tool schema.

`ToolSchema.parse` used to reject a bare `{ "type": "object" }` root,
drop root `patternProperties` as an unknown key, and reject a root
`additionalProperties` written as a schema instead of a boolean.
Free-form roots now parse successfully, and both constraints survive
parsing exactly as written. The public `ToolSchema` type now makes
`properties` optional to reflect those valid property-less object
schemas.

This matters downstream. Every provider reads `inputParameters` after
parsing, so a tool that declares dynamic keys had those rules stripped
before the model ever saw them.

An omitted `additionalProperties` still stays omitted. The parser does
not invent a value, because each converter decides its own default.

  **What no longer works**

Parsing no longer fails on a schema-valued root `additionalProperties`.

  ```ts
  const tool = ToolSchema.parse({
    slug: 'MY_TOOL',
    inputParameters: {
      type: 'object',
      properties: { name: { type: 'string' } },
      additionalProperties: { type: 'number' },
    },
    // ...
  });

  // before: parsing failed, because only a boolean was accepted
// now: tool.inputParameters.additionalProperties is { type: 'number' }
  ```

  **What to do instead**

Nothing, if you only ever passed boolean values. That case is unchanged.

If your code assumed `inputParameters` never carries
`patternProperties`, or that `additionalProperties` is always a boolean,
widen that assumption. Both keywords can now appear, and
`additionalProperties` can be a boolean or a schema object.

### Patch Changes

- a2f6b96: Add `type: "object"` to nested JSON Schema nodes that carry
`properties` without an explicit type, so tool schemas work with strict
OpenAPI 3.0 consumers like Google Gemini.
- c625edc: Reuse fetched tool schemas when provider-wrapped tools
execute to avoid a redundant retrieval request.
- Updated dependencies [5e57815]
  - @composio/json-schema-to-zod@0.3.0
## @composio/json-schema-to-zod@0.3.0

### Minor Changes

- 5e57815: Keep the content of free-form object arguments, and apply
dynamic key rules only where they belong.

An object schema with no named properties, written as `{ "type":
"object" }` or `{ "properties": {} }`, used to become a strict empty
object. The only value that passed was `{}`. Any real payload was
rejected. Such an object is now open, so arbitrary content is accepted
and preserved. This works at the root, nested inside another object, and
inside array items.

Dynamic keys are now routed to the schemas that actually claim them.
Every matching `patternProperties` entry validates its key. A
schema-valued `additionalProperties` applies only to keys that no
declared property and no pattern matches. Before this change a key could
pass by satisfying an unrelated pattern instead of its own.

  **What no longer works**

Converting a property-less object no longer gives you a schema that
rejects everything.

  ```ts
  const schema = jsonSchemaToZod({ type: 'object' });

  schema.parse({ anything: 1 });
  // before: threw, because the result was z.object({}).strict()
  // now:    returns { anything: 1 }
  ```

  **What to do instead**

If you want a property-less object to stay closed, say so in the JSON
Schema:

  ```ts
const schema = jsonSchemaToZod({ type: 'object', additionalProperties:
false });

  schema.parse({ anything: 1 }); // throws, as before
  ```

Objects that name at least one property are unchanged. They still reject
unknown keys when `additionalProperties` is omitted.
## @composio/claude-agent-sdk@0.11.0

### Minor Changes

- ac6bbab: Register each tool with its complete schema, so root rules
reach the Claude Agent SDK.

The provider used to register a raw property shape. A raw shape is only
the map of named properties, so it cannot carry any root rule.
`additionalProperties` and `patternProperties` were dropped before the
SDK saw them.

Two things went wrong because of that. Free-form object arguments lost
their content. An argument the tool never declared was quietly removed,
and the tool ran anyway.

  Both are fixed. The provider now registers the whole object schema.

  **What no longer works**

  An undeclared argument no longer passes silently.

  ```ts
// The tool declares `to` and nothing else. The model also sends
`hallucinated`.
  { to: 'someone@example.com', hallucinated: 'value' }

// before: `hallucinated` was stripped, and the tool ran with { to:
'...' }
// now: the caller receives an error result, and the tool does not run
  ```

Tools with no input parameters behave the same way. They stay closed and
reject every argument.

  **What to do instead**

If the model should be allowed to send extra keys, say so in the tool
schema:

  ```json
  {
    "type": "object",
    "properties": { "to": { "type": "string" } },
    "additionalProperties": true
  }
  ```

If an argument is one the tool really accepts, declare it in
`properties`. Rejection is usually the better outcome, because the agent
sees the error and can correct itself instead of running with a silently
dropped argument.

This brings the provider in line with `@composio/vercel`,
`@composio/langchain`, and `@composio/llamaindex`, which already
registered complete schemas.
## @composio/google@0.10.2

### Patch Changes

- a2f6b96: Add missing object types to nested Google GenAI tool schemas
so Gemini accepts function declarations with property maps. This release
requires `@composio/core >=0.16.0 <1.0.0` for the shared schema
normalizer.
## @composio/slim@0.16.0

### Patch Changes

- Updated dependencies [5e57815]
  - @composio/json-schema-to-zod@0.3.0

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-11 10:54:21 +02:00
sergioperezcheco a2f6b9699b fix(json-schema): integrate Google object type normalization
Co-authored-by: jkomyno <alberto@composio.dev>
2026-08-10 22:01:09 +02:00
Alberto Schiabel 1af694baee Merge branch 'next' into fix/cross-sdk-json-schema-object-conversion 2026-08-10 21:23:10 +02:00
Alberto Schiabel c625edc0f7 fix(core): reuse fetched schemas for provider execution (#4103)
Reuse fetched raw tool schemas for provider-wrapped execution while preserving modifier isolation, runtime validation, and unknown-slug fallback behavior.

Refresh the Python 3.12.13 mise lock entries required by the Audit freshness gate.

Co-authored-by: breezeFur <voidexl@outlook.com>
2026-08-10 13:34:20 +02:00
sdkrelease[bot] 13cba53b1d Release: update version (#4038)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to next, this PR will
be updated.


# Releases
## @composio/core@0.15.0

### Minor Changes

- 1503786: Replace the loose JSON Schema property type with a recursive,
type-safe definition.

`JSONSchemaProperty` (re-exported from `@composio/core` and reachable
through
`Tool.input_parameters` / `Tool.output_parameters`) is now a concrete
recursive
interface instead of effectively `any`. Runtime behavior is unchanged,
but
  consumer code that indexed into it without narrowing (for example
`schema.properties.foo.type` or `schema.default.someField`) may see new
type
errors: `properties` entries are now possibly `undefined` and `default`
/
`enum` values are `unknown`. Narrow with optional chaining or explicit
type
  guards when upgrading.

### Patch Changes

- 2ac6ad3: Bound the background npm version check so registry outages
cannot leave the request pending indefinitely.
- 5105612: Match sensitive upload path segments using the target
filesystem's actual case sensitivity so case-insensitive mounts cannot
bypass the denylist without over-blocking distinct paths on
case-sensitive mounts.
- 051c8c5: Redact secrets that appear inside JSON payloads in telemetry
error text. The key/value rule required the separator to follow the key
name directly, so a serialized body such as `{"api_key": "..."}` — the
shape error messages usually carry — was sent unredacted.
- e5c9ada: Refresh the OpenAI runtime dependency to version 7.
- ecd0861: Release unread response bodies on the paths the SDK knowingly
abandons: cancel every intermediate redirect body in `ssrfSafeFetch`,
and the response body before throwing on `!response.ok` in both
URL-upload call sites, instead of leaving them for the garbage collector
to reclaim.
- 2a6a051: Remove the unused internal `isNewerVersion` helper.
- Updated dependencies [1503786]
  - @composio/json-schema-to-zod@0.2.2
## @composio/openai@0.11.0

### Minor Changes

- e5c9ada: Add support for OpenAI versions 6 and 7.
## @composio/experimental@0.2.2

### Patch Changes

- 4ceaede: Refresh the TypeBox runtime dependency.
## @composio/json-schema-to-zod@0.2.2

### Patch Changes

- 1503786: Model the parser-supported `min`, `max`, and `example` JSON
Schema extensions in the exported recursive schema type.
## @composio/slim@0.15.0

### Patch Changes

- e5c9ada: Refresh the OpenAI runtime dependency to version 7.
- Updated dependencies [1503786]
  - @composio/json-schema-to-zod@0.2.2

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-07 22:16:46 +05:30
jkomyno 3fef432dc0 fix: fail closed on invalid dynamic schemas 2026-08-07 21:03:08 +05:30
LHMQ878 051c8c5357 fix(telemetry): redact secrets inside JSON payloads (#4043)
## Summary

Telemetry error text is redacted before it leaves the process, but the
key/value rule only matches when the separator follows the key name
directly:

```
\b(authorization|api[-_]?key|...|pwd)\b(\s*[:=]+\s*)(["']?)([^\s"',}&]+)\3
```

In JSON — and in a Python `dict` repr — the key's own closing quote sits
between the name and the colon, so `{"api_key": "..."}` never matches
and the value is sent verbatim. That is the shape error messages usually
carry: an API error envelope, a rejected request body echoed back, or an
f-string interpolating a config dict. Both SDKs share the pattern and
both are affected.

The fix moves the optional quote into the separator group
(`(["']?\s*[:=]+\s*)`). The key name, separator, and quoting are all
preserved in the output; only the value is replaced.

Measured with `redact_sensitive_text` / `redactSensitiveText` directly,
before and after:

| input | before | after |
|---|---|---|
| `api_key=sk-1` | redacted | redacted |
| `api_key: "sk-1"` | redacted | redacted |
| `x-api-key: sk-live-hdr` | redacted | redacted |
| `{"api_key": "sk-live-abc"}` | **leaks** | `{"api_key": "[REDACTED]"}`
|
| `{"api_key":"sk-live-abc"}` | **leaks** | redacted |
| `{"api_key" : "sk-live-abc"}` | **leaks** | redacted |
| `{"refresh_token":"rt-abc.def-123"}` | **leaks** | redacted |
| `{"x-api-key":"sk-hdr","user":"bob"}` | **leaks** | redacted,
`"user":"bob"` kept |
| `{'client_secret': 'cs-live-abc'}` | **leaks** | redacted |
| `the password field is required` | untouched | untouched |
| `no separator here "api_key" and nothing else` | untouched | untouched
|

The end-to-end path in Python is `composio/core/models/base.py:60-61`,
which passes `str(e)` and `traceback.format_exc()` through the redactor
into the telemetry `error` field. A tool failure whose message
interpolates the rejected request body — `Error executing tool: request
body was rejected: {"toolkit": "GMAIL", "arguments": {"api_key": "...",
...}}` — leaked the customer's key today.

## Changes

- `python/composio/utils/redaction.py`,
`ts/packages/core/src/telemetry/redact.ts`: allow a quote between the
key name and the separator.
- Tests on both sides for JSON, minified JSON, spaced-colon JSON, dict
repr, the realistic serialized-payload shape, key/quote preservation,
and no-false-positive cases.
- Changeset for `@composio/core`.

## Type of change

- [x] Bug fix

## How Has This Been Tested?

Node 24.13.0 / pnpm 11.8.0 / Python 3.10.20 (uv), Windows.

- `uv run pytest tests/test_redaction.py` — 13 passed. With
`redaction.py` reverted and the new tests kept, 9 of them fail; the 4
that still pass are the pre-existing test plus the three
no-false-positive controls.
- `npx vitest run test/telemetry/redact.test.ts` in `ts/packages/core` —
10 passed. With `redact.ts` reverted, the 3 new cases fail.
- Full Python suite: 913 passed, same 4 failures as on `next` unchanged
(3 are `ModuleNotFoundError: composio_langchain` from providers not
installed in my env; 1 is `test_empty_name_safe_fails_at_write_time`,
which expects `IsADirectoryError` but Windows raises `PermissionError`
when opening a directory). Collection 944 → 956, exactly the 12 tests
added.
- Full `@composio/core` vitest: 798 passed / 2 failed, byte-identical to
the unchanged baseline (794 passed / same 2 failed — both Windows
path-and-symlink cases). 11 test files fail to load in both runs because
I installed with `--ignore-scripts`; the repo's `preinstall` hook
doesn't run in my shell.
- `ruff format --check` and `ruff check` clean on the two Python files;
`prettier --write` applied to the two TypeScript files.

## Checklist

- [x] I have read the Code of Conduct and this PR adheres to it
- [x] I ran linters/tests locally and they passed
- [ ] I updated documentation as needed — no user-facing behavior change
to document
- [x] I added tests or explain why not applicable
- [x] I added a changeset if this change affects published packages

## Additional context

This is defence-in-depth, so it stays deliberately narrow: same denylist
of key names, same value character class, no new rules. Bare-prefix
secrets (`sk-...`, `ghp_...`) and PEM blocks are still not matched by
either SDK — worth a separate look, but widening the pattern is a
different risk trade-off than closing a shape the rule already intends
to cover.
2026-08-07 19:14:49 +05:30
jkomyno 8d4bb3bf7e chore: merge next into json schema conversion fix 2026-08-07 18:44:34 +05:30
jkomyno 4a89ff112c fix(json-schema): enforce dynamic object contracts 2026-08-07 00:06:55 +05:30
jkomyno 063f03a72f docs(changesets): explain each behavior change with a before and after 2026-08-06 22:50:46 +05:30
jkomyno ac6bbabd31 fix(claude-agent-sdk): register complete tool schemas
The provider registered tools through jsonSchemaToZodShape, which returns only
the per-property map. Root-level constraints -- `additionalProperties`, boolean
or schema-valued, and `patternProperties` -- are structurally unrepresentable in
a raw shape, so they were dropped before the Claude Agent SDK ever saw them:
free-form object arguments lost their content, and an unknown key was silently
stripped from tool input rather than rejected.

Registering the complete object schema fixes both. A tool with no input
parameters keeps its closed root explicitly, since a bare `properties: {}` is
now an open object.

Proven at the real SDK boundary rather than against a mocked `tool()`: the new
suite drives createSdkMcpServer over an in-memory MCP transport and asserts on
the advertised inputSchema and on real tools/call results.
2026-08-06 21:59:12 +05:30
jkomyno cb6fe9bf64 chore(changesets): release the schema conversion fixes as minor bumps
Python's unknown-key rejection changes behavior for the langchain, langgraph,
and crewai providers rather than only fixing a defect, so the release carries a
minor bump across both SDKs instead of a patch.
2026-08-06 21:00:15 +05:30
jkomyno 5e57815af1 fix(json-schema): accept and preserve dynamic object content across converters
Property-less objects (`{ type: "object" }`, `properties: {}`) were being
collapsed to a strict empty object by the Zod converter and given an implicit
`additionalProperties: false` by the Effect converter, so valid free-form
payloads such as METABASE_POST_API_CARD.dataset_query were rejected at the CLI
boundary. `ToolSchema.parse` separately dropped root `patternProperties` and
rejected a schema-valued root `additionalProperties`.

Dynamic keys are now routed to exactly the schemas that apply to them, and the
shared acceptance contract lives in one checked-in corpus with byte-identical
TypeScript and Python copies.
2026-08-06 20:33:53 +05:30
Alberto Schiabel ecd0861741 fix(core): release unread response bodies (#4078)
This PR:
- follows [Undici's
recommendation](https://github.com/nodejs/undici#garbage-collection) to
consume or cancel unread response bodies instead of leaving
connection-resource cleanup to the garbage collector, which can increase
connection pressure and reduce reuse
- cancels every intermediate redirect body in `ssrfSafeFetch` before
validating and following the next hop
- cancels non-success response bodies in both URL-upload callers before
preserving their existing errors
- adds regression coverage for redirect ordering, redirect-budget
exhaustion, and both caller error paths
- adds a patch changeset for `@composio/core`
- supersedes https://github.com/ComposioHQ/composio/pull/4018 and
credits @pacocartones as co-author

## Verification

- `pnpm --filter @composio/core test ssrfGuard fileUtils
ToolRouterSessionFilesMount` (4 files, 58 tests passed)
- `pnpm --filter @composio/core test` (47 files, 1,049 tests passed)
- `pnpm --filter @composio/core typecheck`
- `pnpm lint:packages` (0 errors; existing warnings only)
- Prettier check on all touched files
- `pnpm validate:changesets`

Co-authored-by: pacocartones <pacocartones@users.noreply.github.com>
2026-08-06 17:52:02 +05:30