Commit Graph

455 Commits

Author SHA1 Message Date
Alberto Schiabel 8086ef02cb fix(examples): align Python and TypeScript examples with current backend (#4107)
## Summary

- Repairs runnable TypeScript and Python examples for current backend
requirements, including authenticated MCP endpoints, current transports,
valid tool identifiers, provider limits, and resource uniqueness.
- Replaces placeholder resource IDs with explicit `COMPOSIO_EXAMPLES_*`
configuration and makes failed examples exit loudly.
- Adds `scripts/examples-provision.mjs` as an idempotent provisioning
check for a disposable examples project.

## Scope

- This PR no longer changes the Python SDK runtime or generated-client
dependency.
- Python remains pinned to the published `composio-client==1.43.0` in
`pyproject.toml`, `setup.py`, and `uv.lock`.
- The owned 2.x client integration is deferred until that client
completes its release guarantees and is explicitly published.
- The scheduled live workflow and manifest remain deferred until their
runner is tracked.

## Verification

- `make chk`
- `make tst` (`927 passed, 33 skipped`)
- Hosted checks rerun against commit `12691aca7`.
2026-08-11 19:45:58 +02:00
Alberto Schiabel 26ec42a38c chore(py): prepare 0.19.0 release (#4114)
This PR:

- builds on top of https://github.com/ComposioHQ/composio/pull/4094
- bumps `composio` and all 12 provider distributions from `0.18.2` to
`0.19.0`
- keeps `python/composio/__version__.py` and `uv.lock` aligned with
package metadata
- documents recursive provider argument-presence preservation in the
existing cross-SDK changelog
- passes the release-workflow guard, Ruff, mypy, provider type
inference, and 1,167 Python tests
- builds and validates 26 wheel/sdist artifacts with Twine
- must be retargeted to `next` after #4094 merges
2026-08-11 10:44:44 +02:00
jkomyno 64fbc4ee03 fix(py): preserve nested provider argument presence 2026-08-10 22:14:21 +02:00
Alberto Schiabel 82e25a603e Merge branch 'fix/cross-sdk-json-schema-object-conversion' into fix/python-provider-argument-serialization 2026-08-10 21:23:35 +02:00
Alberto Schiabel 1af694baee Merge branch 'next' into fix/cross-sdk-json-schema-object-conversion 2026-08-10 21:23:10 +02:00
jkomyno cec99e7a32 test(py): run provider serialization coverage in CI 2026-08-10 20:02:23 +02:00
jkomyno 5bb1e20518 fix(py): preserve serialized provider arguments
Co-authored-by: Aditya Chawla <aditya17.mitmpl2023@learner.manipal.edu>
2026-08-10 20:02:23 +02:00
jkomyno 191d919eef fix(python): harden dynamic schema validation 2026-08-07 21:56:39 +05:30
Alberto Schiabel c337e5696e chore(sdk): prepare Python 0.18.2 and TypeScript 0.15.0 releases (#4095)
Prepares the next SDK train: Python `composio` `0.18.2` and the
TypeScript packages that the pending Changesets will publish
(`@composio/core` and `@composio/slim` `0.15.0`, `@composio/openai`
`0.11.0`, `@composio/experimental` and `@composio/json-schema-to-zod`
`0.2.2`).

No CLI release is part of this train.

## What this PR does

- bumps Python core and all 13 provider packages to `0.18.2`, including
`python/composio/__version__.py` and the root plus per-provider
`uv.lock` files
- adds `docs/content/changelog/08-07-26-sdk-releases.mdx`, the combined
customer-facing changelog for both SDKs
- documents `@composio/core` `0.15.0` in that entry so the generated
release PR passes the release-workflow guard
- leaves the pending Changesets untouched; the generated release PR
remains responsible for npm versioning and publication

## Release sequence

1. Merge this PR.
2. Let Changesets regenerate the "Release: update version" PR, wait for
it to go green, then merge it. Merging publishes the TypeScript
packages.
3. Tag the resulting `next` commit `py@0.18.2` to publish the Python
packages to PyPI.

## Notes

- The root `uv.lock` diff carries ~28 lines of marker-annotation churn
beyond the version bumps. That is the repo-pinned `uv` `0.8.19`
normalising markers the committed lockfile had recorded differently; no
dependency versions change.
- `@composio/mastra` and the TypeScript providers are unchanged: their
declared peer ranges still accept `@composio/core` `0.15.0`, so
Changesets does not bump them.

## Verification

- `pnpm test:release-workflow` — passes (Python package, runtime, and
provider versions agree; both SDK versions documented in the changelog)
- `uv run pytest tests/` in `python/` — 925 passed, 35 skipped
- `uv run --package composio python -c "import composio"` — reports
`0.18.2`
- `bun test tests/static/` in `docs/` — 195 pass, 1 pre-existing failure
(`getLLMText — version pointer`, also fails on a clean tree)
- `bun run types:check` in `docs/` — MDX generation and route typegen
succeed; the final `tsc` step could not run locally because
`typescript-7` is absent from `docs/node_modules`
2026-08-07 21:35:55 +05:30
jkomyno 3fef432dc0 fix: fail closed on invalid dynamic schemas 2026-08-07 21:03:08 +05:30
jkomyno 1f94167595 fix(python): walk only schema positions when checking dynamic refs
The dynamic-key reference walker descended into every nested dict, so a
$ref-shaped value stored under const, default, enum, or examples was
treated as a schema reference and raised while the tool was wrapped.
Restrict the walk to Draft 7 schema positions.
2026-08-07 19:39:06 +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 4f009baa69 fix(python): resolve nested dynamic refs from document root 2026-08-07 19:11:18 +05:30
jkomyno 4a89ff112c fix(json-schema): enforce dynamic object contracts 2026-08-07 00:06:55 +05:30
jkomyno bbeacac653 fix(python): preserve and police dynamic object content in schema conversion
`json_schema_to_model` and `json_schema_to_pydantic_type` now share one
compiled object policy, so provider-facing Pydantic models stop silently
discarding valid free-form arguments and stop ignoring `patternProperties` and
explicit `additionalProperties` controls. Accepted dynamic keys are written back
into `__pydantic_extra__`, which is what `LangchainProvider.wrap_tool` re-reads
with `getattr`.

Acceptance and rejection are asserted from the shared cross-SDK corpus, so the
Python entry points cannot diverge from the TypeScript converters.
2026-08-06 20:40:59 +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
dependabot[bot] f286ca98ac chore(deps): bump the uv group across 2 directories with 1 update (#4052)
Bumps the uv group with 1 update in the / directory:
[cryptography](https://github.com/pyca/cryptography).
Bumps the uv group with 1 update in the
/python/providers/claude_agent_sdk directory:
[cryptography](https://github.com/pyca/cryptography).

Updates `cryptography` from 49.0.0 to 50.0.0
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst">cryptography's
changelog</a>.</em></p>
<blockquote>
<p>50.0.0 - 2026-07-31</p>
<pre><code>
* **SECURITY ISSUE**:

:func:`~cryptography.hazmat.primitives.serialization.pkcs7.pkcs7_decrypt_der`
and its PEM and S/MIME variants no longer expose distinguishable errors
or
timing when unwrapping a ``RecipientInfo``'s ``encryptedKey``, which
could
act as a Bleichenbacher oracle for callers that decrypt untrusted
messages.
A random key is now substituted on failure, as described in :rfc:`3218`.
  Credit to **@X1AOxiang** for reporting the issue. **CVE-2026-69247**
* Deprecated Diffie-Hellman key exchange over finite fields (FFDH).
  Everything FFDH is deprecated, including the types in
``cryptography.hazmat.primitives.asymmetric.dh`` and loading FFDH keys
or
  parameters with the key loading APIs. Users should migrate to a more
  modern key exchange algorithm.
* Added ``xof()`` class methods to
  :class:`~cryptography.hazmat.primitives.hashes.SHAKE128` and
:class:`~cryptography.hazmat.primitives.hashes.SHAKE256` for
constructing
  algorithm instances configured for use with
  :class:`~cryptography.hazmat.primitives.hashes.XOFHash`.
* The :mod:`X.509 verification &lt;cryptography.x509.verification&gt;`
APIs are now
  considered stable and are subject to our API stability policy.
* Added the :doc:`/cobblestone` recipe, an implementation of the
  Cobblestone-128 and Cobblestone-256 instantiations of the `C2SP
  chunked-encryption specification
&lt;https://c2sp.org/chunked-encryption&gt;`_ for streaming
authenticated
  encryption of large messages.
* Parsing a Signed Certificate Timestamp list now rejects encodings that
carry trailing bytes after the list or after an individual SCT, instead
of
  silently ignoring them.
* Added support for using :class:`~cryptography.x509.Name` as a field
type in
  the :doc:`/hazmat/asn1/index` module.
* Loading a public key or an EC private key now rejects DER where the
``subjectPublicKey`` (or EC ``publicKey``) ``BIT STRING`` declares a
non-zero
  number of unused bits, instead of silently ignoring it.
* Parsing a CRL entry's ``InvalidityDate`` extension now rejects a
``GeneralizedTime`` that carries fractional seconds or another non-DER
form,
matching the strict encoding already required for every other X.509 time
  field.
* :func:`~cryptography.x509.ocsp.load_der_ocsp_request` and
:func:`~cryptography.x509.ocsp.load_der_ocsp_response` now reject a
request
or response whose ``version`` field is not ``v1``, the only version
defined
by RFC 6960, matching the version validation already performed when
loading
  certificates, CSRs and CRLs.
* :class:`~cryptography.hazmat.primitives.hashes.XOFHash` is now
supported
  when building against AWS-LC.
* HMAC (and therefore PBKDF2-HMAC) with SHA-3 hashes is now supported
when
  building against AWS-LC.
* Diffie-Hellman (:doc:`/hazmat/primitives/asymmetric/dh`) is now
supported
  when building against AWS-LC.
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pyca/cryptography/commit/dcb7050b807b00392fa9fe2eac7cb362fcf355cc"><code>dcb7050</code></a>
Prepare for 50.0.0 release (<a
href="https://redirect.github.com/pyca/cryptography/issues/15372">#15372</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f"><code>53fccd9</code></a>
Don't leak how PKCS#7 encryptedKey decryption failed (<a
href="https://redirect.github.com/pyca/cryptography/issues/15369">#15369</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/d472f978470fbefa521b86d98b2ecccbbb4d1dd8"><code>d472f97</code></a>
Add <code>from __future__ import annotations</code> to all src/ Python
files (<a
href="https://redirect.github.com/pyca/cryptography/issues/15371">#15371</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/908773d53829fb1466c6db364b31321c3cd8eb9a"><code>908773d</code></a>
Bump downstream dependencies in CI (<a
href="https://redirect.github.com/pyca/cryptography/issues/15368">#15368</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/2cc07cc948948211899bcb0cddd1fddf86e95812"><code>2cc07cc</code></a>
Bump BoringSSL, OpenSSL, AWS-LC in CI (<a
href="https://redirect.github.com/pyca/cryptography/issues/15367">#15367</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/c94ede9f040fa44942f7139772603419000acf66"><code>c94ede9</code></a>
chore(deps): bump ruff from 0.16.0 to 0.16.1 (<a
href="https://redirect.github.com/pyca/cryptography/issues/15366">#15366</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/67a8308dc9ea4cce6056e0f1438f903c208c3f35"><code>67a8308</code></a>
chore(deps): bump virtualenv from 21.7.0 to 21.7.1 (<a
href="https://redirect.github.com/pyca/cryptography/issues/15365">#15365</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/95018ffcdbbc510fd92fc872e3a3e80aa6e58596"><code>95018ff</code></a>
Release the GIL in one-shot AEAD encrypt/decrypt (<a
href="https://redirect.github.com/pyca/cryptography/issues/15361">#15361</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/6954733eaf55a0074abf88f06f7242dfca3a5d02"><code>6954733</code></a>
Release the GIL during DH and DSA parameter generation (<a
href="https://redirect.github.com/pyca/cryptography/issues/15364">#15364</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/6893b94c33e948f6240082461424cfb5da2dacc6"><code>6893b94</code></a>
Import _serialization instead of serialization in x509/extensions (<a
href="https://redirect.github.com/pyca/cryptography/issues/15363">#15363</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pyca/cryptography/compare/49.0.0...50.0.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `cryptography` from 49.0.0 to 50.0.0
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst">cryptography's
changelog</a>.</em></p>
<blockquote>
<p>50.0.0 - 2026-07-31</p>
<pre><code>
* **SECURITY ISSUE**:

:func:`~cryptography.hazmat.primitives.serialization.pkcs7.pkcs7_decrypt_der`
and its PEM and S/MIME variants no longer expose distinguishable errors
or
timing when unwrapping a ``RecipientInfo``'s ``encryptedKey``, which
could
act as a Bleichenbacher oracle for callers that decrypt untrusted
messages.
A random key is now substituted on failure, as described in :rfc:`3218`.
  Credit to **@X1AOxiang** for reporting the issue. **CVE-2026-69247**
* Deprecated Diffie-Hellman key exchange over finite fields (FFDH).
  Everything FFDH is deprecated, including the types in
``cryptography.hazmat.primitives.asymmetric.dh`` and loading FFDH keys
or
  parameters with the key loading APIs. Users should migrate to a more
  modern key exchange algorithm.
* Added ``xof()`` class methods to
  :class:`~cryptography.hazmat.primitives.hashes.SHAKE128` and
:class:`~cryptography.hazmat.primitives.hashes.SHAKE256` for
constructing
  algorithm instances configured for use with
  :class:`~cryptography.hazmat.primitives.hashes.XOFHash`.
* The :mod:`X.509 verification &lt;cryptography.x509.verification&gt;`
APIs are now
  considered stable and are subject to our API stability policy.
* Added the :doc:`/cobblestone` recipe, an implementation of the
  Cobblestone-128 and Cobblestone-256 instantiations of the `C2SP
  chunked-encryption specification
&lt;https://c2sp.org/chunked-encryption&gt;`_ for streaming
authenticated
  encryption of large messages.
* Parsing a Signed Certificate Timestamp list now rejects encodings that
carry trailing bytes after the list or after an individual SCT, instead
of
  silently ignoring them.
* Added support for using :class:`~cryptography.x509.Name` as a field
type in
  the :doc:`/hazmat/asn1/index` module.
* Loading a public key or an EC private key now rejects DER where the
``subjectPublicKey`` (or EC ``publicKey``) ``BIT STRING`` declares a
non-zero
  number of unused bits, instead of silently ignoring it.
* Parsing a CRL entry's ``InvalidityDate`` extension now rejects a
``GeneralizedTime`` that carries fractional seconds or another non-DER
form,
matching the strict encoding already required for every other X.509 time
  field.
* :func:`~cryptography.x509.ocsp.load_der_ocsp_request` and
:func:`~cryptography.x509.ocsp.load_der_ocsp_response` now reject a
request
or response whose ``version`` field is not ``v1``, the only version
defined
by RFC 6960, matching the version validation already performed when
loading
  certificates, CSRs and CRLs.
* :class:`~cryptography.hazmat.primitives.hashes.XOFHash` is now
supported
  when building against AWS-LC.
* HMAC (and therefore PBKDF2-HMAC) with SHA-3 hashes is now supported
when
  building against AWS-LC.
* Diffie-Hellman (:doc:`/hazmat/primitives/asymmetric/dh`) is now
supported
  when building against AWS-LC.
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pyca/cryptography/commit/dcb7050b807b00392fa9fe2eac7cb362fcf355cc"><code>dcb7050</code></a>
Prepare for 50.0.0 release (<a
href="https://redirect.github.com/pyca/cryptography/issues/15372">#15372</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f"><code>53fccd9</code></a>
Don't leak how PKCS#7 encryptedKey decryption failed (<a
href="https://redirect.github.com/pyca/cryptography/issues/15369">#15369</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/d472f978470fbefa521b86d98b2ecccbbb4d1dd8"><code>d472f97</code></a>
Add <code>from __future__ import annotations</code> to all src/ Python
files (<a
href="https://redirect.github.com/pyca/cryptography/issues/15371">#15371</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/908773d53829fb1466c6db364b31321c3cd8eb9a"><code>908773d</code></a>
Bump downstream dependencies in CI (<a
href="https://redirect.github.com/pyca/cryptography/issues/15368">#15368</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/2cc07cc948948211899bcb0cddd1fddf86e95812"><code>2cc07cc</code></a>
Bump BoringSSL, OpenSSL, AWS-LC in CI (<a
href="https://redirect.github.com/pyca/cryptography/issues/15367">#15367</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/c94ede9f040fa44942f7139772603419000acf66"><code>c94ede9</code></a>
chore(deps): bump ruff from 0.16.0 to 0.16.1 (<a
href="https://redirect.github.com/pyca/cryptography/issues/15366">#15366</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/67a8308dc9ea4cce6056e0f1438f903c208c3f35"><code>67a8308</code></a>
chore(deps): bump virtualenv from 21.7.0 to 21.7.1 (<a
href="https://redirect.github.com/pyca/cryptography/issues/15365">#15365</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/95018ffcdbbc510fd92fc872e3a3e80aa6e58596"><code>95018ff</code></a>
Release the GIL in one-shot AEAD encrypt/decrypt (<a
href="https://redirect.github.com/pyca/cryptography/issues/15361">#15361</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/6954733eaf55a0074abf88f06f7242dfca3a5d02"><code>6954733</code></a>
Release the GIL during DH and DSA parameter generation (<a
href="https://redirect.github.com/pyca/cryptography/issues/15364">#15364</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/6893b94c33e948f6240082461424cfb5da2dacc6"><code>6893b94</code></a>
Import _serialization instead of serialization in x509/extensions (<a
href="https://redirect.github.com/pyca/cryptography/issues/15363">#15363</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pyca/cryptography/compare/49.0.0...50.0.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/ComposioHQ/composio/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 19:20:09 +05:30
Alberto Schiabel 0ae443bece chore(sdk): prepare Python 0.18.1 and TypeScript 0.14.1 releases (#3988)
This PR:
- prepares Python SDK core and provider packages for `0.18.1`, including
runtime and lockfile metadata
- adds the canonical combined changelog for Python `0.18.1` and the
TypeScript releases tracked by
https://github.com/ComposioHQ/composio/pull/3906
- documents `@composio/core` `0.14.1` so the generated release PR passes
the release-workflow guard
- refreshes `mise.lock` so the TypeScript audit workflow reaches the
dependency audit
- keeps the pending TypeScript Changesets untouched; the generated
release PR remains responsible for npm versioning and publication
- validates Python with Ruff/mypy, 944 tests, all 13 package builds, and
`twine check`
- validates TypeScript/docs with the full 26-task `pnpm test`,
high-severity production audit, 100 docs static tests, link checks, docs
typecheck, and production build
2026-07-30 16:03:55 +05:30
Checo a3f6b9c8f2 fix(py/triggers): swallow malformed chunked frame instead of tearing down subscription (#3892)
## Problem

TriggerSubscription._handle_chunked_events is bound directly as a Pysher
channel callback. Pysher channel dispatch has no local exception
boundary, so malformed chunk frames reached websocket-client error
handling and marked the realtime connection failed. The normal payload
parser already skips malformed frames, but the chunked path did not.

## Fix

- Validate that each decoded frame is an object with string id/chunk,
integer non-boolean index, and boolean final fields.
- Contain decoder, validation, and reassembly failures at the callback
boundary and log only a bounded frame preview.
- Clear partial state only when the frame carries a validated string id,
allowing that id to be reused safely.
- Preserve the valid chunk reassembly path and fix the Chunked docstring
typo.

## Regression coverage

Tests cover invalid JSON, non-object frames, missing fields, unhashable
ids, wrong field types, unexpected decoder failures, and successful
same-id reassembly immediately after a bad frame clears partial state.

## Verification

- nox -s tst -- tests/test_triggers.py: 83 passed
- make chk: Ruff and mypy passed

---------

Signed-off-by: Checo <sergio@checo.cc>
Co-authored-by: Checo <sergio@checo.cc>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-07-28 20:08:54 +05:30
Checo 2a2d77a764 fix(py/triggers): stop() deadlock + subscribe() timeout connection leak (#3890)
Fixes #3858 (both bugs reported there).

Bug 1 — TriggerSubscription.stop() deadlocks when called from a trigger
callback. pysher's Connection.disconnect runs socket.close() then
join(timeout) on the websocket thread, but callbacks execute on that
same thread's dispatch path (_handle_event blocks on the callback's
future.result()), so joining the thread from within a callback that runs
on it deadlocks the whole process and _alive is never cleared, so a main
thread parked in wait_forever() never exits. Fix: clear _alive
synchronously (so wait_forever unblocks immediately) and run
disconnect() on a daemon thread, breaking the reentrancy while still
closing the socket.

Bug 2 — _SubcriptionBuilder.connect() leaks a websocket thread on
timeout. The deadline path raised ComposioSDKTimeoutError without
calling pusher.disconnect(), so pysher's run loop kept redialing every
reconnect_interval for the life of the process — one leaked thread per
timed-out subscribe(), and if a leaked connection later succeeded it
would authenticate and subscribe the channel of an abandoned
TriggerSubscription, silently consuming events. Fix: wrap the wait loop
in try/except so the timeout path tears the connection down before
re-raising.

Added regression tests (TestTriggerSubscriptionStop and
TestSubscriptionBuilderConnectTimeout) that reproduce both failure modes
offline with mocked pusher connections; all 73 tests in
tests/test_triggers.py pass, ruff check + format clean.

---------

Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
Co-authored-by: jkomyno <alberto.schiabel@gmail.com>
2026-07-28 17:59:14 +05:30
manjunath bhaskar 8cb290be35 fix(schema_converter): allOf containing a false member must reject every value (#3921)
### Summary

Preserves JSON Schema boolean-schema semantics throughout the Python
schema converter:

- an unsatisfiable `allOf` now propagates through nested combiners
instead of being dropped;
- impossible array items, object properties, and local definition
references remain rejecting;
- composed impossible schemas use a Pydantic type that rejects every
value, including JSON `null`;
- the existing `json_schema_to_pydantic_type(False) is None`
compatibility behavior remains unchanged.

### Problem

The original fix handled only a literal `false` directly inside one
`allOf`. Recursive filtering still interpreted the same `None` value as
both "discard this union branch" and "the whole schema is impossible."
That widened nested `allOf` schemas to `str`, removed impossible
properties/items/definitions, and allowed JSON `null` because Pydantic
treats a bare `None` annotation as `NoneType`.

### Fix

- Add a private unsatisfiable marker with Pydantic core- and JSON-schema
hooks.
- Drop that marker only from `anyOf`/`oneOf`; propagate it through
`allOf`.
- Preserve rejecting semantics for nested object properties, array
items, and local `$defs`/`definitions` references.
- Emit `{"not": {}}` from the rejecting Pydantic type.
- Correct the related empty-`allOf` and all-false-`anyOf` expectations.

### Testing

- `uv run pytest tests/test_schema_converter.py
tests/test_schema_parser.py -q` - 106 passed
- `uv run pytest -m schema -q` - 71 passed
- `make tst` - 928 passed
- `make chk` - Ruff and mypy passed across core, providers, tests, and
scripts

Regression coverage includes direct and nested `allOf`, JSON `null`,
empty arrays with impossible items, optional and required impossible
properties, local definition references, generated JSON Schema, and
shared model consumers.

### Note

#3876 adds coverage for the same function's `anyOf` handling but does
not change this `allOf` propagation path.

---------

Co-authored-by: jkomyno <alberto@composio.dev>
2026-07-28 14:15:49 +05:30
Alberto Schiabel 026c104fe8 fix(py): validate canonical URL targets (#3962)
This PR:
- builds on top of https://github.com/ComposioHQ/composio/pull/3823
- carries forward the Requests canonicalization hardening proposed in
https://github.com/ComposioHQ/composio/pull/3810
- resolves and validates the prepared hostname that Requests will
actually connect to
- blocks backslash parser differentials and rejects malformed ports
before DNS resolution
- adds focused URL-safety coverage while preserving both existing upload
entry points

Validation:
- `uv run --frozen pytest tests/test_url_safety.py tests/test_files.py
tests/test_tool_router_session_files.py -q` (177 passed)
- `make chk`
2026-07-28 14:03:53 +05:30
Checo f016bcebb9 fix(mimetypes): make guess() case-insensitive on file extensions (#3899)
guess() in composio/utils/mimetypes.py looked up Path(file).suffix
against the lowercase _types table without normalizing case, so
uppercase suffixes like .PNG or .PDF resolved to
application/octet-stream instead of the correct MIME type. Since guess()
feeds the mimetype field on file uploads (core/models/_files.py),
uploads from phone cameras, scanners and Windows — where uppercase
extensions are common — were getting the wrong content type.

The fix is a one-line normalization: Path(file).suffix.lower() before
the table lookup. This aligns guess() with its sibling
get_extension_from_mime_type, which is already case-insensitive, and
with the stdlib mimetypes.guess_type. I updated the existing test that
documented the old case-sensitive behavior to assert the
case-insensitive result instead.

Fixes #3857

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/ComposioHQ/codesmith/composio/pr/3899"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1787222508&installation_model_id=428187&pr_number=3899&repository=ComposioHQ%2Fcomposio&return_to=https%3A%2F%2Fgithub.com%2FComposioHQ%2Fcomposio%2Fpull%2F3899&signature=51d4b1022aa69bc47f45a7daf2e704db96c5eb2a2f267141010ca769b502a062"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

Signed-off-by: sergioperezcheco <checo520@outlook.com>
Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-07-28 13:18:55 +05:30
eeshsaxena d94aa32a41 test: add unit tests for the schema_converter boolean-schema filter (#3876)
## Description

`composio.utils.schema_converter._filter_boolean_schemas` pre-processes
JSON
Schemas before handing them to `json-schema-to-pydantic`, which doesn't
support boolean schemas (draft-06+ allows `true`/`false` as schemas). It
replaces `true` with `{}`, drops `false`, and recurses through combiner
arrays (`anyOf`/`allOf`/`oneOf`), `properties`, `items` and `$defs`. It
had no
test coverage.

This adds `python/tests/test_schema_converter.py` covering:

- `True` -> `{}` and `False` -> `None`
- non-schema values passing through unchanged
- lists dropping `False` entries, and an all-`False` list becoming
`None`
- combiner arrays dropping `False` entries, and the whole combiner being
  removed when every entry is `False`
- a `False` property being dropped while siblings are kept
- plain keys being preserved

No source changes.

## Verification

`pytest python/tests/test_schema_converter.py` - 9 passed. `ruff check`
/
`ruff format` clean.

Co-authored-by: jkomyno <alberto@composio.dev>
2026-07-28 13:18:40 +05:30
eeshsaxena 80e09ad0f0 test: add unit tests for pydantic utils (none_to_omit, parse_pydantic_error) (#3784)
## Description

`composio/utils/pydantic.py` had no dedicated test module. This adds
`tests/test_pydantic_utils.py` covering:

- `none_to_omit` converting `None` to the `omit` sentinel while passing
through other values (including falsy `0`/`""`)
- `parse_pydantic_error` listing missing fields, reporting type errors
with the offending parameter, and handling both together

No source changes.

## Verification

`pytest tests/test_pydantic_utils.py` - 5 passed. `ruff check` / `ruff
format --check` clean.

Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
2026-07-28 13:05:51 +05:30
Alberto Schiabel 503b50ab02 chore(deps): refresh SDK, Python, and CI dependencies (#3955)
This PR:

- splits https://github.com/ComposioHQ/composio/pull/3953 in two: this
PR carries every dependency and GitHub Actions bump **except** the docs
site, which follows in a stacked PR
- consolidates and supersedes Dependabot PRs #3915, #3916, and #3934
through #3942
- adopts TypeScript 7.0.2 for primary compilation while retaining the
`@typescript/typescript6` API lane that TypeScript-ESLint still
requires, following the upstream side-by-side guidance
- refreshes Python core and provider dependencies, lockfiles, and the
Ruff 0.16 lint configuration
- updates every GitHub Action with a verified newer official release,
including majors, while retaining immutable commit SHA pins and
migrating setup-uv cache pruning
- deletes four per-package `eslint.config.mjs` shims: under ESLint 10
the default per-file config lookup re-anchors the root config's globs
into each package, so `pnpm lint` stayed green while the CLI's
try/catch, `process.env` and node-builtin bans went unenforced
- bounds and documents the new `brace-expansion` and `@hono/node-server`
security overrides, raising the `@hono/node-server` floor to 2.0.10 to
clear GHSA-9mqv-5hh9-4cgg
- preserves intentional compatibility fixtures and lanes for AI SDK 6,
Zod 3, TypeScript 5.8, Mastra AI SDK 5, and Python provider constraints

## Context

The docs site is a separate Bun workspace with its own `bun.lock` and is
not a pnpm workspace member, so the two halves share no lockfile and no
build. Splitting them keeps the Fumadocs 11 migration (a breaking API
change with real refactoring) reviewable on its own, independently of
the mechanical version bumps here.

The `brace-expansion` override deliberately spans majors:
GHSA-mh99-v99m-4gvg (HIGH) is published as a single `<=5.0.7` range with
no 1.x or 2.x backport, so narrowing it to the 5.x line puts
`brace-expansion` 2.1.2 back under `core>minimatch>brace-expansion` and
`pnpm audit --prod --audit-level=high` exits 1. Verified both ways; the
trade-off it buys is recorded inline in `pnpm-workspace.yaml`.

Verified on this branch standalone: `pnpm install --frozen-lockfile`,
`pnpm lint`, `pnpm typecheck`, `pnpm build:packages`, `pnpm test` (963
tests, 26/26 tasks), and `pnpm audit --prod --audit-level=high`.
2026-07-27 17:57:29 +05:30
Alberto Schiabel facd40258c chore(py): prepare 0.18.0 release (#3825)
This PR:
- builds on https://github.com/ComposioHQ/composio/pull/3823 and
https://github.com/ComposioHQ/composio/pull/3824
- bumps the Python SDK and every provider package from `0.17.1` to
`0.18.0`
- synchronizes the runtime version and root `uv.lock`
- adds the canonical July 16 changelog entry, including URL-upload and
telemetry security, trigger connection-resolution behavior, provider
schema fixes, and the `pyautogen` to `ag2` migration
- strengthens release guards for Python workflow invocation, provider
metadata, and current Python and TypeScript changelog coverage
- limits provider packaging to directories containing `pyproject.toml`
- makes provider cleanup, installation, and builds stop on the first
failure
- incorporates the merged TypeScript changelog and missing Changeset
from https://github.com/ComposioHQ/composio/pull/3849

After merging, create and push the annotated `py@0.18.0` tag from the
merged `next` commit.

## Validation

- `pnpm test:release-workflow`
- `uv lock --check`
- `uv run --frozen python -c "import composio; assert
composio.__version__ == \"0.18.0\""`
- `pnpm exec prettier --check
docs/content/changelog/07-16-26-python-sdk-018.mdx
test/release-workflow.test.ts`
- `cd docs && bun run test`
- `cd docs && bun run lint:links`
- `cd docs && bun run types:check`
- `cd python && make build`
- `cd python && uv tool run twine check dist/*`
2026-07-16 00:33:30 +04:00
Anas Khan 3b28dc756e fix(py): handle list-valued JSON Schema type in openapi signatures (#3822)
`_type_to_parameter` in `python/composio/utils/openapi.py` assumed a
property's
`type` was a scalar string. JSON Schema Draft 2020-12 and OpenAPI 3.1
can express
a nullable field as a list of types instead, for example
`{"type": ["string", "null"]}`. Passing that list to the scalar
membership check
raised `TypeError: unhashable type: 'list'`, which could break tool
signature
generation in providers such as `google_adk`.

## Changes

- Handle list-valued `type` definitions as unions.
- Re-run every member through `_type_to_parameter` with a copy of the
full
property schema and only `type` replaced. This preserves sibling fields
such
as an array's `items`, so `{"type": ["array", "null"], "items": ...}`
resolves
  to `Optional[List[...]]`.
- Preserve scalar error behavior for invalid member types: an unknown
type in a
  list raises `InvalidSchemaError` instead of silently becoming `Any`.
- Keep the existing empty-list fallback to `Any`.
- Add regression coverage for nullable arrays and scalar/list
unknown-type
  parity alongside the existing list-valued type cases.

## Type of change

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

## Verification

From `python/`:

- `uv run pytest tests/test_openapi.py -q` — 18 passed.
- `make chk` — Ruff and mypy passed across the Python SDK, providers,
tests,
  examples, and scripts.

## 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 (not applicable: internal
helper)
- [x] I added regression tests
- [ ] I added a changeset (not applicable: Python-only change)

---------

Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
Signed-off-by: jkomyno <alberto@composio.dev>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-07-15 23:34:26 +04:00
Gautam Sharma cb84856a16 fix(openai-agents): preserve array item schemas (#3819)
## Summary

Prevent the Python OpenAI Agents provider from rewriting valid JSON
Schema array item definitions.

- Stop adding `"type": "string"` when an `items` schema has no direct
`type`.
- Preserve item schemas using `anyOf`, `oneOf`, `$ref`, object
constraints, or `{}`.
  - Continue removing `examples`, `pattern`, and `default` as before.
  - Add regression coverage for each affected schema shape.

  ## Problem

The provider previously added `"type": "string"` to every array `items`
object without a direct
  type declaration.

  That changes the meaning of valid JSON Schemas. For example:

  ```json
  {
    "anyOf": [
      { "type": "object" },
      { "type": "null" }
    ]
  }

  became:

  {
    "type": "string",
    "anyOf": [
      { "type": "object" },
      { "type": "null" }
    ]
  }

Because both constraints apply, valid object and null values become
impossible. This could expose
misleading tool schemas to OpenAI Agents or cause correct tool arguments
to be rejected.

  ## Testing

  - OpenAI Agents provider tests — 6 passed
  - OpenAI Agents type-inference tests — 5 passed
  - Mypy passed
  - Ruff lint and formatting passed
  - Schema-preservation smoke test passed

Co-authored-by: GautamSharma99 <gautamsharme99067@gmail.com>
Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
2026-07-15 18:07:20 +04:00
Aditya Painuli 1ea8ad9b3f docs: migrate AutoGen provider examples to ag2's LLMConfig API (#3833)
## Summary
The AutoGen provider examples still used the legacy
`llm_config={"config_list": [...]}` dict form, which ag2 0.14 (the
distribution `composio-autogen` depends on since #3729) only keeps for
backport compatibility and marks deprecated in
`LLMConfig.ensure_config`. The install command also pulled
`autogen-agentchat` (Microsoft's next-gen AutoGen package), which the
provider never imports, while the `ag2` distribution the examples
actually import from was only installed transitively.

Fixes #3831


## Changes
- `docs/content/docs/providers/autogen.mdx`: example migrated to
`LLMConfig({...})`, install line changed to `composio composio_autogen
ag2`, added `ag2` keyword and a one-line note that the provider runs on
the ag2 distribution
- `python/providers/autogen/README.md`: same quickstart and install
fixes
- `python/providers/autogen/autogen_demo.py`: same `LLMConfig` migration

`initiate_chat` is left as is: it isn't deprecated in ag2 0.14, and it's
the flow `register_tools` (caller/executor) is verified against.

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

## How Has This Been Tested?
- Verified in a fresh venv with the local `composio` +
`composio-autogen` packages and `ag2==0.14.0`: the updated example
constructs both agents, wraps a Composio tool via `AutogenProvider`,
registers it with `register_tools`, and executes it through the
executor's function map
- `uv run nox -s chk` (ruff + mypy): green
- `ruff format --check providers/autogen/`: clean
- Docs: `bun run build` and `bun run lint:links` from `docs/`: green

## Screenshots (if 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
- [ ] I added tests or explain why not applicable
- [x] I added a changeset if this change affects published packages

## Additional context
No changeset added: docs and Python-only change, no published TypeScript
package affected. No tests added: example/docs change with no runtime
code paths; the updated example was exercised manually against ag2 0.14
as described above.

---------

Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-07-14 15:00:03 +04:00
Alberto Schiabel c4071feab4 fix(py): guard URL uploads and redact telemetry (#3823)
This PR:
- ports the TypeScript SDK's public-network guard to Python URL file
uploads
- rejects non-HTTP(S), private, loopback, link-local, reserved, and
mixed DNS targets before a request is made
- applies the guard to both `FileUploadable.from_url()` and Tool Router
session-file URL uploads
- redacts URL queries, Authorization credentials, and secret-like
key/value pairs before error telemetry leaves the process
- adds focused SSRF and redaction regressions alongside the existing
file-upload coverage

Validation:
- `uv run --frozen pytest tests/test_url_safety.py
tests/test_redaction.py tests/test_files.py -q`
- `uv run --frozen ruff check …`
- `uv run --frozen ruff format --check …`
2026-07-14 14:32:05 +04:00
Alberto Schiabel 3a5e62feb4 fix(py): support list-valued schema types in tool signatures (#3815)
This PR:

- builds on top of https://github.com/ComposioHQ/composio/pull/3754
- normalizes list-valued JSON Schema `type` fields before scalar type
lookups
- applies the handling to direct fields and `anyOf`/`oneOf` options
- adds regressions for nullable, single, mixed, unknown, and
combiner-nested type lists
- credits @anxkhn as a Git co-author

## Context

JSON Schema Draft 2020-12 and OpenAPI 3.1 allow `type` to be an array.
The previous parser passed that list to a dictionary lookup, raising
`TypeError: unhashable type: 'list'`. This replacement covers the direct
case from #3754 and the same shape nested in a combiner.

---------

Co-authored-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
2026-07-13 17:00:21 +04:00
Alberto Schiabel edd0bf0479 docs: centralize SDK release notes (#3814)
This PR:
- Adds public docs changelog entries for the Python and TypeScript SDK
releases since May 15, 2026, including the previously unmigrated Python
0.13.1 entry.
- Removes the Python-local changelog and replaces it with canonical
changelog links from the repository and Python READMEs.
- Retargets the Python release guard to the docs changelog, documents
its table contract, and covers prior-version plus PEP 440 version
parsing.

Validation:
- pnpm test:release-workflow
- docs: bun run test
- docs: bun run lint:links
- docs: bun run types:check
- docs: bun run build
- pnpm exec prettier --check test/release-workflow.test.ts
docs/agent-guidance/guides/changelog.md docs/content/changelog/*.mdx

Review:
- ce-code-review completed with correctness, testing, maintainability,
project-standards, agent-native, learnings, and adversarial passes. All
actionable findings were fixed; final adversarial re-review found no
remaining P0-P2 issue.
2026-07-13 16:23:11 +04:00
MatinGathani d5a37efb85 fix(python): handle title-less top-level schemas (#3325)
## Summary

Fixes `json_schema_to_model` when a top-level JSON schema omits `title`
or sets it to `null`. This is the remaining direct-helper path;
anonymous nested objects from #2435 are already handled by
`schema_converter.py`.

- falls back to `GeneratedModel` only when the title is `None`,
preserving intentionally empty string titles
- adds regression coverage for missing, `null`, and empty titles, model
construction, and required-field validation

## Validation

- `ruff format --check python/composio/utils/shared.py
python/tests/test_schema_parser.py`
- `ruff check python/composio/utils/shared.py
python/tests/test_schema_parser.py`
- `PYTHONPATH="$PWD/python"
/Users/jkomyno/work/composio/composio2/.venv/bin/pytest -q
python/tests/test_schema_parser.py`

This is a follow-up to #2435; it intentionally does not close that
already-resolved issue.

Co-authored-by: jkomyno <alberto@composio.dev>
Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
2026-07-13 14:01:32 +04:00
Rahul Tarak a13c883845 docs: package-manager picker for install commands (#3802)
## What

Two changes to the docs install experience:

**1. Package-manager picker on install code blocks.** New
`PackageInstall` client component
(`docs/components/package-install.tsx`): the code block's copy button
opens a dropdown of package managers. Selecting one rewrites the
displayed command and copies it to the clipboard.

- TypeScript blocks: npm (default, `npm install`) / pnpm / bun / yarn
(`<pm> add`)
- Python blocks: uv (default, `uv add`) / pip (`pip install`)
- The menu is portaled to `document.body` so it isn't clipped by the
`Tabs`/`CodeBlock` overflow containers, and closes on outside click,
Escape, or real scroll movement.

**2. `@composio/slim` callout in the quickstart.** The quickstart
TypeScript install blocks carry display-only `#` comment lines noting
that `@composio/core` ships its docs and TypeScript source in the
package (inspectable to coding agents) and that `@composio/slim` is the
smaller install with the same API. Comment lines are rendered muted and
are **never copied** — the picker copies only the command.

## Where

Converted every plain `npm install` / `pip install` code block under
`docs/content` (quickstart, all provider pages, single-toolkit MCP,
custom tools, migration guides, standup example, SDK reference indexes).
The TS reference index's manual npm/pnpm/yarn/bun tabs collapse into one
picker. The two SDK-reference generators
(`ts/packages/core/scripts/generate-docs.ts`,
`python/scripts/generate-docs.py`) emit the new component so
regeneration keeps it. Flagged upgrade commands (`pip install
-U/--upgrade`) and the bun-first examples were left as-is.

## Verification

- `bun run types:check` — pass
- `bun run build` — pass
- `eslint` on touched TS files — clean (remaining repo lint errors are
pre-existing on `next`)
- Visual check via dev server + browser: comment lines render muted
below the command, picker opens npm/pnpm/bun/yarn (uv/pip on Python
tabs), selecting a manager rewrites the command, and the clipboard
receives exactly the command with no comment lines.

Docs-only behavior change; no changeset (generator-script edits don't
touch published code).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 06:33:42 -07:00
Rahul Tarak caef908075 docs: refresh README(s) (#3801)
## Summary

Rewrites the stale root `README.md` against the current docs (structure
modeled on [earendil-works/pi](https://github.com/earendil-works/pi)'s
README).

- **Quickstarts** now use the session-based API (`composio.create()` →
`session.tools()`) that the docs treat as canonical, replacing the
legacy `composio.tools.get()` flow
- **Positioning** matches the docs welcome page: 1000+ pre-authenticated
toolkits, per-user sessions, auth, triggers, and a sandbox
- **New sections**: CLI install (curl / Homebrew / npm),
sessions-via-MCP note, repository layout, minimum runtime versions
- **Provider table** merged with package links; adds the Claude Agent
SDK providers (both SDKs) and the experimental Pi provider
(`@composio/experimental`); documents the Python
`composio-gemini`/`composio-google`/`composio-google-adk` split
- **Removed**: Rube section (no longer in docs), cover banner (replaced
with theme-adaptive brand logomark from brand.composio.dev),
master-branch pointer, internal OpenAPI-pull section, private
`@composio/ts-builders` listing

Docs-only change — no changeset needed.

## Preview

Rendered view: [README.md on this
branch](https://github.com/ComposioHQ/composio/blob/docs/refresh-root-readme/README.md)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 06:14:38 -07:00
Alberto Schiabel 15a03c10fb ci: add dead-code detection (knip + vulture + orphaned-CI check) (#3787)
## What

Adds a **report-only** `Dead Code` CI workflow that surfaces
likely-orphaned code on every PR, across all three surfaces —
TypeScript, Python, and GitHub Actions. Follow-up to the root
`Dockerfile` cleanup (#3783) and the dead-code sweep in #3786: instead
of finding this stuff by hand, catch it automatically.

| Surface | Tool | Wiring |
|---|---|---|
| TypeScript | [**knip**](https://knip.dev) | `knip.json`; runs via
`pnpm dlx knip@5`. Finds unused files, exports, types, and dependencies.
|
| Python | [**vulture**](https://github.com/jendrikseipp/vulture) |
`dead_code` nox session + `make dead-code`; allowlist at
`python/config/vulture_allowlist.py`. Finds unused
functions/classes/variables (complements Ruff's F401/F841). |
| GitHub Actions | small bash script |
`.github/scripts/check-orphan-ci.sh` — flags reusable workflows and
composite actions with no callers. |

## Why report-only (not blocking)

Every job writes findings to the run's **Step Summary** and **never
fails the build**. These tools carry unavoidable false positives on a
library monorepo — public API surface, dynamic imports, import-map
targets (e.g. core's `#platform`), framework entry points. A red ❌ on
false positives would just train everyone to ignore the check. Once a
job's config is refined enough that a clean run is the steady state, it
can be flipped to blocking.

## Validation (ran each locally)

- **knip**: 0 unused *files* after scoping out the e2e-test workspaces
(knip crashes traversing `ts/e2e-tests/**` — pre-existing knip bug,
filed via `ignoreWorkspaces`) and build/docs artifacts. Export/dep
categories surface advisory items.
- **vulture**: clean run (report-only); allowlist suppresses the 3
`TYPE_CHECKING` re-exports in `custom_tool.py`; `build/`/`dist/`
excluded. Surfaces 4 genuine minor items (`bases`, `desc`×3).
- **orphan-CI script**: finds none — the repo has no orphaned CI
plumbing today.
- **Pinning**: `jk actions check` clean for this workflow;
`actions/checkout` is SHA-pinned, the two local composite actions need
no pin.

Local usage is documented under **Dead code detection** in
`CONTRIBUTING.md`.

## Note on scope

These cover TS/Python/GHA — they would *not* have caught the root
`Dockerfile` itself (an arbitrary root file no tool tracks). That class
stays a manual-review concern; the orphan-CI script is the closest
analogue for the CI surface.
2026-07-10 02:17:54 +04:00
eeshsaxena 3e37f3c7a4 test: add unit tests for the mimetypes utility (#3750)
## Summary

`composio/utils/mimetypes.py` had no test coverage. This adds a suite
for both of its public helpers:

**`guess()`** (filename/extension → MIME type):
- known extensions resolve correctly (json, html, png, mp4, …)
- the lookup table is lower-case, so an upper-case suffix (`photo.PNG`)
falls back to the default
- unknown / missing extensions return `application/octet-stream`
- only the final suffix is used (`archive.tar.gz` == `archive.gz`)
- `Path` objects are accepted, not just strings

**`get_extension_from_mime_type()`** (MIME type → extension):
- direct mappings (`image/png` → `png`, `application/vnd.ms-excel` →
`xls`, …)
- parameters are stripped (`text/plain; charset=utf-8` → `txt`) and
matching is case-insensitive
- structured `+suffix` handling: known prefixes win
(`application/atom+xml` → `atom`), otherwise the suffix is used
(`application/vnd.api+json` → `json`, `application/foo+bar` → `bar`)
- subtype fallback (`application/x-custom` → `x-custom`), empty subtype
→ `txt`, and malformed/empty input → `bin`

Tests only — no library code changed. `pytest
python/tests/test_mimetypes.py` passes (`32 passed`); `ruff check` /
`ruff format --check` are clean.

Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
2026-07-07 00:44:15 +04:00
Alberto Schiabel fc17c37bf9 fix(cli): enforce sensitive-file upload denylist (GHSA-hp3h-89pf-5q58) (#3763)
This PR:

- fixes #3746 / GHSA-hp3h-89pf-5q58 — the CLI's tool file-upload path
bypassed the sensitive-file denylist
- **root cause:** the denylist was enforced at the *caller* layer, so
`@composio/cli`'s duplicate upload path (`readFileFromDisk`) read and
uploaded any local path a tool argument pointed at — `~/.ssh/id_rsa`,
`~/.aws/credentials`, `.env`, etc. — enabling credential exfiltration
via `composio execute` / `composio run` (incl. LLM-driven agents hit by
prompt injection)
- **unified fix:** one canonical guard, exported from `@composio/core`
and enforced at the read primitive in every SDK
- `core`: the guard now routes fs/path access through the internal
`#platform` abstraction (adds a `realpathSync` method), so it carries
**no static `node:*` imports** and is exported from the package root:
`assertSafeFileUploadPath`, `isBlockedSensitiveFileUploadPath`,
`BUILTIN_FILE_UPLOAD_PATH_DENY_SEGMENTS`
- `cli`: `readFileFromDisk` calls the shared `assertSafeFileUploadPath`
before `fs.readFile`; URLs and `File` objects are unaffected (matches
core)
- `python`: already routes all uploads through the guarded
`FileUploadable.from_path` — no functional change; adds regression tests
for parity
- supersedes #3755, which exported the then-node-only guard from core's
**main** `index.ts` and would have injected `node:fs` into the shared
`dist/index.mjs` (the workerd/edge-light entry), likely breaking
Cloudflare Workers / Vercel Edge
- changesets: `@composio/core` minor (new public exports),
`@composio/cli` patch (security fix)

## Verification

- core: full suite passes (997), incl. new root-export +
realpath-symlink cases
- **edge bundle stays clean**: built `dist/index.mjs` has no static
`node:*` imports and reaches the platform only via `#platform`
- cli: new regression test drives the real `uploadToolInputFiles →
readFileFromDisk → guard` chain — `~/.ssh/id_rsa` and `.env` throw
`ComposioSensitiveFilePathBlockedError` before any `createPresignedURL`
call; a normal file still uploads
- python: `test_files.py` + sensitive-path suite pass (145), incl. 3 new
`from_path` guard tests
- `tsgo` typecheck + eslint clean for core and cli
2026-07-07 00:30:40 +04:00
Alberto Schiabel 820abb9072 fix(sdk): resolve toolkit versions case-insensitively in both SDKs (#3762)
## Summary

Toolkit version maps are keyed by **normalized (lowercase) slugs** on
the *write* side — env vars (`COMPOSIO_TOOLKIT_VERSION_*`) and
user-supplied dicts both get their keys lowercased when the map is
built. But the *lookup* read the map with the **raw slug**. A pin
configured under a different casing (e.g. `{ "GitHub": "20250101_00" }`
or `COMPOSIO_TOOLKIT_VERSION_GITHUB` looked up as `GitHub`) therefore
missed the map and silently fell back to `"latest"`, which
`Tools.execute` then rejects with a version-required error — discarding
the user's explicit pin.

Rather than patch only the read side, this **centralizes the
normalization rule** into a single helper per SDK and routes **both**
the map-building (write) and lookup (read) paths through it, so the two
sides can never drift apart again:

- **Python** — `normalize_toolkit_slug()` in
`python/composio/utils/toolkit_version.py`
- **TypeScript** — `normalizeToolkitSlug()` in
`ts/packages/core/src/utils/toolkitVersion.ts`, imported by
`getToolkitVersionsFromEnv` in `sdk.ts`

Behavior is now identical across the two SDKs. This **supersedes
#3759**, which patched only the Python read side and left TS diverging.

## Backend verification

Confirmed against the backend (staging, **1403 toolkits across 15
pages**): every toolkit `slug` is already lowercase (the mixed-case
`name` field is display-only). So the *primary* execute path — which
resolves via `tool.toolkit.slug` — was never broken in practice. This
change:

- **hardens** the direct-util and mixed-case-config paths (a user
passing `{ "GitHub": "…" }` now resolves as expected), and
- **removes a latent cross-SDK divergence**: the TS suite previously
asserted `getToolkitVersion` was *case-sensitive* (`versions.test.ts`),
locking in the bug for an input the production code path can't actually
produce. That test is flipped to assert case-insensitive resolution.

## Tests

- **Python** (`python/tests/test_toolkit_version.py`): case-insensitive
lookup via `get_toolkit_versions` round-trip, and against a raw
pre-normalized map. `18 passed`.
- **TypeScript** (`ts/packages/core/test/core/versions.test.ts`):
replaced the case-sensitivity assertion with case-insensitive resolution
+ a write→read round-trip of a mixed-case user pin. Full core suite `997
passed`.
- Typecheck (`tsc --noEmit`) clean; `ruff` clean.

## Notes

- Changeset added: `@composio/core` patch.
- No public API change; the fix is behavioral (case-insensitive) plus an
internal shared helper.
2026-07-06 15:30:06 +04:00
Alberto Schiabel 58bc93b2d9 chore(deps): consolidate dependency updates (#3760)
This PR:
- consolidates https://github.com/ComposioHQ/composio/pull/3733,
https://github.com/ComposioHQ/composio/pull/3735 through
https://github.com/ComposioHQ/composio/pull/3744, and
https://github.com/ComposioHQ/composio/pull/3752
- refreshes the pnpm workspace dependencies under the existing
`minimumReleaseAge` supply-chain gate
- keeps latest compatible pins for ESLint 9, AI SDK 6, and Cloudflare
workers types 4 where latest majors conflict with the current workspace
- keeps zod-v3 runtime fixtures on `zod@3.25.76` while bumping the
workspace catalog to zod 4
- bumps Python `composio-client` to `1.42.0` and refreshes `uv.lock`
- updates the SHA-pinned Claude and Codex workflow actions from the
Dependabot action group
- adds a patch changeset for the versioned package manifests touched by
dependency updates
- verifies the rollup with frozen pnpm/uv locks, peer checks, audit
threshold checks, typecheck, lint, builds, TypeScript tests, and Python
`nox -s chk`

## Context

`pnpm audit --prod --audit-level=moderate` passes. The remaining
production audit item is a low `@ai-sdk/provider-utils` advisory through
Mastra transitive dependencies; forcing it higher would require leaving
the compatible stable Mastra dependency path.
2026-07-06 14:33:55 +04:00
Alberto Schiabel 2e42d75259 fix(py): apply skip_default to agentic provider signatures (langchain, langgraph, autogen) (#3730)
## Summary

Agentic providers honor `schema_config={"skip_defaults": True}` for
`args_schema` (defaults stripped, fields become required) but were
**not** applying the same `skip_default` to the function
`__signature__`, so the wrapped tool's signature and `args_schema`
disagreed about which defaulted params were optional. The sibling
`llamaindex` provider already passed it to both.

**Supersedes #3727.** This is the polished version, now that the ag2
migration (#3729) landed and `composio-autogen` imports again — so the
autogen case uses a real import instead of the earlier source-load stub,
and the parity tests run in CI.

## Changes

- **langchain / langgraph** (original commit by @anxkhn): pass
`skip_default=self.skip_default` to
`get_signature_format_from_schema_params`.
- **autogen**: same one-line alignment. autogen builds **only**
`__signature__` (no `args_schema`), so it never had the *mismatch* — but
its signature still ignored `skip_defaults`. Aligned for consistency;
verified on the signature alone.
- **tests**: `TestAgenticSkipDefaultsParity` guards each provider with
`pytest.importorskip`; adds real-import autogen regression tests (no
stub).
- **CI + nox**: install `providers/autogen` in the unit-test job
(`py.test.yml`) and the nox `tst` session so the autogen parity tests
actually run, plus a **hard `import composio_autogen` smoke check** in
CI.

## Why the CI import guard (not just the tests)

The `importorskip`-guarded unit tests **skip** when a provider can't be
imported — which is the correct behavior for optional providers, but
means they'd *silently skip* if autogen broke on a fresh install
(exactly the failure that #3729 just fixed). The extra `python -c
"import composio_autogen"` in the import step is a **hard** check that
fails loudly, so a future dependency breakage can't slip through as a
green build.

`providers/langchain` + `providers/autogen` co-install cleanly
(verified: 59 packages, ag2 0.14.0, no conflict). langgraph continues to
skip in CI — its code path is identical to langchain's, which runs.

## Type of change

- [x] Bug fix

## How Has This Been Tested?

- Base venv (langchain only): langchain parity **passes**, langgraph +
autogen **skip** (no error).
- CI-mirror (langchain + autogen, editable): **4 passed, 2 skipped
(langgraph)** — autogen tests actually run and pass.
- Regression proof, each provider: reverting only the provider line
fails the skip-defaults case with `assert 5 is Parameter.empty`;
restoring passes. Verified for langchain **and** autogen.
- `ruff check`, `ruff format --check`, `mypy`, and full
`tests/test_provider.py` (29 passed, 4 skipped) → clean. `py.test.yml`
parses.

## Checklist

- [x] I ran linters/tests locally and they passed
- [x] I added tests: langchain/langgraph parity + regression, and
autogen parity that runs in CI
- [ ] Changeset: not applicable — Python-only change.

## Credits

Original fix by @anxkhn (#3713 → #3727), preserved as the first commit
and co-authored on the follow-up.

---------

Co-authored-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
2026-07-02 15:38:53 +04:00
Alberto Schiabel 5ac2de2431 fix(py): migrate composio-autogen off abandoned pyautogen to ag2 (#3729)
## Summary

`composio-autogen` fails to import on a fresh install: the provider
depended on the abandoned `pyautogen` distribution, whose latest release
(`0.10.0`) ships **no top-level `autogen` module**. The pin
`pyautogen>=0.2.19,<0.11` resolves straight to it, so `import
composio_autogen` raises `ModuleNotFoundError: No module named
'autogen'`.

The autogen 0.2 lineage moved to the **`ag2`** distribution (`0.14.x`),
which provides the same `autogen` package the provider uses
(`ConversableAgent`, `autogen.agentchat.register_function`) alongside
`autogen-core`. This swaps the dependency.

Closes #3728.

## Changes

- `providers/autogen/pyproject.toml` and `providers/autogen/setup.py`:
`pyautogen>=0.2.19,<0.11` → `ag2>=0.14,<1.0`. `flaml` and `autogen_core`
unchanged.

**No provider code changes** — the imports and runtime API are identical
across the two distributions.

## Type of change

- [x] Bug fix

## How Has This Been Tested?

Verified in isolation via `uv run --with ./providers/autogen` (a real
fresh install of the provider with its new deps → resolves `ag2
0.14.0`):

1. **Import** — `import composio_autogen` now succeeds (previously
`ModuleNotFoundError`).
2. **`wrap_tool`** — returns a real `autogen_core.tools.FunctionTool`
with the expected signature.
3. **`register_tools` → `autogen.agentchat.register_function`** — the
AG2 runtime path works end to end:
   - caller (LLM side): `caller.llm_config.tools` advertises the tool,
- executor (execution side): the function lands in
`executor._function_map`.

`flaml==2.6.0` + `autogen-core==0.7.5` resolve cleanly alongside `ag2
0.14` (no conflict).

## Notes / follow-up

This class of bug (a provider silently un-importable on fresh install)
slipped through because **CI never installs or imports the autogen
provider** — the unit-test job only installs `providers/langchain`.
Worth a small follow-up: add a fresh-install import smoke test for the
agentic providers so this can't recur silently. Left out here to keep
this PR to the dependency fix and avoid adding ag2's install weight to
the main test matrix.

## Checklist

- [x] I ran the migration verification locally (isolation) and it passed
- [ ] Changeset: not applicable — Python-only change.
2026-07-02 15:10:29 +04:00
Alberto Schiabel e78ed312c6 fix(core): run all parallel OpenAI tool calls from the first choice (#3726)
This PR:

- supersedes https://github.com/ComposioHQ/composio/pull/3712,
preserving the original commits by @serhiizghama (kept as
author/co-author)
- fixes `OpenAIProvider.handleToolCalls` to execute **every** tool call
in an assistant message — it previously only read `tool_calls[0]`, so
parallel tool calls (on by default) were dropped and their
`tool_call_id`s went unanswered, failing the next request with the "must
be followed by tool messages responding to each `tool_call_id`" error
- limits execution to the **first choice** — with `n > 1` the previous
loop ran each tool call once per choice and orphaned the `tool_call_id`s
belonging to the alternative completions
- aligns the Python SDK (`OpenAIProvider.handle_tool_calls`) with the
same first-choice behavior so both SDKs stay in parity
- fixes the stale `@composio/openai` test that modeled "multiple tool
calls" as separate `n` choices rather than one message's `tool_calls`
array
- clarifies in the changeset that the calls run **sequentially**, in the
order the model returned them (here "parallel" means the model issued
several calls in one turn, not concurrent execution), so each
`tool_call_id` is answered exactly once and deterministically
- adds regression tests (TS + Python): both calls run for parallel tool
calls in one message, and only the first choice runs when `n > 1`

Full `@composio/core` (996) and `@composio/openai` suites green; Python
`handle_tool_calls` tests, Ruff, and mypy clean; `tsc` clean.

---------

Co-authored-by: serhiizghama <zmrser@gmail.com>
2026-07-02 13:27:49 +04:00
Alberto Schiabel a69c3edea3 fix(triggers): align create() user_id resolution across SDKs (#3723)
This PR:

- builds on top of https://github.com/ComposioHQ/composio/pull/3714 —
addresses its code-review findings
- restores TS/Python parity on slug validation: Python `create()` now
validates the slug up-front and raises a new `TriggerTypeNotFound`,
mirroring the TS `ComposioTriggerTypeNotFoundError`
- switches both SDKs to the native `user_id` upsert field — TS drops the
`& { user_id }` intersection and Python drops the `extra_body` shim
(both pinned clients already expose the field)
- treats a blank/whitespace `userId` (TS) or
`user_id`/`connected_account_id` (Python) as missing instead of
forwarding it to the backend
- documents the error-contract change (`create()` no longer throws
`ComposioConnectedAccountNotFoundError` — that case now surfaces from
the backend) in the changeset and `ts/docs/api/triggers.md`, and bumps
the changeset from `patch` to `minor`
- adds Python tests for the 2FA (both-provided) path, unknown-slug, and
blank `userId`; tightens the TS auto-resolve assertion and adds an
empty-`userId` case

## ⚠️ Release gating

Since #3714, `triggers.create()` (both SDKs) relies on the backend
resolving the trigger connection from `user_id` on upsert
([ComposioHQ/platform#10932](https://github.com/ComposioHQ/platform/pull/10932)).
There is **no client-side fallback** — reintroducing one would undo
#3714's intent, so this is a release-sequencing requirement, not a code
change here.

- Do not release `@composio/core` or the Python SDK until #10932 is live
in all regions.
- Self-hosted / on-prem deployments must be on a backend version that
includes #10932.

## Review findings addressed

| # | Finding | Resolution |
|---|---------|------------|
| P1-1 | Coupled to unreleased backend, no fallback | Documented
dependency in changeset; flagged release-gating above (no fallback by
design) |
| P2-2 | Cross-SDK parity on slug validation | Kept `getType` in both;
added Python `TriggerTypeNotFound` mirroring TS |
| P2-3 | `user_id` bridge redundant in both SDKs | TS uses native field
directly; Python uses `user_id=` kwarg |
| P2-4 | Error-contract change shipped silently | Documented in
changeset + docs `Throws` section; `minor` bump |
| P2-5 | Python missing 2FA (both-provided) test | Added
`test_create_with_user_id_and_connected_account_id` |
| P3 6–13 | Cleanups | undefined-assertion, `none_to_omit`, docstring,
`parsedBody.data`, redundant `else`, stale comments, blank-string guard
|

## Verification

- TS: `vitest` 59 passed, `typecheck` clean, `eslint`/`prettier` clean
- Python: `pytest tests/test_triggers.py` 69 passed, `nox -s chk` (ruff
+ mypy) clean
2026-07-02 11:40:47 +04:00
Anas Khan c660007c71 fix(py): handle typeless and empty combiner schemas in openapi signatures (#3710)
### Summary
`function_signature_from_jsonschema` in
`python/composio/utils/openapi.py` builds the `__signature__` for tools
wrapped by the `google_adk` provider
(`providers/google_adk/composio_google_adk/provider.py:66`). It already
falls back to `Any` for a top-level property with no `type`, but the
nested combiner helpers do not, so real combiner shapes crash at wrap
time:

- a `oneOf`/`anyOf` option with no `type` key -> `KeyError: 'type'`
- an empty `oneOf`/`anyOf` list -> `TypeError: Cannot take a Union of no
types`
- an empty `allOf` list -> `KeyError: 'type'` (merges to `{}`)

This extends the existing "default to Any" intent to nested options.
Follow-up to #3708, which fixed the same bug family in `shared.py` and
noted this `openapi.py` case for a separate PR.

Fixes #

### Changes
- `_type_to_parameter`: read `schema.get("type")` instead of
`schema["type"]`; when the type is missing, return `t.Any` (mirrors the
top-level fallback). An unknown *string* type still raises
`InvalidSchemaError`.
- `_handle_composite_type`: return `t.Any` for an empty option list
instead of building an empty `Union`. Empty `allOf` merges to `{}` and
resolves to `Any` via the above.
- New `python/tests/test_openapi.py` (11 tests): this builder had none.
Asserts the five former crashes now resolve to `Any`, and that existing
shapes are unchanged.

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

### How Has This Been Tested?
From `python/`:

```
uv run pytest tests/test_openapi.py -q   # 11 passed
make chk                                 # ruff + mypy -> clean
```

To confirm the tests guard the fix, the five former crashes (typeless
`anyOf`/`oneOf` option, empty `anyOf`/`oneOf`/`allOf`) raise on the
unmodified file and now resolve to `Any`; regressions cover
`anyOf:[str,null]` -> `Optional[str]`, a 4-member union, enum, array,
object, and a typeless property.

### 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 (internal util)
- [x] I added tests (new test_openapi.py)
- [ ] I added a changeset (Python-only, not needed)

Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
2026-07-02 11:40:35 +04:00
Anas Khan b0ab8eacff fix(py): handle 4+ option and typeless anyOf/oneOf unions in tool signatures (#3708)
## Summary
`get_signature_format_from_schema_params` in
`python/composio/utils/shared.py` builds the `__signature__` for the
tool wrappers used by the langchain, langgraph, llamaindex, and autogen
providers. Its `oneOf`/`anyOf` handling hardcoded a ladder for exactly
1, 2, or 3 union members and raised `ValueError("Invalid 'oneOf'
schema")` for any union with four or more options. It also indexed
`PYDANTIC_TYPE_TO_PYTHON_TYPE[ptype.get("type")]` directly, so any
combiner option missing a `type` key produced `None` and raised
`KeyError(None)`.

Both shapes are valid JSON Schema and appear in real tool input schemas,
so a tool with a 4-way union, or a combiner option without an explicit
`type`, makes those four providers raise at tool-wrap time. This
replaces the ladder with a single map + `functools.reduce` path that
mirrors the existing `_build_union_from_options` in
`schema_converter.py`, keeping the 1/2/3-member and nullable `[type,
null]` outputs identical to before.

Fixes #

## Changes
- `python/composio/utils/shared.py`: in
`get_signature_format_from_schema_params`, replace the 1/2/3-member
`oneOf`/`anyOf` ladder (which raised `ValueError` for 4+ members) with a
map-each-option-to-a-Python-type + `functools.reduce` Union build that
supports any member count. Unknown/missing option types map to
`typing.Any` instead of raising `KeyError`. Adds `from functools import
reduce` and removes three now-unnecessary `# type: ignore` comments.
- `python/tests/test_schema_parser.py`: add
`TestGetSignatureFormatFromSchemaParams` (8 cases) covering 4- and
5-member unions, an `anyOf` option missing `type`, an all-typeless
`anyOf`, and regressions for the previously supported single/2/3-member
and nullable `[type, null]` shapes.

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

## How Has This Been Tested?
From `python/`:

```
uv run pytest tests/test_schema_parser.py::TestGetSignatureFormatFromSchemaParams -v   # 8 passed
uv run pytest tests/test_schema_parser.py -q                                           # 75 passed
make chk   # ruff + mypy -> clean, no new ignores
```

To confirm the tests guard the fix, the new test class was also run
against the unmodified `shared.py`: 4 of the 8 fail there
(`ValueError`/`KeyError` on the 4+/typeless cases), and all 8 pass with
this change. The nullable `anyOf [type, null]` case still resolves to
`Union[str, Any, NoneType]` (its pre-existing behavior); collapsing
`null` into `Optional` lives in `schema_converter` and is intentionally
left out of this minimal fix, with the test documenting current
behavior.

## Screenshots (if applicable)
N/A

## 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
- [x] I added tests or explain why not applicable
- [ ] I added a changeset if this change affects published packages

## Additional context
No linked issue: this is a self-identified, reproduced bug in the older
schema->signature path. The fix deliberately mirrors
`schema_converter._build_union_from_options` so the two schema paths
agree on N-ary union handling. The same bug family exists in
`python/composio/utils/openapi.py`
(`function_signature_from_jsonschema`, used by the google_adk provider)
but is kept out of this PR to stay single-purpose; happy to follow up on
it separately.
2026-07-02 11:39:41 +04:00
Anshu Garg 20a4711ee0 feat(triggers): resolve connection from user_id server-side in create() (#3714)
## What

`triggers.create()` used to make an extra **`connectedAccounts.list()`**
call to turn a `user_id` into a `connected_account_id` before calling
upsert. The backend now resolves the connection from `user_id` directly
(picks the first active connection for the user + the trigger's toolkit,
parity with tool execution), so the SDK passes `user_id` straight
through and drops the round-trip.

- **TS** (`ts/packages/core`): removed the `connectedAccounts.list()`
block in `Triggers.create()`. `user_id` was already forwarded to upsert
(for 2FA); this just removes the now-redundant lookup. `getType` is kept
for an early, clear "trigger type not found" error.
`connected_account_id` is passed when the caller pins one.
- **Python** (`python/composio`): dropped
`_get_connected_account_for_user`; `user_id` is sent via `extra_body`
and `connected_account_id` is omitted when not provided.

## Why

The SDK only ever holds a `user_id`, so every `triggers.create()` paid
an extra API call to find a connection. The backend change
(ComposioHQ/platform#10932) makes trigger upsert accept `user_id` and
auto-resolve — this PR consumes that.

## Behavior notes

- When `connected_account_id` is omitted, the backend picks the first
active connection ordered by `created_at desc`. The TS SDK's old
`list()[0]` ordering was unspecified — selection is now deterministic
and consistent with tool execution.
- "No connected account found" / "connected account not found" now
surface from the **backend** instead of a client-side pre-check.
- When 2FA is enabled and `connected_account_id` is pinned, the backend
validates that `user_id` owns it (unchanged; `user_id` was already
forwarded).
- The `extra_body` / `& { user_id }` bridges are still needed only until
`@composio/client` / `composio_client` regenerate with `user_id` on the
upsert type (the field is already in the OpenAPI contract).

## Tests

- TS: `vitest run test/models/triggers.test.ts` → **58 passed** (create
tests rewritten to assert no `connectedAccounts.list()` call + `user_id`
forwarded).
- Python: `pytest tests/test_triggers.py` → **66 passed** (same).

## Depends on

Backend: ComposioHQ/platform#10932 (`feat(apollo): resolve trigger
connection from user_id on upsert (PLEN-2580)`).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 01:05:07 +04:00
Alberto Schiabel 03cb181fdb chore(deps): roll up dependency bumps (#3711)
This PR:
- rolls up https://github.com/ComposioHQ/composio/pull/3695,
https://github.com/ComposioHQ/composio/pull/3696,
https://github.com/ComposioHQ/composio/pull/3697, and
https://github.com/ComposioHQ/composio/pull/3698 into one
dependency-bump branch
- updates Python dependency metadata for `langchain-openai`,
`pyautogen`, and `crewai`
- updates npm production dependency metadata for `zod-to-json-schema`,
`openai`, and `@modelcontextprotocol/sdk`
- refreshes `uv.lock` and `pnpm-lock.yaml` with the repo-pinned package
managers
- leaves Changesets unchanged because the touched TS package manifests
are private/ignored and no published TS SDK package metadata changes
- verified with `pnpm install --frozen-lockfile`, `pnpm lint`, `pnpm
typecheck`, focused TS builds/tests, clean-worktree `pnpm
test:examples`, Python import tests, and Python nox checks
- note: local `pnpm build:packages` hit a pre-existing broad-build race
around `@composio/slim` rebuilding `@composio/core`; focused dependent
builds pass

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 19:20:15 +04:00
Alberto Schiabel 9901e01a80 feat(py): add tool router session deletion
Release Python 0.17.1 with Tool Router session deletion support.
2026-06-28 22:59:25 +04:00