535 Commits

Author SHA1 Message Date
Alberto Schiabel dafe1389b1 chore(release): prepare Python 0.22.0 and TypeScript releases (#4563)
This PR:

- bumps Python `composio` and all 13 provider packages to `0.22.0`
- regenerates `uv.lock` and adds the coordinated Python and TypeScript
release changelog
- records the manually published `@composio/typesafe@0.1.0` as the
repository baseline
- replaces the original TypeSafe minor changeset with a patch release
for `0.1.1`, so post-publication runtime fixes ship instead of being
skipped
- keeps the existing Changesets train for `@composio/core@0.19.0`,
`@composio/slim@0.19.0`, and provider updates
- verifies the release workflow, changesets, all 20 TypeScript package
builds, 147 TypeSafe tests, 590 docs static tests, and all 28 Python
distributions with Twine
2026-09-21 23:02:55 +04:00
Deep Gori a1873e90a7 fix(core): make HTTP status errors catchable as ComposioError (#4543)
## Summary

In the Python SDK, HTTP failures from the generated client escape
`except ComposioError`: `composio_client` has its own exception root,
unrelated to `composio.exceptions.ComposioError`. An invalid API key, a
429 or a 500 therefore bypasses a handler written against the SDK's base
error, while the TypeScript SDK covers the equivalent case (#4459).

Opened this so there's something concrete to look at alongside the
issue. Happy to rework it or close it if you'd prefer a different
approach.

Fixes #4537

## Changes

- `HttpClient` overrides `_make_status_error`, the single place the
generated client builds status errors, and returns each error as a
subclass of **both** the generated class and `ComposioError` (one cached
subclass per generated class).
- New `python/tests/test_client_errors.py` covering every mapped status
plus an unmapped one, class reuse, and the SDK's existing
`ToolNotFoundError` mapping.

I went with this rather than wrapping errors at call sites, which is
what I suggested on the issue, because it keeps every existing handler
working:

- `except ComposioError` now catches HTTP failures.
- `except composio_client.AuthenticationError` / `APIStatusError` still
work, with `status_code`, `response` and `body` unchanged.
- The SDK's own mappings are untouched: `get_raw_composio_tool_by_slug`
still raises `ToolNotFoundError` on 400/404 and re-raises other client
errors unchanged, per its docstring and `test_tool_retrieval_errors.py`.
The same holds for `TriggerTypeNotFound`.
- No call sites change, so every endpoint is covered, including ones
added later.

On relying on a private method: `_make_status_error` is the hook the
generated base client declares (`raise NotImplementedError()`) and calls
for every status error, and `HttpClient` already overrides
`_prepare_request` from the same base. `composio-client` is pinned
exactly, and the new tests run through a real `HttpClient`, so a
generator change that altered the hook would fail CI at the version bump
rather than silently regress.

Left alone:

- **Transport-level errors.** `APIConnectionError` and `APITimeoutError`
are raised by the base client without going through
`_make_status_error`, so an HTTP timeout still escapes `except
ComposioError`. (The issue said timeouts were already covered; that was
true only for the SDK's own `ComposioSDKTimeoutError` from
`wait_for_connection`, not for HTTP timeouts.) Happy to follow up if you
want those covered too.
- **The existing `composio.client.ComposioAPIError` alias** still points
at the generated `APIError`, unchanged. The name I floated on the issue
would have collided with it, and this approach doesn't need a new public
class.

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

## How Has This Been Tested?

`python/tests/test_client_errors.py` runs a real `HttpClient` against an
`httpx.MockTransport`. Each status (400, 401, 403, 404, 409, 422, 429,
500, and an unmapped 418) raises an error that is both a `ComposioError`
and the expected generated class, with `status_code` preserved. Without
the fix, 12 of the 13 new tests fail.

From `python/`, Python 3.12, `composio-client==1.43.0`:

```
$ pytest tests/ -q
2072 passed, 1 skipped
$ ruff check --config config/ruff.toml composio tests
All checks passed!
$ ruff format --config config/ruff.toml --check composio/client/__init__.py tests/test_client_errors.py
2 files already formatted
$ mypy --config-file config/mypy.ini composio
Success: no issues found in 58 source files
```

Live check against production with an invalid key:

```python
from composio import Composio
from composio.exceptions import ComposioError

try:
    Composio(api_key="ak_invalid").create(user_id="u")
except ComposioError as e:
    print(type(e), e.status_code)
```

On `next` this raises `composio_client.AuthenticationError`, which
escapes the handler. On this branch the handler catches it, and it is
still an `AuthenticationError` with status 401.

## 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 docs change; the public API
is unchanged)
- [x] I added tests or explain why not applicable
- [ ] I added a changeset if this change affects published packages
(Python-only change; CONTRIBUTING asks for changesets on published
TypeScript packages)

## Additional context

Found while integrating the Python SDK into
[Inferra](https://github.com/deepgori/inferra), where a GitHub-issue
filer caught `ComposioError` and missed the invalid-key path.

---------

Co-authored-by: jkomyno <alberto@composio.dev>
Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
2026-09-21 21:46:27 +04:00
Alberto Schiabel d4d306073c fix(sdk): save tool-only MCP updates and connection-management intent (#4512)
This PR:

- closes
[PLEN-3890](https://linear.app/composio/issue/PLEN-3890/fix-mcp-lifecycle-update-configuration-defects)

- fixes `composio.mcp.update()` dropping parts of the requested
configuration: tool-only updates (`allowedTools` without `toolkits`)
sent no tools field at all, and updates with toolkits sent the
create-time `custom_tools` alias, which the update endpoint never reads
- inverts `manuallyManageConnections` into `managed_auth_via_composio`
on update, so `manuallyManageConnections: true` no longer stores
"Composio manages auth" (create and generate already inverted it)
- builds the update body sparsely: each provided field is sent
independently, omitted fields are left out entirely instead of being
sent as `undefined`
- keeps the auth config of a `{ toolkit, authConfigId }` toolkit entry
in `create()` and `update()` instead of discarding it via an `else if`
(Python already extracted both)
- sends `allowed_tools` instead of the deprecated `custom_tools` alias
from `create()` too, in both SDKs; Python previously raised `TypeError`
(masked as `ValidationError`) for the removed `custom_tools` kwarg
against the pinned `composio-client` 1.43.0
- adds regression coverage for both SDKs and a changeset for
`@composio/core`

## Context

Found by the September SDK + Composio client hackathon (Area 5, MCP
lifecycle), where the two worst frictions were "successful SDK updates
that do not save the requested configuration" and reversed manual
connection management. The hackathon's third Area 5 finding — PATCH
accepting nonexistent tool slugs — is an Apollo-side defect, fixed
separately in ComposioHQ/platform#13015. `MCP.ts` and the Python
`mcp.py` are identical between `next` and `main`, so this merges cleanly
to the beta channel afterwards.
2026-09-21 21:37:22 +04:00
Alberto Schiabel 62e51e838f chore(deps): refresh safe dependencies and Effect v4 (#4538)
## Summary

Refreshes the safe TypeScript, Python, and GitHub Actions dependency
surface in one maintainer-owned change. Effect 4 rc.115, Vitest 5, the
vendored Effect source, CLI migrations, and agent guidance move
together, while known incompatible boundaries stay pinned. The Effect v4
config schemas preserve unknown fields across `config.json` and
`user_data.json` read-update-write cycles.

Fixes #4535

## Changes

- Keeps Cloudflare Workers fixtures on Vitest 4 until
`@cloudflare/vitest-pool-workers` supports Vitest 5.
- Keeps Mastra on the Workers-compatible versions and AG2 below 1.0
because AG2 1.x no longer ships the imported `autogen` module.
- Removes the unused package-level `pnpm` dependency instead of changing
the repository's pinned pnpm 11 toolchain.
- Migrates the Effect CLI APIs, Eve callback contract, provider peer
ranges, and repository skills required by the selected upgrades.
- Preserves unknown CLI settings when `config.json` and `user_data.json`
are read, updated, and written back.
- Uses immutable SHA pins for the refreshed Claude Code actions and adds
release metadata for the affected published TypeScript packages.

## Type of change

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

## How Has This Been Tested?

- `pnpm install --frozen-lockfile` with pnpm 11.8.0
- `pnpm typecheck`
- `pnpm build:packages`
- `pnpm --filter @composio/cli test` — 1,400 passed, 1 skipped,
including targeted persistence regressions for `config.json` and
`user_data.json`
- Package tests — 28 workspace tasks passed
- Example typechecks/tests and all Cloudflare dry-runs
- Provider compatibility, experimental/Eve, Mastra, CLI keyring, and
JSON-schema Effect checks
- Agent-skill validation, routing validation, Effect skill example
compilation, and peer-dependency checks
- All three Python `uv lock --check` runs
- `nox -s tst_autogen`, `nox -s snt`, and `nox -s chk type_inference`
- Production dependency audit completed with the repository's three
existing ignored advisories

Docker CLI E2E was not run locally because the Docker daemon is
unavailable. The exact root lint command also enters the vendored Effect
submodule, whose checkout does not install its `@effect/oxc/oxlint`
plugin; scoped lint over the changed non-vendor files passed.

## Screenshots (if applicable)

Not applicable.

## Checklist

- [x] I have read the Code of Conduct and this PR adheres to it
- [ ] 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

The dependency migrations are covered by the focused and workspace
suites. Two targeted regression tests verify that CLI updates preserve
unknown fields in `config.json` and `user_data.json`.

## Additional context

The Connect client sync retains its existing `Bash(curl *)` permission
while moving the removed `allowed_tools` input to `claude_args`. A
separate hardening change should move logo downloads outside the
model-controlled shell boundary.

---

[![Compound
Engineering](https://img.shields.io/badge/Built_with-Compound_Engineering-6366f1)](https://github.com/EveryInc/compound-engineering-plugin)
2026-09-21 15:23:42 +04:00
Alberto Schiabel c7843d8a3a feat(core): return session config from session.update() (#4533)
This PR:

- makes `session.update()` resolve to the updated server-side session
configuration instead of `void`
- exposes that configuration as `session.config` (new
`ToolRouterSessionConfig` type) on sessions from `create()`, `use()` and
attach, so the toolkit/tool allowlist is readable without dropping to
the raw client
- renames the private SDK-config member on `ToolRouterSession` to
`sdkConfig`, ending the runtime name clash that made `session.config`
look like the SDK's `ComposioConfig`
- applies the same change to the Python `ToolRouterSession` (`config`
attribute, `update()` returns it)
- adds a minor changeset for `@composio/core`

## Context

After `sessions.use(id)` there was no way to know the session's
allowlist, and `update()` threw the response away except for
`configVersion` / `preload` / `sandbox` / `warnings`. Hackathon feedback
(area 8).
2026-09-18 18:29:08 +02:00
shams haroon aefc3ec897 feat(typesafe): add TypeSafe Jev provider for TypeScript and Python (#4513)
## Summary

Adds TypeSafe Jev providers for TypeScript and Python that turn tool
schemas and a request into a call, a partial call, or an abstention.

## What changed

- adds `@composio/typesafe`, a provider for TypeSafe's Jev model. Jev
has no tool calling, so `composio.tools.get()` compiles tools into typed
questions and `decide` returns a `call`, a `partial` call, or an
`abstain`, each with a confidence
- adds `execute` for a user ID or a session: caller arguments complete a
`partial`, and a tool tagged `destructiveHint` routes at a fixed floor
of 0.9 and needs `confirm: true`
- adds the companion helpers `shortlistTools` and `confidenceGate` (a
`beforeExecute` modifier that fails closed) for use with other providers
- adds `composio-typesafe`, the Python counterpart with sync and async
clients; both test suites compile one shared question corpus, so both
SDKs ask Jev the same questions for the same tool
- registers the package in the provider-compatibility release gate, adds
a `minor` changeset, the `ts/examples/typesafe` example, a Python demo,
and a dedicated `py.test.yml` step
- exempts only `@typesafe-ai/sdk@0.6.0` from `minimumReleaseAge`
(publisher, SLSA provenance, and the absence of install scripts were
checked by hand), and sets `engines.node` to `>=24.17.0` for this
package because the SDK terminates the process after a handled
cancellation on older Node.js releases (typesafe-ai/typesafe-sdk-js#2)

## Usage

```typescript
const provider = new TypesafeProvider();
const composio = new Composio({ provider });

const toolSet = await composio.tools.get('user_123', { tools: ['GITHUB_LIST_REPOSITORY_ISSUES'] });
const decision = await provider.decide(toolSet, 'List the closed issues of ComposioHQ/composio');

if (decision.kind !== 'abstain') {
  // Jev binds closed-set arguments (enums, booleans, arrays of enums). Free text comes from you.
  await provider.execute('user_123', decision, { arguments: { owner: 'ComposioHQ', repo: 'composio' } });
}
```

## Behavior notes

- `abstain` means only that the model judged so. A failed request throws
one `TypesafeApiError` whose `reason` tells rate limits, timeouts, and
rejections apart, and a malformed response throws
`TypesafeMalformedResponseError`. No error holds state, argument values,
response content, or the SDK's own error.
- Routing and the action gate see `request` only, so text in `context`
cannot change which tool is picked. `contextScope: 'all'` opts out.
- State is never truncated: over-budget state, unknown top-level state
keys, and non-JSON values throw.
- The options are `client`, `apiKey`, `model`, `thresholds`, and
`contextScope`. The provider builds its client at log level `warn`, so
`TYPESAFE_LOG_LEVEL=debug` cannot print request bodies.
- Root-level `allOf`, `anyOf`, and `oneOf` schemas are rejected
explicitly in both SDKs, including after `$ref` resolution, so composed
requirements cannot silently disappear. Property-level composition
remains supported as documented.
- Completing a partial decision requires an own, non-`undefined`
argument value in TypeScript; inherited names such as `toString` do not
satisfy required arguments. Supplied `__proto__` keys are preserved as
own data properties.

## Validation

- 147 TypeScript provider tests and 141 Python provider tests pass. The
11 new missing-argument regression cases fail on the original
implementation and pass with the fixes.
- Typecheck, Oxlint, Prettier, the tsdown build with ATTW/publint, Ruff,
mypy, type-inference, and release-gate checks passed locally.
- All 13 opt-in live tests passed across the TypeSafe-only and
Composio-backed suites against real Jev 1.13.0. These tests make
decisions without executing external tools.
- The actual TypeScript and Python Hacker News examples both ran end to
end against production APIs: fetch tools, decide, detect the missing
username, supply `pg`, and execute the read-only lookup. Both returned
the live profile for `pg`.

Not in this PR: the docs page, which needs the first npm publish so its
snippets compile. The first npm and PyPI publishes and a
`TYPESAFE_API_KEY` CI secret are manual steps.

```mermaid
flowchart LR
  A[composio.tools.get] --> B[compile tools into questions]
  B --> C[decide: state + questions]
  C --> D{Jev answers}
  D -->|none fits, no action, low confidence| E[abstain]
  D -->|required arguments missing| F[partial]
  D -->|everything bound| G[call]
  F -->|caller arguments| H[execute]
  G --> H
  H -->|destructive tool| I[needs confirm: true]
```
2026-09-17 20:55:51 -04:00
shams haroon cf15b62e60 fix(typesafe): guard required arguments in compilation and execution 2026-09-17 20:23:17 -04:00
jkomyno cc1248370b fix(typesafe): block null-leaf container swaps and tighten gate option checks
- Treat dict/list as their own leaf kind when asserting a redactor masks
  only, so a redactor cannot replace a JSON null leaf with an object or
  array while the gate would approve the altered call (Python now matches
  the TS scalarTypeOf behavior).
- Reject non-number gate thresholds ('', '0.9', true) at construction on
  the TS side, matching the strict Python check.
- Omit the gate state context key when getContext returns null, matching
  Python and decide's context handling.
- Pin all three with regression tests on both SDKs, and assert the
  context key is absent from the sent state when no getContext is given.
2026-09-17 19:03:21 +02:00
jkomyno 8878a9bcc5 fix(typesafe): fail closed on redaction structure changes and invalid gate options
Review findings from the TypeSafe (Jev) provider PR, fixed in both SDKs:

- The confidence gate enforces a masking-only redaction contract: the redacted
  arguments must keep the original JSON structure (same keys, same array
  lengths) with every leaf replaced by a value of the same scalar type, or the
  call is blocked. Jev can no longer approve a call that differs from the one
  that runs.
- `onUnavailable` is validated when the gate is built, in the companion both
  entry points share: a typo'd mode raises TypesafeInvalidOptionsError at
  construction instead of failing open at check time.
- `minItems` is carried through classification and compilation into array
  arguments, and a selection with fewer members than `minItems` counts as not
  stated, so a required array stays missing instead of executing with `[]`.
- The boolean class gains `nullable`: a ['boolean', 'null'] property compiles
  to a yes/no/null Choice so Jev can bind null; plain booleans still compile to
  yes/no only, and one-boolean enum sets stay open-ended.
- `stable()` rejects `undefined` wherever it appears instead of rewriting it,
  and the gate builds its state without a `context` key when there is no
  context, so a context-less gate call still works.
- `Probability` and the classify number type accept JSON integers, so an API
  score of 0 or 1 and integer `maxItems`/`minItems` parse instead of marking
  the property open-ended or the response malformed. Bools, strings, NaN, inf,
  and out-of-range values are still rejected.
2026-09-17 18:33:37 +02:00
jkomyno 1501ae6fdb test: classify the TypeSafe provider in the $ref handling contract 2026-09-17 17:53:02 +02:00
Brendan O'Leary 705888faff docs: describe the four verdict hints sessions filter on (#4470)
## Why
Requirement 1 of the PRD: sessions accept all four verdict hints. The
Configuring Sessions tag table listed the four MCP-spec hints, two of
which (idempotentHint, openWorldHint) are set on a minority of tools.
Every tool carries at least one of readOnlyHint, createHint, updateHint,
destructiveHint.

## What
- Tag table leads with the four verdict hints and says every tool
carries at least one; idempotentHint and openWorldHint noted as accepted
with partial coverage.
- Callout: the v3 tools endpoints default to the pinned version
00000000_00, sessions read latest.
- Python example uses createHint. The TypeScript twoslash example stays
on readOnlyHint so docs CI passes against the published SDK; switch it
to createHint when merging, after #4467 is released.
- Python and TypeScript SDK reference docs list the widened enum.

## Merge after
API: platform#12843 (accept createHint and updateHint). SDK:
composio#4467 released.

PRD:
https://app.notion.com/p/composio/Session-Governance-via-hints-Across-toolkits-3daf261a6dfe80df8e0ce337a2b26e08
Linear workstream:
https://linear.app/composio/project/sessions-execution-governance-a0942233a0d0

Stack order (merge top to bottom, each after its API change is
deployed): D1 verdict hints, D2 precedence, D3 proxy execute toolkit
lists, D4 MCP classification, D5 proxy execute API key permission.

Verification, run in `docs/` at the top of the stack (D5 head, which
contains this PR): `bun run types:check` passes, `bun run build`
compiles (twoslash blocks type-check against the published
`@composio/core`), `bun run lint:links` reports 0 errors, `bun run test`
568 pass. `pnpm exec prettier --check` flags the changed mdx files on
`next` already, so no reformatting was applied.

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

https://claude.ai/code/session_01VHkYsmhteM1jJQoaoruiP3
2026-09-17 11:48:08 -04:00
jkomyno b2cf623045 refactor(py): mirror the TypeSafe provider simplification
Drop tool_thresholds, describe, and log_level, collapse the API error subclasses, validate decisions against the public TypedDicts, and read the question corpus from the TypeScript package instead of a second copy.
2026-09-17 17:42:24 +02:00
jkomyno 36a7ed4405 feat(py): add composio-typesafe provider for TypeSafe Jev 2026-09-17 16:55:21 +02:00
Alberto Schiabel b39a9b30e1 docs(py): render raises sections and normalize reST in SDK reference (#4491)
This PR:

- fixes the raw `:raises ...:` reST directives leaking into the
generated Python SDK reference, flagged by [Greptile on the
auto-generated docs
PR](https://github.com/ComposioHQ/composio/pull/4479#discussion_r4004672537)
- teaches `python/scripts/generate-docs.py` to parse `:raises Exc:`
docstring fields (plus `:raise`/`:except`/`:throws` synonyms) into a
structured `**Raises**` section, with indented continuation-line support
- normalizes inline reST in all rendered prose:
`:class:`/`:func:`/`:meth:`/`:mod:` roles honor `~` short-name
semantics, and double-backtick literals become single-backtick inline
code
- regenerates `docs/content/reference/sdk-reference/python/` pages
- adds regression tests for raises parsing and reST normalization in
`python/tests/test_generate_docs.py`

## Context

The Python SDK reference pages are generated by
`python/scripts/generate-docs.py` (workflow: `generate-sdk-docs.yml`),
so the fix lives in the generator rather than the MDX — hand-edits would
be overwritten by the next auto-regen PR. Unrecognized `:raises` lines
previously fell through into the `:returns:` description text.
2026-09-16 14:57:04 +02:00
Alberto Schiabel 11de45889a fix(core): contain async Pusher subscription errors (#4448)
## Summary
`PusherService.subscribe` binds `pusher:subscription_error` after the
Pusher subscription call returns. `pusher-js` dispatches this event
asynchronously without catching listener exceptions, so authentication,
permission, server, or network subscription failures could escape as
uncaught exceptions in Node applications.

Fixes #4445

## Changes
- Log asynchronous Pusher subscription errors at the SDK error boundary
instead of throwing from the event callback.
- Add regression coverage that emits `pusher:subscription_error` after
`subscribe()` resolves and verifies that it does not throw.
- Add a patch changeset for the fixed `@composio/core`/`@composio/slim`
package group.

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

## How Has This Been Tested?
- Node `v24.17.0` / pnpm `11.8.0`
- `pnpm --filter @composio/core exec vitest run
test/services/pusher.test.ts test/utils/pusher.test.ts` — 2 files, 5
tests passed
- `pnpm --filter @composio/core test` — 55 test files passed; 1,289
tests passed and 2 existing tests reported expected failures; command
exited successfully
- `pnpm --filter @composio/core typecheck`
- `pnpm lint` — passed with existing repository warnings
- `pnpm validate:changesets`
- `pusher-js` `v8.6.0` runtime probe confirmed that an exception thrown
from a `pusher:subscription_error` listener reaches Node's
`uncaughtException` handler; the regression test verifies the SDK
callback no longer throws.

## 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
This patch is intentionally limited to the live `PusherService` path.
XHR timeout handling is a separate concern and is not included here. The
older unreferenced `PusherUtils` helper is unchanged to keep this fix
scoped to the path used by `Triggers`.
2026-09-16 14:56:45 +02:00
jkomyno c2e70f66a6 fix(py): route establish-time subscription failures through on_subscription_error
pysher performs the channel-auth request synchronously inside
pusher.subscribe(), so an auth rejection raised on the websocket thread
before pusher:subscription_error could ever be bound or fire. The new
on_subscription_error callback was skipped for exactly the failures it
documents, and callers waited out the full connect timeout for a
generic ComposioSDKTimeoutError.

- _connection_handler catches subscribe() failures and routes them
  through the error path (log + callback with {'error': ...}).
- The failure is recorded on the subscription and the connect() wait
  loop re-raises it on its next poll, so subscribe() fails promptly
  with the underlying error and still tears down the pusher.
- Update the Python reference, guide, and docstrings; add regression
  coverage for the handler routing, the failure record, and fast-fail.

Addresses the Cursor Bugbot comment on triggers.py:1023.
2026-09-16 14:54:14 +02:00
Alberto Schiabel 0601190ca0 Merge branch 'next' into fix/openai-responses-demo-docs 2026-09-16 14:47:24 +02:00
jkomyno 27701dd541 feat(py): add optional on_subscription_error for trigger subscriptions
Mirror the TypeScript API surface from the previous commit:

- Triggers.subscribe accepts an optional on_subscription_error callback,
  threaded through _SubcriptionBuilder.connect and bound to pysher's
  pusher:subscription_error event on the trigger channel.
- TriggerSubscription._handle_subscription_error logs the failure at the
  SDK boundary and invokes the callback with the parsed payload (or
  {'raw': frame} for malformed frames); callback exceptions are
  contained and logged so a faulty handler cannot tear down pysher's
  dispatch thread.
- The parameter is optional; existing callers are unaffected.
- Update the Python triggers reference and the subscribing-to-events
  guide.

Python never bound pusher:subscription_error at all, so subscription
failures after connect() were previously invisible to hosts.
2026-09-15 16:58:37 +02:00
jkomyno 14cb6b2e75 docs(openai): align responses demo on gpt-5 and print aggregated output
- Use gpt-5 like the rest of the repo's examples (getting-started.md,
  the pre-PR demo) instead of the gpt-5.2 id used nowhere else.
- Print response.output_text instead of dumping the raw Response object,
  matching the final line of the demo.
2026-09-15 16:42:47 +02:00
jkomyno 39e3c35531 docs(openai): rename assistant demo to responses demo
The demo no longer uses the deprecated Assistants API; the filename now
matches the Responses API content.
2026-09-15 16:40:59 +02:00
Brendan O'Leary 3f31ef9609 Merge remote-tracking branch 'origin/next' into codex/docs-priority-guides
# Conflicts:
#	docs/tests/static/product-navigation.test.ts
2026-09-15 10:12:04 -04:00
Brendan O'Leary a92d4920f0 docs: finish priority guide integration 2026-09-15 10:08:10 -04:00
jkomyno 3271679ee0 docs(py): render Raises sections and normalize reST in generated SDK reference
- parse :raises Exc: docstring fields (plus Sphinx synonyms) into a
  structured Raises section instead of leaking raw directives into the
  Returns description
- normalize inline reST in all rendered prose: roles (:class:, :func:,
  :meth:, ...) honor ~ short-name semantics; double-backtick literals
  become single-backtick inline code
- regenerate docs/content/reference/sdk-reference/python/ pages
- add regression tests for raises parsing and reST normalization

Flagged by Greptile on the legacy-repo auto-PR (ComposioHQ/composio#4479).
2026-09-15 13:12:25 +02:00
Palash Kala 82341760c4 docs: describe the four verdict hints sessions filter on
Every tool carries at least one of readOnlyHint, createHint, updateHint
or destructiveHint, but the tag table and SDK reference docs only listed
the four MCP-spec hints, two of which (idempotentHint, openWorldHint)
are set on a minority of tools. Lead with the four verdict hints and
note the pinned-version gotcha on the v3 tools endpoints. The TypeScript
twoslash example stays on readOnlyHint until @composio/core ships the
widened enum (composio#4467).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHkYsmhteM1jJQoaoruiP3
2026-09-13 21:49:13 +05:30
jkomyno 8bb1d29950 fix(core): raise tool not found only on 404/400
getRawComposioToolBySlug relabelled every client error, including an
invalid API key (401), as ComposioToolNotFoundError. Map only 404/400 to
not-found and wrap the rest in a new ComposioToolFetchError that keeps
the client error as cause. Toolkits.getToolkitBySlug compared against
the OpenAI APIError class, so its not-found branch never fired; import
the Composio client class instead. Python mirrors the mapping: an
unknown slug raises ToolNotFoundError (now a NotFoundError), anything
else propagates the composio_client error unchanged.

PRDE-1613

Claude-Session: https://claude.ai/code/session_017HtbhwMAKcfebo8HyXWa5s
2026-09-11 22:54:53 +02:00
Alberto Schiabel 85996c4a1d fix(sdk): harden pusher auth and cross-origin redirect headers (#4406)
This PR:

- wraps `pysher.Pusher` in `_ComposioPusher`, whose channel-auth POST
carries a `(5, 15)` connect/read timeout and raises
`TriggerSubscriptionAuthError` (a `TriggerSubscriptionError`) on a
transport failure, a non-200, or a response without an `auth` token —
pysher 1.0.8 sent it with no timeout and turned a non-200 into a bare
`AssertionError` on the websocket thread, on every (re)subscribe
- keeps that POST a plain `requests.post(..., timeout=...)` rather than
routing it through `safe_request`: the endpoint is built from the
configured Composio API base URL, a fixed trusted host, not a value from
a response, and the SSRF guard would refuse a local dev base URL
- validates `pusher_cluster` against `^[a-z0-9-]+$` (non-empty, at most
64 chars) before pysher formats it into `ws-{cluster}.pusher.com`,
raising `InvalidPusherClusterError` that names the shape violation
without echoing the value
- replaces the `unittest.mock.MagicMock` stand-in for pysher's
connection logger with a dedicated `logging.Logger` (`NullHandler`,
`propagate=False`, disabled), so `unittest` leaves the runtime import
graph while raw frames stay out of user logs; a test asserts the module
source no longer mentions `unittest`
- strips `Authorization`, `Proxy-Authorization`, and `Cookie` from the
next hop when `ssrfSafeFetch` or `safe_request` follows a redirect to a
different origin; same-origin hops keep them. Manual redirect following
bypasses both `fetch`'s cross-origin rule and `requests`'
`rebuild_auth`, so neither guard applied it before — the gap #4387 left
out
- `@composio/slim` has no mirrored source (its build copies
`core/dist`), so the changeset covers `@composio/core` and
`@composio/slim` as patches

Verified with `pytest tests/test_triggers.py tests/test_url_safety.py
tests/test_path_join_guardrail.py` (192 passed), `ruff check` / `ruff
format --check` on the changed files, `mypy --config-file
config/mypy.ini` on the three changed modules with the noxfile's stub
pins (no issues), `vitest run test/utils/ssrfGuard.test.ts` in
`@composio/core` (42 passed), `pnpm typecheck` at the root (14 tasks
successful), and `oxlint` + `prettier --check` on the changed TypeScript
files.

https://claude.ai/code/session_016ZuBv7JhVdSYTLYcTy2VJr
2026-09-09 21:29:18 +02:00
Alberto Schiabel 1049942edb fix(py): raise requests and urllib3 floors past open advisories (#4404)
This PR:

- raises the `composio` package floors to `requests>=2.32.4` and
`urllib3>=2.7.0` in `python/pyproject.toml`, `python/setup.py`, and
`uv.lock`
- closes GHSA-9hjg-9r4m-mvj7 (`requests` `.netrc` credential leak via
malicious URLs) for downstream installs; `url_safety` fetches user- and
server-supplied URLs through a `trust_env` session
- closes GHSA-mf9v-mfxr-j63j (decompression-bomb guard bypass in the
`urllib3` streaming API that `_fetch_file_from_url` relies on for its
size limit) and GHSA-qccp-gfcp-xxvc (sensitive headers forwarded across
origins)
- the workspace lock already resolves 2.34.2 / 2.7.0, so only the
`requires-dist` specifiers change; `uv lock --check` passes and `import
composio` still works
- documents the advisory IDs next to each floor so the next bump has
context
2026-09-09 18:37:25 +02:00
Alberto Schiabel ba85f4d183 fix(sdk): honor Fetch redirect semantics in both SSRF guards (#4387)
This PR:

- builds on top of https://github.com/ComposioHQ/composio/pull/4271,
whose commit it carries unchanged
- applies the Fetch standard's redirect method/body rules in **both**
SSRF guards via `_redirect_rewrite` / `redirectRewrite`: a `303` retries
as a bodiless request, a `301`/`302` does the same for a `POST`, and
`307`/`308` replay both
- narrows `ssrfSafeFetch` to the five statuses the Fetch standard calls
a redirect, so a `304` or `305` carrying a `Location` is returned to the
caller instead of followed — Python already used
`_REDIRECT_STATUS_CODES`
- drops `params` after the first hop in `safe_request`, since `Location`
carries the query for the target it names and re-appending handed a
query-string credential to a target that never asked for one
- purges the union of the Fetch `request-body-header` set and the two
`requests` also drops, identically on both sides
- blocks the IPv6 transition ranges the TypeScript CIDR list missed —
6to4 `2002::/16`, Teredo and the rest of `2001::/23`, local-use NAT64
`64:ff9b:1::/48`, `100::/64`, `2001:db8::/32`, site-local `fec0::/10` —
and the IPv4/IPv6 multicast and `192.88.99.0/24` ranges Python's
`is_global` missed

## Context

Both guards follow redirects by hand so every hop is revalidated against
the address blocklist. That also means neither inherits the method and
body rewriting `fetch` and `requests` would have done, so an upload
answered with a `303` was replayed — payload and all — at a result URL
that expects a GET.

https://github.com/ComposioHQ/composio/pull/4271 landed that rule in
Python only, which left the two SDKs disagreeing on the same wire
behavior. Reviewing for that divergence surfaced the redirect-status
set, the `params` replay, and the address-blocklist gaps above.
`2002:7f00:1::` is 6to4 for `127.0.0.1`, and it passed the TypeScript
guard as a public address.

Verified with `pytest python/tests/test_url_safety.py` (56 passed) and
`vitest run` in `@composio/core` (54 files, 1280 passed), plus `ruff`,
`tsc --noEmit`, `oxlint` and `prettier`. Fail-before confirmed: 10 of
the new TypeScript cases and 5 of the new Python cases fail against the
unmodified guards.

Two known gaps are deliberately left out, each deserving its own change:
neither guard strips `Authorization`/`Cookie` on a cross-origin
redirect, and a non-seekable Python body is re-sent exhausted on a `307`
where TypeScript throws a bare `TypeError` on a consumed
`ReadableStream`.

https://claude.ai/code/session_01SB3ZJdvoqBcRrWb2toWVrX

---------

Co-authored-by: ump45nose <52391318+ump45nose@users.noreply.github.com>
2026-09-08 20:51:12 +02:00
Alberto Schiabel 53c024a9c8 fix(py): keep local results when remote multi-execute transport fails (#4386)
## Summary

Follow-up to #4310. The TypeScript SDK (since #3800) catches a thrown
backend call for the remote half of a mixed
`COMPOSIO_MULTI_EXECUTE_TOOL` batch and turns it into one failure entry
per remote slug, so completed local results are not lost. The Python SDK
still let the exception escape `_route_multi_execute`, discarding every
local result that had already run.

This ports the TypeScript behavior so both SDKs return the same shape on
a remote transport failure.

Fixes #

## Changes

- Catch the remote future's exception in `_route_multi_execute` and keep
`str(error)`, falling back to `Remote tool execution failed` when the
message is empty (same fallback as TS).
- Synthesize `{response: {successful: False, data: {}, error},
tool_slug, error}` for each remote index and merge them in original
request order.
- Recompute `total_count` / `success_count` / `error_count` on transport
failure, as TS does.
- Add two regression tests mirroring the TS cases in
`customToolRouting.test.ts`: local results preserved with per-tool
remote errors, and the empty-message fallback.

## Type of change

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

## How Has This Been Tested?

- `uv run --locked --group dev pytest tests/test_custom_tools.py -q`: 87
passed.
- `uv run --locked --group dev nox -s chk`: ruff and mypy clean.
- Without the source change, the new
`test_remote_transport_failure_keeps_local_results` raises
`RuntimeError: remote unavailable` out of `_route_multi_execute`.

## 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
(Python does not use Changesets)

## Additional context

TypeScript reference:
`ts/packages/core/src/models/ToolRouterSession.ts`, the
`remoteErrorMessage` branch, and the test "should preserve successful
local results when remote transport fails".

https://claude.ai/code/session_01PAXMbiZd3qPoJ8Z9uPvEAb
EOF -R ComposioHQ/composio
2026-09-08 18:11:25 +02:00
CoralGarden52 2c4339a859 fix(python): preserve mixed multi-execute result order (#4310)
## Summary

`ToolRouterSession._route_multi_execute` currently concatenates remote
results before local results and assigns new indexes from that
concatenated list. For a request such as `[LOCAL_TOOL, REMOTE_TOOL]`,
callers receive `[REMOTE_TOOL, LOCAL_TOOL]`, so code that correlates
`results[index]` with the requested tools can use the wrong result.

This brings the Python implementation in line with the merged TypeScript
behavior in [#3800](https://github.com/ComposioHQ/composio/pull/3800):
preserve each tool's original request index, restore that order after
local/remote execution, and then assign contiguous result indexes.

Fixes #

## Changes

- Preserve the original index on locally executed result entries.
- Map remote sub-batch results back to their original request indexes
before merging.
- Sort the merged results by original index and re-index them
sequentially.
- Update the mixed local/remote regression test to assert request order
and indexes.

## Type of change

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

## How Has This Been Tested?

- `uv run --locked --group dev pytest tests/test_custom_tools.py -q` —
69 passed.
- `uv run --locked --group dev nox -s tst -- tests/test_custom_tools.py`
— 69 passed.
- `uv run --locked --group dev ruff --config config/ruff.toml check
composio/core/models/tool_router_session.py tests/test_custom_tools.py`
— passed.
- Ruff format check on both changed files — passed.
- Verified the regression test fails on the pre-fix implementation and
passes after the fix.

## 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
- [ ] 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

This change is limited to Python multi-execute result ordering.
All-local and all-remote fast paths remain unchanged. No changeset is
needed because this repository does not use Changesets for Python
package changes.

Signed-off-by: CoralGarden52 <2193436736@qq.com>
2026-09-08 16:28:24 +02:00
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
Adesh Deshmukh 7d36de8c0a fix(python-sdk): isolate default provider per Composio instance (#4370)
Construct a fresh OpenAIProvider per SDK instance instead of sharing a
module-level singleton whose execute_tool binding was overwritten by the
last-constructed instance, silently routing tool execution through the
wrong client/API key. Regression test in tests/test_sdk.py.

Fixes #4369

Co-authored-by: Adesh Deshmukh <adeshkd123@gmail.com>
Claude-Session: https://claude.ai/code/session_015YPz5SzeScR9TkgoRi2p1F
EOF -R ComposioHQ/composio
2026-09-08 13:33:10 +02:00
CoralGarden52 85d4923507 fix(python): dereference $ref/$defs in Google provider (#4297)
## Summary

The Python Vertex AI Google provider rebuilt tool parameter schemas from
`properties` and `required` without resolving internal `$ref`/`$defs`
references first. As a result, referenced properties were sent as
dangling references and could not be interpreted by Vertex AI.

This change dereferences internal schema references before the existing
Google-specific translation. It follows the provider behavior fixed in
[TypeScript PR #4288](https://github.com/ComposioHQ/composio/pull/4288).

## Changes

- Dereference Google provider input schemas with the existing
`dereference_json_schema` helper.
- Use the resolved schema when extracting properties and required
fields.
- Add a regression test covering a property defined through
`$ref`/`$defs`.

## Type of change

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

## How Has This Been Tested?

- `pytest tests/test_google_provider.py tests/test_json_schema.py
tests/test_provider.py -q -k 'not TestLangchainReservedKeywords and not
TestLangchainFreeFormObjectArguments'` — 59 passed, 4 skipped, 5
deselected.
- `ruff check --config config/ruff.toml
providers/google/composio_google/provider.py
tests/test_google_provider.py` — passed.
- `ruff format --check providers/google/composio_google/provider.py
tests/test_google_provider.py` — passed.
- `mypy --config-file config/mypy.ini
providers/google/composio_google/provider.py
tests/test_google_provider.py` — passed.

## 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 TypeScript
packages

## Additional context

This is a Python-only provider fix; no TypeScript changeset is required.
No existing issue was found for the Python provider, so this PR includes
the minimal reproduction and regression test directly.

---------

Co-authored-by: jkomyno <alberto@composio.dev>
2026-09-07 16:00:20 +02:00
Alberto Schiabel 684a392816 chore(python): prepare 0.21.1 release (#4361)
## Summary

- bump the Python SDK and all provider package versions to `0.21.1`
- regenerate the root `uv.lock` from the updated workspace metadata
- keep the existing coordinated changelog as the release authority

## Verification

- `pnpm test:release-workflow`
- `make build` (26 artifacts)
- `python -m twine check python/dist/*`
2026-09-04 20:50:30 +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
Swapnil Yadav fc681b87b2 Fix .jpg resolving to non-standard image/jpg instead of image/jpeg (#4333)
## Problem

`composio.utils.mimetypes.guess()` maps `.jpg` to `"image/jpg"`:

```python
".jpe": "image/jpeg",
".jpeg": "image/jpeg",
".jpg": "image/jpg",   # <- non-standard
```

`image/jpg` is not a registered IANA media type. It is inconsistent
with:

- its own sibling extensions `.jpe`, `.jpeg`, and `.jfif-bnl`, which all
map to `image/jpeg`
- Python's standard library: `mimetypes.guess_type("x.jpg")` returns
`image/jpeg`
- IANA, which registers `image/jpeg`

`guess()` feeds the `Content-Type` used for presigned uploads
(`_upload_to_presigned_url`), so `.jpg` files are uploaded and stored
with a non-standard content type.

## Fix

Map `.jpg` to `image/jpeg`. One line.

The reverse `_MIME_TO_EXT` map keeps its `"image/jpg": "jpg"` entry on
purpose: the SDK should still accept `image/jpg` when a server sends it
in a `Content-Type` header (liberal in what it accepts, strict in what
it produces).

## Tests

Added coverage in `tests/test_mimetypes.py`: `.jpg`/`.jpeg` in the
known-extensions table plus a dedicated test documenting that `.jpg`
resolves to `image/jpeg`. Verified the new tests fail on the old value
and pass after the fix; the full `test_mimetypes.py` (35 tests) passes.
2026-09-03 12:27:09 +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 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 28bcb190d9 refactor(python): collapse redundant download error handlers, cover both
Review follow-up on the download size cap.

`requests.exceptions.RequestException` subclasses `OSError`, so the two
handlers added for the write loop were byte-identical and the second already
subsumed the first. Collapse them into one `except OSError` and say why in a
comment, so the next reader does not re-add the redundant clause.

Route partial-file cleanup through `_discard_partial_download`, which
suppresses cleanup failures: an `OSError` from `unlink` would otherwise
replace the `ResponseTooLargeError` or transport error the caller needs.

Cover the two error paths that had no tests: a transport failure mid-stream
and a failing write both raise `ErrorDownloadingFile` and leave no partial
file behind. Without the handler the write failure escapes as a raw
`OSError(28)` — the defect these pin.

Claude-Session: https://claude.ai/code/session_01K1hH9PMmd6KPKdkACX553z
2026-08-28 09:54:23 +02:00
jkomyno 54d07dc5f5 fix(python): cap automatic file download size at 100 MiB
`FileDownloadable.download` streamed the response straight to disk with no
byte accounting, so an untrusted `s3url` could fill the disk. Add the same
`Content-Length` pre-check plus authoritative streamed-byte counter the
sibling `_fetch_file_from_url` already uses, capped at `_MAX_RESPONSE_SIZE`
and overridable per call via `max_size`.

Also close two gaps the write loop left open: an `OSError` from `fd.write`
(disk full, permissions) escaped the documented `ErrorDownloadingFile`
contract, and any failure left a partial file on disk that no caller was
told about. Every failure path now unlinks the partial file;
`ResponseTooLargeError` still propagates uncaught so callers see the limit.

Claude-Session: https://claude.ai/code/session_01K1hH9PMmd6KPKdkACX553z
2026-08-28 09:51:04 +02:00
jkomyno 3cbc7556f5 chore(sdk): prepare Python 0.21.0 and TypeScript 0.18.0 2026-08-27 19:27:19 +02:00
jkomyno f37c6fbec3 docs(strict-mode): correct provider support details 2026-08-27 15:51:18 +02:00
jkomyno 9feca8f95d fix(json-schema): accept document-root references in strict mode 2026-08-27 15:39:38 +02:00
jkomyno 507c4fe3a8 fix(python): preserve explicit empty tool schemas 2026-08-27 15:38:27 +02:00
Alberto Schiabel 81631f83f4 Merge branch 'next' into fix/strict-mode-keep-optional-parameters 2026-08-27 15:14:24 +02:00
Alberto Schiabel 64db269a33 fix(deps-dev): bump langchain-openai from 1.4.3 to 1.6.0 in /python in the pip-version group across 1 directory (#4196)
Bumps the pip-version group with 1 update in the /python directory:
[langchain-openai](https://github.com/langchain-ai/langchain).

Updates `langchain-openai` from 1.4.3 to 1.6.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langchain/releases">langchain-openai's
releases</a>.</em></p>
<blockquote>
<h2>langchain-openai==1.6.0</h2>
<p>Changes since langchain-openai==1.5.2</p>
<p>release(openai): 1.6.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39762">#39762</a>)
feat(core): add standard model exception types (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39538">#39538</a>)
fix(openai): raise clear error on unexpected response type in
<code>_create_chat_result</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39731">#39731</a>)</p>
<h2>langchain-openai==1.5.2</h2>
<p>Changes since langchain-openai==1.5.1</p>
<p>release(openai): 1.5.2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39719">#39719</a>)
fix(openai): preserve reasoning item boundaries (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39278">#39278</a>)
release(openai): 1.5.2a1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39709">#39709</a>)
feat(openai): extract gateway metadata from response headers when
available (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39706">#39706</a>)
chore(openai): update snapshots (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39657">#39657</a>)
fix(openai): support o-series models in
<code>get_num_tokens_from_messages</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/38710">#38710</a>)</p>
<h2>langchain-openai==1.5.2a1</h2>
<p>Initial release</p>
<p>release(openai): 1.5.2a1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39709">#39709</a>)
feat(openai): extract gateway metadata from response headers when
available (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39706">#39706</a>)
chore(openai): update snapshots (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39657">#39657</a>)
fix(openai): support o-series models in
<code>get_num_tokens_from_messages</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/38710">#38710</a>)
release(openai): 1.5.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39653">#39653</a>)
fix(openai): preserve streamed encrypted reasoning (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39635">#39635</a>)
chore(infra): support langsmith gateway in CI (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39651">#39651</a>)
release(openai): 1.5.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39629">#39629</a>)
feat(openai): support openai 3.0 SDK (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39613">#39613</a>)
chore(partners): bump langgraph floor in openai and huggingface
lockfiles (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39617">#39617</a>)
release(openai): 1.4.3 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39485">#39485</a>)
fix(openai): filter invalid tool calls from content (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39366">#39366</a>)
chore(openai): update guidance for responses API for OpenAI-compatible
providers (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39327">#39327</a>)
chore(openai): update docstring for
<code>include_response_headers</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39326">#39326</a>)
release(openai): 1.4.2 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39322">#39322</a>)
fix(openai): handle <code>ContextWindowExceededError</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39300">#39300</a>)
chore: bump the minor-and-patch group across 3 directories with 7
updates (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39187">#39187</a>)
fix(openai): filter langchain-generated content block IDs (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39209">#39209</a>)
fix(openai): preserve Responses <code>text</code> options (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39204">#39204</a>)
fix(openai): redact MCP <code>authorization</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39155">#39155</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39050">#39050</a>)
release(openai): 1.4.1 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39045">#39045</a>)
feat(anthropic,fireworks,openai): support langsmith gateway through env
var (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/38742">#38742</a>)
fix(openai): correct <code>gpt-5.3-chat-latest</code> profile (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39009">#39009</a>)
release(openai): 1.4.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/38983">#38983</a>)
chore: bump pillow from 12.2.0 to 12.3.0 in /libs/partners/openai (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/38999">#38999</a>)
feat(core): add <code>reasoning_effort</code> as a standard chat model
parameter (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/38887">#38887</a>)
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/38797">#38797</a>)
release(openai): 1.3.5 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/38785">#38785</a>)
feat(openai): support explicit prompt caching (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/38762">#38762</a>)</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/langchain-ai/langchain/commit/3478c28ef21435162cb67abbd2aaef67c7cd8981"><code>3478c28</code></a>
release(anthropic): 1.6.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39763">#39763</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/420dfc94516f57a4d8662132a55bc532afbc6045"><code>420dfc9</code></a>
release(openai): 1.6.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39762">#39762</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/85602c3676fbd51e098a01d7c66638719f529f84"><code>85602c3</code></a>
release(core): 1.6.0 (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39760">#39760</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/5c3538e83a24eaf819ecec066773a232dcd4a8e6"><code>5c3538e</code></a>
fix(core): resolve postponed annotations in
`StructuredTool._injected_args_ke...</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/9984a87fa5a6971c76cb12fc75b37ed74286b740"><code>9984a87</code></a>
feat(core): add standard model exception types (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39538">#39538</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/b3e9eef13c23c3a048f9846e1592b658f85f5f94"><code>b3e9eef</code></a>
chore(model-profiles): refresh model profile data (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39751">#39751</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/ded2a1fb3c06a9562d9079e42099020ecaca4060"><code>ded2a1f</code></a>
fix(core): allow deserializing <code>RunnablePick</code> (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/39753">#39753</a>)</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/04ae7447d72e61841905a41b309856c5191452fb"><code>04ae744</code></a>
fix(core): make <code>convert_to_openai_function</code> handle callables
and non-dict ma...</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/37f266278d780cd7ebdfbf1891ba92066192d687"><code>37f2662</code></a>
feat(langchain): support custom token_counter in
ContextEditingMiddleware (<a
href="https://redirect.github.com/langchain-ai/langchain/issues/3">#3</a>...</li>
<li><a
href="https://github.com/langchain-ai/langchain/commit/2019bf5ebe50324c548f67c2666a804343f9b772"><code>2019bf5</code></a>
fix(openai): raise clear error on unexpected response type in
`_create_chat_r...</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langchain/compare/langchain-openai==1.4.3...langchain-openai==1.6.0">compare
view</a></li>
</ul>
</details>
<br />
2026-08-27 15:13:51 +02:00
jkomyno 78fb09efdf test(py): preserve isolated Autogen regression coverage 2026-08-26 19:40:26 +02:00
jkomyno c5690031d3 fix(py): isolate incompatible provider dependencies 2026-08-26 19:40:26 +02:00
jkomyno 8fe03eff47 test(json-schema): add strict-mode edge cases enumerated with a second model
Extends strict-cases.json to 68 cases with shapes enumerated independently
(single-element and three-member type arrays, null-only and null-carrying
enum/const properties, nested compositions, nullable objects in arrays,
tuple and boolean items, conditional and dependency keywords, oneOf beside
anyOf, boolean and malformed properties, $ref siblings and chains, legacy
definitions next to $defs, non-string required entries, ten-level
nesting) plus null-omission pairs for nullable, composed and $ref-typed
arguments. Checks in the generator that derives the pinned JSON.

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

Claude-Session: https://claude.ai/code/session_01TDrxCHn2hg51HmxVstSUgs
2026-08-26 17:59:21 +02:00