mirror of
https://github.com/ComposioHQ/composio.git
synced 2026-09-22 11:46:35 +08:00
@composio/cli@0.2.32-beta.281
939 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8d5823be87 |
chore(ci): cache turbo, bun, and uv and cancel superseded pr runs (#3747)
This PR: - adds `concurrency` to the ten PR/push CI workflows: superseded **PR** runs are cancelled, while push runs use a per-run group (`github.run_id`) so every commit on `next`/`master` still gets its own run - caches turbo outputs (`.turbo/cache`) via a new opt-in `enable-turbo-cache` input on the `setup-node-pnpm-bun` composite (keyed per job — concurrent jobs save different task graphs), enabled in `ts.build`, `ts.test`, `ts.typecheck`, and all four `ts.test-e2e` jobs - enables turbo caching for `typecheck`, removes the incorrect `dist/` outputs from `test`, hashes root `tsconfig.base.json` in `build`/`typecheck` inputs, and adds `mise.toml`/`mise.lock` to `globalDependencies` so toolchain bumps bust every task hash (addresses the Bugbot finding) - fixes `@composio/slim`'s build to participate in the turbo graph: core moves to slim's `devDependencies` (published `dependencies` unchanged) and the script no longer shells out to `pnpm --filter @composio/core build`, which raced concurrent dependents by wiping `core/dist` mid-build and double-built core on every cold run — slim's `dist` remains byte-identical to core's; no changeset added since the published artifact is unchanged (say the word if policy wants one anyway) - fixes the `py.test` matrix cache to `~/.cache/uv` (it cached `~/.cache/pip` while installing via `uv pip`) and caches `~/.bun/install/cache` in the five docs workflows - adds `turbo.jsonc` to the TS workflows' path filters (previously unwatched) - release/publish workflows are deliberately untouched Measured (same commit, cold first attempt vs `gh run rerun` warm): ts.build 103s → 53s, ts.test 157s → 55s (`turbo test` 24/24 cached in 182ms), ts.typecheck 80s → 43s; py.test legs save a few seconds each (uv was already fast). Warm floor is checkout + mise + pnpm-install overhead. ## Context Implements `plans/001-ci-speed-caching-and-concurrency.md` from the advisor audit on `docs/sdk-v1-decision-records`. Implementation by codex against the plan, then self-reviewed and counter-reviewed; warm-rerun testing surfaced and fixed three latent issues (toolchain files absent from turbo's hash, first-writer-wins cache-key collision across jobs, and the slim out-of-band core rebuild race). |
||
|
|
e78ed312c6 |
fix(core): run all parallel OpenAI tool calls from the first choice (#3726)
This PR: - supersedes https://github.com/ComposioHQ/composio/pull/3712, preserving the original commits by @serhiizghama (kept as author/co-author) - fixes `OpenAIProvider.handleToolCalls` to execute **every** tool call in an assistant message — it previously only read `tool_calls[0]`, so parallel tool calls (on by default) were dropped and their `tool_call_id`s went unanswered, failing the next request with the "must be followed by tool messages responding to each `tool_call_id`" error - limits execution to the **first choice** — with `n > 1` the previous loop ran each tool call once per choice and orphaned the `tool_call_id`s belonging to the alternative completions - aligns the Python SDK (`OpenAIProvider.handle_tool_calls`) with the same first-choice behavior so both SDKs stay in parity - fixes the stale `@composio/openai` test that modeled "multiple tool calls" as separate `n` choices rather than one message's `tool_calls` array - clarifies in the changeset that the calls run **sequentially**, in the order the model returned them (here "parallel" means the model issued several calls in one turn, not concurrent execution), so each `tool_call_id` is answered exactly once and deterministically - adds regression tests (TS + Python): both calls run for parallel tool calls in one message, and only the first choice runs when `n > 1` Full `@composio/core` (996) and `@composio/openai` suites green; Python `handle_tool_calls` tests, Ruff, and mypy clean; `tsc` clean. --------- Co-authored-by: serhiizghama <zmrser@gmail.com> |
||
|
|
a69c3edea3 |
fix(triggers): align create() user_id resolution across SDKs (#3723)
This PR: - builds on top of https://github.com/ComposioHQ/composio/pull/3714 — addresses its code-review findings - restores TS/Python parity on slug validation: Python `create()` now validates the slug up-front and raises a new `TriggerTypeNotFound`, mirroring the TS `ComposioTriggerTypeNotFoundError` - switches both SDKs to the native `user_id` upsert field — TS drops the `& { user_id }` intersection and Python drops the `extra_body` shim (both pinned clients already expose the field) - treats a blank/whitespace `userId` (TS) or `user_id`/`connected_account_id` (Python) as missing instead of forwarding it to the backend - documents the error-contract change (`create()` no longer throws `ComposioConnectedAccountNotFoundError` — that case now surfaces from the backend) in the changeset and `ts/docs/api/triggers.md`, and bumps the changeset from `patch` to `minor` - adds Python tests for the 2FA (both-provided) path, unknown-slug, and blank `userId`; tightens the TS auto-resolve assertion and adds an empty-`userId` case ## ⚠️ Release gating Since #3714, `triggers.create()` (both SDKs) relies on the backend resolving the trigger connection from `user_id` on upsert ([ComposioHQ/platform#10932](https://github.com/ComposioHQ/platform/pull/10932)). There is **no client-side fallback** — reintroducing one would undo #3714's intent, so this is a release-sequencing requirement, not a code change here. - Do not release `@composio/core` or the Python SDK until #10932 is live in all regions. - Self-hosted / on-prem deployments must be on a backend version that includes #10932. ## Review findings addressed | # | Finding | Resolution | |---|---------|------------| | P1-1 | Coupled to unreleased backend, no fallback | Documented dependency in changeset; flagged release-gating above (no fallback by design) | | P2-2 | Cross-SDK parity on slug validation | Kept `getType` in both; added Python `TriggerTypeNotFound` mirroring TS | | P2-3 | `user_id` bridge redundant in both SDKs | TS uses native field directly; Python uses `user_id=` kwarg | | P2-4 | Error-contract change shipped silently | Documented in changeset + docs `Throws` section; `minor` bump | | P2-5 | Python missing 2FA (both-provided) test | Added `test_create_with_user_id_and_connected_account_id` | | P3 6–13 | Cleanups | undefined-assertion, `none_to_omit`, docstring, `parsedBody.data`, redundant `else`, stale comments, blank-string guard | ## Verification - TS: `vitest` 59 passed, `typecheck` clean, `eslint`/`prettier` clean - Python: `pytest tests/test_triggers.py` 69 passed, `nox -s chk` (ruff + mypy) clean |
||
|
|
20a4711ee0 |
feat(triggers): resolve connection from user_id server-side in create() (#3714)
## What `triggers.create()` used to make an extra **`connectedAccounts.list()`** call to turn a `user_id` into a `connected_account_id` before calling upsert. The backend now resolves the connection from `user_id` directly (picks the first active connection for the user + the trigger's toolkit, parity with tool execution), so the SDK passes `user_id` straight through and drops the round-trip. - **TS** (`ts/packages/core`): removed the `connectedAccounts.list()` block in `Triggers.create()`. `user_id` was already forwarded to upsert (for 2FA); this just removes the now-redundant lookup. `getType` is kept for an early, clear "trigger type not found" error. `connected_account_id` is passed when the caller pins one. - **Python** (`python/composio`): dropped `_get_connected_account_for_user`; `user_id` is sent via `extra_body` and `connected_account_id` is omitted when not provided. ## Why The SDK only ever holds a `user_id`, so every `triggers.create()` paid an extra API call to find a connection. The backend change (ComposioHQ/platform#10932) makes trigger upsert accept `user_id` and auto-resolve — this PR consumes that. ## Behavior notes - When `connected_account_id` is omitted, the backend picks the first active connection ordered by `created_at desc`. The TS SDK's old `list()[0]` ordering was unspecified — selection is now deterministic and consistent with tool execution. - "No connected account found" / "connected account not found" now surface from the **backend** instead of a client-side pre-check. - When 2FA is enabled and `connected_account_id` is pinned, the backend validates that `user_id` owns it (unchanged; `user_id` was already forwarded). - The `extra_body` / `& { user_id }` bridges are still needed only until `@composio/client` / `composio_client` regenerate with `user_id` on the upsert type (the field is already in the OpenAPI contract). ## Tests - TS: `vitest run test/models/triggers.test.ts` → **58 passed** (create tests rewritten to assert no `connectedAccounts.list()` call + `user_id` forwarded). - Python: `pytest tests/test_triggers.py` → **66 passed** (same). ## Depends on Backend: ComposioHQ/platform#10932 (`feat(apollo): resolve trigger connection from user_id on upsert (PLEN-2580)`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
4c3a321550 |
fix(core): disable retries on non-idempotent tools.execute/proxyExecute (#3716)
This PR: - Closes https://github.com/ComposioHQ/composio/issues/3586 - disables client retries on `tools.execute` and `tools.proxyExecute` — the two non-idempotent writes — so a request that succeeds server-side but times out client-side is no longer silently replayed (the cause of duplicate `GMAIL_SEND_EMAIL` sends) - routes both through a sibling client built with `withOptions({ maxRetries: 0 })`; reads keep the client's default retries (`maxRetries: 2`) - caches that sibling per `Tools` instance (`clientWithoutRetries`) since `withOptions` constructs a fresh client and execute/proxy is the hottest path; mirrors the Python SDK's `client.without_retries` - adds regression tests asserting both paths route through `maxRetries: 0` and that the sibling is built once across multiple executes ## Context The original hang on attachments larger than ~25 KB was a backend defect and has already been fixed server-side. The remaining gap was a cross-SDK parity one: the Python SDK already disabled retries on these non-idempotent writes, but `@composio/core` still inherited the client default of 2 retries (3 attempts total), with no public knob to turn them off. This is the TypeScript parity port — scoped to exactly `tools.execute` / `tools.proxy`, leaving `toolRouter.session.execute` and all reads on their default retry behaviour, matching Python. |
||
|
|
03cb181fdb |
chore(deps): roll up dependency bumps (#3711)
This PR: - rolls up https://github.com/ComposioHQ/composio/pull/3695, https://github.com/ComposioHQ/composio/pull/3696, https://github.com/ComposioHQ/composio/pull/3697, and https://github.com/ComposioHQ/composio/pull/3698 into one dependency-bump branch - updates Python dependency metadata for `langchain-openai`, `pyautogen`, and `crewai` - updates npm production dependency metadata for `zod-to-json-schema`, `openai`, and `@modelcontextprotocol/sdk` - refreshes `uv.lock` and `pnpm-lock.yaml` with the repo-pinned package managers - leaves Changesets unchanged because the touched TS package manifests are private/ignored and no published TS SDK package metadata changes - verified with `pnpm install --frozen-lockfile`, `pnpm lint`, `pnpm typecheck`, focused TS builds/tests, clean-worktree `pnpm test:examples`, Python import tests, and Python nox checks - note: local `pnpm build:packages` hit a pre-existing broad-build race around `@composio/slim` rebuilding `@composio/core`; focused dependent builds pass --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
156a1eb7b2 |
Release: update version (#3686)
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/vercel@0.11.0 ### Minor Changes - |
||
|
|
956f9be9b4 |
chore(agents): normalize repo guidance skills (#3666)
This PR: - replaces the duplicated repo-local skill set with the requested canonical `.agents/skills` taxonomy and short router `SKILL.md` files backed by first-level references - makes `.claude/skills` a compatibility symlink to `.agents/skills` and removes hand-maintained Claude skill/rule copies - moves docs agent guidance and decisions into neutral `docs/agent-guidance/` and `docs/decisions/`, with `docs/CLAUDE.md` reduced to a shim - retires CLI Cursor rules after migrating CLI design, Effect source, and client-cache sync guidance into `AGENTS.md` and `cli-command` - adds nested `AGENTS.md` files for TS, core, providers, e2e, Python, Python providers, and docs - adds `pnpm validate:agent-skills` to validate skill frontmatter, taxonomy, references, symlink invariants, stale paths, and command names - implements missing Python `tst` and `snt` nox sessions that existing Makefile targets already exposed - no changeset: repository guidance/tooling only, no published SDK package behavior ## Verification - `pnpm validate:agent-skills` -> `Validated 14 canonical agent skills and guidance invariants.` - `for skill in .agents/skills/*; do python /Users/jkomyno/.codex/skills/.system/skill-creator/scripts/quick_validate.py "$skill" || exit 1; done` -> 14x `Skill is valid!` - `pnpm --dir ts/packages/cli validate:skills` -> `Validated composio-cli skill builds for stable and beta.` - `cd python && uv run nox --list` -> includes `tst` and `snt` - `cd python && uv run nox -s snt` -> 18 passed - `cd python && uv run nox -s tst -- tests/test_imports.py` -> 8 passed - `git diff --check` / `git diff --cached --check` -> clean - stale reference search for retired docs/Claude/Cursor paths -> no matches ## Forward Tests - TypeScript core bug: loaded `bug-fixing`, `typescript-sdk`, `typescript-testing`; found correct root/ts/core `AGENTS.md` route. - Python provider: loaded `python-providers`, `python-testing`; found missing nox sessions, fixed here. - CLI command: loaded `cli-command`; found recording/changeset wording gaps, fixed here. - Cross-SDK drift: loaded `cross-sdk-parity`; again found Python nox drift, fixed here. - Docs + decision: loaded `docs-decisions`; found decision template/index and Twoslash path gaps, fixed here. ## Notes The first normal `git commit` attempt hit a lint-staged/Git stash limitation while replacing `.claude/skills/` with a symlink (`path ... beyond a symbolic link`). The final commit used `--no-verify` after the validators and formatting checks above passed. |
||
|
|
cddd66c3ce |
chore(ts): bump Hono, Mastra, and Chrome deps (#3691)
This PR: - bumps `hono`, `@mastra/core`, and `chrome-devtools-mcp` to their current latest published versions - routes the remaining Cloudflare Tool Router Hono consumer through the workspace catalog - updates the `@composio/mastra` `@mastra/core` peer range and adds a patch changeset - fixes `check-peer-deps` to resolve `semver` through the normal package dependency |
||
|
|
acf8a9ca1f |
test(cli): cover fish completion install idempotency (#3688)
This PR: - follows up on https://github.com/ComposioHQ/composio/pull/3440 and credits Deepak Singh Kandari / @aptsalt from the original implementation - builds on the fish completion fix already merged in https://github.com/ComposioHQ/composio/pull/3162 - references https://github.com/ComposioHQ/composio/issues/3147 - adds regression coverage for running `composio install --completions` twice under fish - verifies `config.fish` keeps a single PATH marker and never receives completion markers - verifies `~/.config/fish/completions/composio.fish` keeps one completion marker ## Testing - `MISE_TRUSTED_CONFIG_PATHS=$PWD mise exec -- pnpm --filter @composio/cli exec vitest run test/src/commands/install.cmd.test.ts` - `MISE_TRUSTED_CONFIG_PATHS=$PWD mise exec -- pnpm --filter @composio/cli typecheck:test` - `git diff --check origin/next...HEAD` Co-authored-by: Deepak Singh Kandari <deepaksinghkandari07@gmail.com> |
||
|
|
9ffc696a99 |
feat(vercel): support AI SDK v7 (#3683)
This PR:
- widens the `ai` peer dependency of `@composio/vercel` to `^6.0.0 ||
^7.0.0`, adding AI SDK v7 support and dropping the previously-untested
v5 arm
- adds Docker-based Node e2e compatibility suites for AI SDK v6 and v7
under `ts/e2e-tests/runtimes/node/vercel-ai-sdk-v{6,7}`, auto-discovered
by the existing e2e matrix (so PR CI exercises them)
- each installs the packed `@composio/vercel` tarball, typechecks the
provider against the target `ai` major, and asserts runtime tool
wrapping/execution (object inputs, JSON-string normalization via
`normalizeToolArguments`, `ToolSet` shape)
- the v7 suite additionally exercises v7's `ToolExecutionOptions` passed
to `tool.execute`
- ships a `minor` changeset (the peer-range narrowing removes a
previously-supported major)
- breaking:
- `ai@5` is no longer an accepted peer of `@composio/vercel`; v5
consumers should stay on the prior minor
|
||
|
|
605a7263ab |
Add tool router session deletion (#3679)
## Summary Explain the motivation and context for this change. Link to any related issues. Fixes # ## Changes - - ## Type of change - [ ] Bug fix - [ ] New feature - [ ] Refactor/Chore - [ ] Documentation - [ ] Breaking change ## How Has This Been Tested? Describe the tests you ran and instructions so reviewers can reproduce. Include any relevant config/versions. ## Screenshots (if applicable) ## Checklist - [ ] I have read the Code of Conduct and this PR adheres to it - [ ] I ran linters/tests locally and they passed - [ ] I updated documentation as needed - [ ] I added tests or explain why not applicable - [ ] I added a changeset if this change affects published packages ## Additional context --------- Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
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 |
||
|
|
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 - |
||
|
|
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>
|
||
|
|
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. |
||
|
|
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 - |
||
|
|
f6b5505739 | fix(core): preserve SDK reference types | ||
|
|
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.** |
||
|
|
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> |
||
|
|
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 |
||
|
|
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` |
||
|
|
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> |
||
|
|
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> |
||
|
|
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 |
||
|
|
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> |
||
|
|
23f9053804 |
chore(ts): clean up dependencies and bump toolchain (#3623)
This PR: - consolidates and bumps TypeScript/npm dependencies across the monorepo, docs, examples, and e2e fixtures — no runtime behavior changes - **cleanup:** remove the unused `ansis` dependency from `@composio/cli` (`picocolors` is the actual color lib), drop the dead `uuid` catalog entry, catalog `dotenv` + `@types/bun` and repoint drifting examples/e2e onto them, and replace `chalk` with `picocolors` in `@composio/core` (smaller, ESM, already used by the CLI) - **TypeScript 6:** bump `typescript` `5.9 → 6.0.3` everywhere (catalog, CLI test fixtures, docs); drop vestigial `declaration`/`outDir` from the provider `tsconfig.json`s to fix the TS 6 `rootDir` regression (`TS6059`); add `ignoreDeprecations: "6.0"` in docs for the `baseUrl` deprecation - **toolchain:** `tsdown 0.18 → 0.22.3`, `vitest` + `@vitest/ui → 4.1.9`, `publint → 0.3.21`, `wrangler → 4.101.0` (each the latest version within the 3-day `minimumReleaseAge` gate) - **Effect + hono:** `effect 3.21.3`, `@effect/cli 0.75.2`, `@effect/platform 0.96.1`, `platform-bun 0.90.0`, `platform-node-shared 0.60.0`, `language-service 0.86.2`, `@effect/vitest 0.29.0`, `hono 4.12.25`; pin the Effect peer cohort (`printer`/`printer-ansi`/`typeclass`/`rpc`/`sql`/`cluster`/`experimental`/`workflow`) as `@composio/cli` devDeps so the auto-installed peers resolve coherently, and bump `@cloudflare/workers-types` to satisfy `wrangler`'s peer - changesets: `@composio/cli` patch (ansis removal) and `@composio/core` patch (chalk → picocolors) - verified: `build`, `typecheck` (tsgo + real `tsc` 6.0.3), and local tests (excluding e2e) all pass |
||
|
|
24e647fa16 |
test(ts/e2e): cover claude agent sdk on node current (#3620)
This PR: - builds on top of https://github.com/ComposioHQ/composio/pull/3495 - adds a Node.js `current` e2e suite for `@composio/claude-agent-sdk` - exercises the packaged provider through Claude Agent SDK `createSdkMcpServer` / `query()` with a deterministic local tool - forwards `ANTHROPIC_API_KEY` through Turbo, Docker e2e env passthrough, and the Node e2e workflow |
||
|
|
f3dd9343e6 |
fix(docs): preserve inline API paths in generated SDK reference (#3619)
## Summary The TypeScript SDK reference generator (`generate-docs.ts`) strips every `api/vN/...` substring from JSDoc descriptions to keep raw endpoint paths out of user-facing docs. The blanket regex also strips paths that live **inside intentional inline-code spans**, mangling them. Concretely, `ConnectedAccounts.initiate()`'s JSDoc: > The legacy `` `POST /api/v3/connected_accounts` `` endpoint that this method … currently renders in the generated reference as: > The legacy `` `POST `` endpoint that this method … — content lost and an unclosed code span. (Visible today in `docs/content/reference/sdk-reference/typescript/connected-accounts.mdx`.) ## Fix In `extractDescription`, split the text on inline-code spans and only strip bare API paths **outside** backticks; deliberate route references inside code spans are left intact. The path regex is also tightened to stop at code-span and punctuation boundaries (`` ` ``, `)`, `,`, `.`, `;`). The single affected reference page is regenerated in the same commit. ## Verification - `pnpm --filter @composio/core generate:docs` → only `connected-accounts.mdx` changes, restoring the full `` `POST /api/v3/connected_accounts` `` span. ## Context Salvaged from the now-superseded #3518 — re-derived against the current generator. The rest of #3518 (drift-gate workflow, regenerated MDX, generator rework) is obsoleted by the post-merge auto-regeneration workflow (`generate-sdk-docs.yml`) and #3613. |
||
|
|
cbbad15126 |
fix(ts): support Zod 4 custom tool schemas (#3495)
## Summary Stacked on #3494. This PR tightens the SDK's Zod boundary now that the TypeScript packages are moving to the newer Node/toolchain baseline. The main issue was asymmetric Zod support: `@composio/json-schema-to-zod` already supports projects with Zod 3 and Zod 4 by generating schemas through `zod/v3`, but custom tools converted schemas in the opposite direction through `zod-to-json-schema` only. With a bare Zod 4 `z.object(...)`, that path degraded to an empty JSON Schema. ## What changed - Added a shared core helper for Zod schema -> JSON Schema conversion. - Supports both `zod/v3` schemas and Zod 4 schemas via `z.toJSONSchema`. - Converts custom-tool input schemas in Zod 4 input mode, so defaulted fields stay optional in the generated JSON Schema. - Uses the shared helper in both custom-tool entrypoints: `createCustomTool(...)` and legacy `composio.tools.createCustomTool(...)`. - Updates custom-tool types so Zod 4 schemas infer their `execute` input type while keeping the session context parameter required for context-using tools. - Exposes `jsonSchemaToZodShape` through the narrow `@composio/core/utils/json-schema` subpath. - Switches the Claude Agent SDK provider to use that core subpath instead of depending directly on `@composio/json-schema-to-zod` or converting to a full Zod object and casting `.shape` out of it. - Adds patch changesets for `@composio/core` and `@composio/claude-agent-sdk`. ## Review hardening Follow-up fixes from a deeper pass over the new conversion boundary: - **Fixes a Zod 3 regression in the v3/v4 discriminator.** The detector keyed on `_def.type` being defined, but Zod 3 stores the element schema there for `z.array()` / `z.promise()` — so a Zod 3 array/promise `outputParams` was misrouted into `z.toJSONSchema` and crashed with `Cannot read properties of undefined (reading 'def')`. It now discriminates on `_def.type` being a string literal (Zod 4) versus a `ZodType` object (Zod 3), with a regression test. - **Mirrors input/output mode on the Zod 3 path.** The `io` mode was applied to the Zod 4 converter but dropped on the Zod 3 branch; it now maps onto `zod-to-json-schema`'s `pipeStrategy`/`effectStrategy` so `.default()` / `.transform()` / `.pipe()` schemas serialize consistently across both runtimes. Added a v3/v4 input-mode parity test. - **Fails loudly on a missing named definition** instead of returning the wrapper document, which downstream silently degraded into an empty object schema. - **Replaces broad `as never` casts** in the Claude Agent SDK provider with a single concrete `tool()` signature — this sidesteps the SDK's deep-instantiation error while keeping the handler's real argument type. - **Trims now-dead code** from the custom-tool schema types and conversion path. ## Verification - `mise exec -- pnpm --filter @composio/core test` (933 passing) - `mise exec -- pnpm --filter @composio/claude-agent-sdk test -- --runInBand` (17 passing) - `mise exec -- pnpm --filter @composio/core build` (publint + attw clean, emits `@composio/core/utils/json-schema`) - `mise exec -- pnpm --filter @composio/claude-agent-sdk exec tsc --noEmit --skipLibCheck` - `mise exec -- pnpm --filter @composio/claude-agent-sdk build` (publint + attw clean) - `mise exec -- pnpm install --frozen-lockfile` - `git diff --check` Note: `mise exec -- pnpm --filter @composio/core typecheck` still fails on the existing `src/utils/pusher.ts` `ChannelAuthorizationHandler` type mismatch. This PR does not touch that file; the core build and full core unit suite pass. |
||
|
|
025a657597 |
feat(ts): drop CommonJS support (#3494)
This PR moves the TypeScript SDK support floor to the latest Node.js 22 release and uses that as the point where we stop carrying custom CommonJS compatibility machinery. The e2e runtime coverage now exercises the latest Node 22, Node 24, and Node 25 lines, so the SDK is tested against the minimum supported version and the newer runtimes users are likely to adopt next. Because the SDK packages are still on `0.x`, the accompanying changeset uses `minor` bumps while calling out that this is a breaking change inside the existing 0.x line. It also moves pnpm under mise on this Node 22 layer. That belongs here, not in #3493, because the pinned `pnpm@10.28.2` requires Node.js `>=22.13`. Removing Corepack is intentional: Node.js documents that Corepack is no longer distributed starting with Node.js v25, so relying on `corepack enable` would keep a toolchain dependency that is already on the way out of the Node distribution. ## What changed - Pins the local/toolchain Node.js floor to `22.22.3` in `mise.toml`, `mise.lock`, `toolchain-versions.json`, and root `devEngines`. - Pins pnpm in `mise.toml` as `"npm:pnpm" = "10.28.2"` and makes mise the single source of truth for the pnpm version. Removes the root `packageManager` / `devEngines.packageManager` Corepack pin entirely (rather than keeping a second copy of the version that could drift) and removes stale `packageManager` metadata from real TS packages/examples and generators. - Sets `dangerouslyDisablePackageManagerCheck: true` in `turbo.jsonc` so Turbo discovers pnpm from `pnpm-lock.yaml` instead of requiring a `packageManager` field. Without this, Turbo fails workspace resolution (`Could not resolve workspaces -> Missing 'packageManager' field`), which is the only reason the field would otherwise need to stay. - Replaces `corepack enable` in the shared setup action with mise-managed pnpm. The E2E Docker images install bun + pnpm directly from `mise.toml`/`mise.lock` (with `dangerouslyDisablePackageManagerCheck`-style separation: Node/Deno stay on the base image as the test matrix axis and are disabled via `MISE_DISABLE_TOOLS` so mise does not shadow them; the tool binaries are symlinked into `/usr/local/bin`). No `BUN_VERSION`/`PNPM_VERSION` build args are threaded from the host anymore, and bun+pnpm now carry `mise.lock` checksum verification. - Extends the install-time toolchain check to validate pnpm against mise alongside Bun. - Sets the Node E2E matrix and install fallback checks to `22.22.3`, `24.16.0`, and `25.9.0`. - Makes TS package builds ESM-only: `tsdown` now emits `.mjs` / `.d.mts` only and uses the ATTW `esm-only` profile. - Removes explicit `require` / `.cjs` / `.d.cts` package export paths and internal import mappings from the public TS SDK packages. - Marks public TS SDK packages as `type: module` where they were missing it. - Replaces the remaining runtime `require('pusher-js')` with dynamic ESM import. - Deletes the legacy CJS example. - Reintroduces `node/cjs-basic` as a modern Node `require(esm)` interop E2E: it verifies `require('@composio/core')` works through Node's native ESM loader on Node 22, 24, and 25 while resolving to `dist/index.mjs`, not a `.cjs` artifact. - Removes `.cjs` / `.cts` handling from example validation and CLI project-language detection. - Updates provider scaffolding so newly generated providers are ESM-only. - Adds a minor changeset warning that CommonJS callers can only rely on Node's native `require(esm)` interop and that custom CommonJS compatibility machinery is gone. ## Bundled behavior changes Two changes here are technically independent of the CommonJS removal but ride along because they touch the same files and ship in the same release cut. Calling them out explicitly so they are not missed in review: - **`PusherUtils` realtime channel auth (`@composio/core`).** Replacing the runtime `require('pusher-js')` with a dynamic ESM `import()` was done alongside conforming `channelAuthorization` to pusher-js's typed `customHandler(params, callback)` contract. The previous `(authOptions) => Promise` shape did not match pusher-js's actual calling convention — it read `endpoint`/`headers`/`params` off an argument that pusher-js never passes — so this also fixes that latent mismatch. New unit tests (`ts/packages/core/test/utils/pusher.test.ts`) cover the auth request shape (endpoint, `x-api-key` header, JSON `socket_id`/`channel_name` body) and the success / invalid-JSON / network-failure callback paths. - **CLI meta-tool slug list (`@composio/cli`).** Drops `COMPOSIO_UPSERT_RECIPE` and `COMPOSIO_GET_RECIPE` from `META_TOOL_SLUG_LIST` in `tools-executor.ts`; `@composio/client` alpha.74 removed those slugs from the `SessionExecuteMetaParams['slug']` union. The list is declared `satisfies ReadonlyArray<SessionExecuteMetaParams['slug']>`, so this is enforced at compile time (`pnpm typecheck`) — keeping the stale slugs would be a type error — and needs no separate runtime test. ## Verification - Verified latest Node 22/24/25 releases from the official Node dist index: `22.22.3`, `24.16.0`, `25.9.0` (https://nodejs.org/dist/index.json). - Verified Node docs state Corepack is no longer distributed starting with Node.js v25: https://nodejs.org/download/release/v22.22.3/docs/api/corepack.html - `mise exec -- pnpm --version` -> `10.28.2` - `mise exec -- pnpm install --frozen-lockfile` - `mise exec -- bun run ts/scripts/pre-install/check-toolchain.ts` - `mise exec -- pnpm --filter @e2e-tests/utils typecheck` - `mise exec -- pnpm --filter @e2e-tests/node-cjs-basic typecheck` - `mise exec -- pnpm --filter @e2e-tests/node-cjs-basic test:e2e:node` - `mise exec -- pnpm --filter @e2e-tests/node-esm-basic typecheck` - `mise exec -- pnpm --filter @composio/cli typecheck` - `mise exec -- pnpm --filter @composio/cli test -- --runInBand` - `mise exec -- pnpm --filter @composio/core typecheck` - `mise exec -- pnpm --filter @composio/core exec vitest run test/utils/pusher.test.ts` - `mise exec -- pnpm run build:packages` - `mise exec -- turbo run build --dry-run` resolves all workspace packages with no root `packageManager` field (pnpm discovered from `pnpm-lock.yaml`). - `mise exec -- pnpm --filter @composio/cli exec vitest run test/src/services/project-environment-detector.test.ts` - `mise exec -- pnpm exec prettier --check ...` - `docker build -f ts/e2e-tests/_utils/Dockerfile.node --build-arg NODE_VERSION=24.16.0 -t composio-e2e-node:misecheck .` (also `Dockerfile.deno` with `DENO_VERSION=2.6.7 NODE_MAJOR=22`) — both build green; runtime resolves Node/Deno from the base image and pnpm 10.28.2 / bun 1.3.10 from mise. - `docker manifest inspect node:24.16.0-slim` - `docker manifest inspect node:25.9.0-slim` - `bash -n ts/scripts/create-provider.sh && git diff --check` - `ruby -e "require 'yaml'; YAML.load_file('.github/workflows/ts.test-e2e.yml')"` - Tracked example validation in a clean temporary tree: `Validated 21 example packages.` |
||
|
|
5f004ffbfe |
fix(cli): drop recipe meta-tool slugs removed from @composio/client (#3615)
This PR: - cherry-picks the CLI fix from https://github.com/ComposioHQ/composio/pull/3494 so `next` is unblocked independently of that larger ESM-only PR - fixes the `@composio/cli#build` typecheck failure on `next` (`TS2322` in `ts/packages/cli/src/services/tools-executor.ts`) that is currently breaking the **TS SDK Release** workflow - `@composio/client` alpha.74 (https://github.com/ComposioHQ/composio/pull/3609) removed `COMPOSIO_UPSERT_RECIPE` and `COMPOSIO_GET_RECIPE` from `SessionExecuteMetaParams['slug']`, but `META_TOOL_SLUG_LIST` (declared `satisfies ReadonlyArray<SessionExecuteMetaParams['slug']>`) still listed them, so the build no longer type-checked - adds a `@composio/cli` patch changeset Verified locally: `pnpm --filter @composio/cli typecheck` passes. |
||
|
|
b346272572 |
chore(ci): use OIDC for TS npm releases, add needed attributes to package.json (#3611)
This PR: - configures the TS release workflow to request GitHub Actions OIDC ID tokens - runs the TS release publish path on Node 24.17.0 so npm supports trusted publishing - leaves a TODO to centralize that release runtime in `mise.toml` after https://github.com/ComposioHQ/composio/pull/3494 lands - removes the long-lived `NPM_TOKEN` `.npmrc` setup before `changeset publish` - adds npm package support metadata (`repository.url`, `bugs.url`, and `homepage`) for every public TS release workspace that Changesets can publish - marks `@composio/cli-local-tools` private so Changesets does not try to publish it |
||
|
|
f6df489c0b |
Release: update version (#3429)
> [!WARNING] > ## ⚠️ Breaking change — `@composio/core@0.11.0` (minor bump, pre-1.0) > > The legacy in-memory custom-tools registry **`composio.tools.createCustomTool(...)` has been removed** (changeset `44e5458`). Because `@composio/core` is pre-1.0, this ships as a **minor** bump per semver convention — but it **is breaking**: any code calling `composio.tools.createCustomTool(...)` will fail to compile/run after upgrading. Consumers pinned to `^0.10.0` will **not** auto-upgrade to `0.11.0` and must opt in explicitly. ### Migration: use Tool Router custom tools Custom tools are now standalone factories bound to a **Tool Router session**, instead of being registered into an in-memory registry on the `composio.tools` namespace. **Before (removed):** ```ts // ❌ No longer available in 0.11.0 const tool = composio.tools.createCustomTool({ /* ... */ }); ``` **After — a standalone tool (no auth):** ```ts import { Composio, experimental_createTool } from '@composio/core'; import { z } from 'zod'; const composio = new Composio(); const grep = experimental_createTool('GREP', { name: 'Grep Search', description: 'Search for patterns in files', inputParams: z.object({ pattern: z.string(), path: z.string() }), execute: async (input) => ({ matches: [] }), }); // Bind the custom tool(s) to a Tool Router session const session = await composio.create(userId, { experimental: { customTools: [grep] }, }); ``` **After — a tool that extends a Composio toolkit (inherits that toolkit's auth):** ```ts const getImportantEmails = experimental_createTool('GET_IMPORTANT_EMAILS', { name: 'Get Important Emails', description: 'Fetch high-priority emails', extendsToolkit: 'gmail', inputParams: z.object({ limit: z.number().default(10) }), execute: async (input, ctx) => { // ctx.execute() returns the standard { data, error, logId } shape const result = await ctx.execute('GMAIL_SEARCH', { query: 'is:important' }); return { emails: result.data }; }, }); const session = await composio.create(userId, { experimental: { customTools: [getImportantEmails] }, }); ``` **After — grouping related tools into a custom toolkit:** ```ts import { experimental_createToolkit } from '@composio/core'; const devTools = experimental_createToolkit('DEV_TOOLS', { name: 'Dev Tools', description: 'Local developer utilities', tools: [grep], // tools here must NOT set `extendsToolkit` }); const session = await composio.create(userId, { experimental: { customToolkits: [devTools] }, }); ``` > Notes: > - `experimental_createTool` / `experimental_createToolkit` are also available from the dedicated `@composio/core/experimental` entry point. > - Tool slugs are auto-prefixed with `LOCAL_` when exposed to the agent (e.g. `GREP` → `LOCAL_GREP`; inside a toolkit, `DEV_TOOLS` + `GREP` → `LOCAL_DEV_TOOLS_GREP`). > - These APIs are intentionally namespaced `experimental_*` — they are not yet stable and may change. --- 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.11.0 ### Minor Changes - |
||
|
|
d7d5d46f4d |
fix(ts/e2e): stabilize Tool Router e2e tests (#3605)
This PR: - addresses the Mastra Tool Router E2E timeout seen in https://github.com/ComposioHQ/composio/actions/runs/27758637694/job/82126898492 - addresses the Cloudflare Tool Router AI failure seen in https://github.com/ComposioHQ/composio/actions/runs/27759741357/job/82130692480 - addresses the follow-up Cloudflare basic timeout seen in https://github.com/ComposioHQ/composio/actions/runs/27760548665/job/82133483582 - uses the existing `TIMEOUTS.LLM_LONG` live-LLM budget for both Mastra Tool Router zod-v3 and zod-v4 suites - raises the HTTP MCP connection-phase timeout from Mastra's implicit 3s default to 15s in both zod-v3 and zod-v4 suites - keeps the Cloudflare AI test's `Output.object({ karma })` structured output and validates the returned `response.karma` - asserts that `HACKERNEWS_GET_USER` was called and that `response.karma` equals the structured MCP tool output - removes the extra Cloudflare `prepareStep` steering and `findNumberProperty` extraction helper - avoids explicit `@ai-sdk/mcp` HTTP client close calls in workerd because the close path aborts the pending stream and Vitest reports it as unhandled - gives the live Cloudflare HackerNews compatibility test a 30s budget while leaving the fast smoke tests on Vitest's default timeout - fixes the zod-v3 test label so it reports `zod 3` instead of `zod 4` Verification: - `./ts/e2e-tests/_utils/node_modules/.bin/tsc --noEmit -p ts/e2e-tests/runtimes/node/mastra-tool-router-zod-v3/tsconfig.json` - `./ts/e2e-tests/_utils/node_modules/.bin/tsc --noEmit -p ts/e2e-tests/runtimes/node/mastra-tool-router-zod-v4/tsconfig.json` - `pnpm exec vitest run -t "should connect to MCP server"` from `ts/e2e-tests/runtimes/cloudflare/cf-workers-tool-router-ai` - `pnpm exec tsc --noEmit --target es2022 --lib es2022 --module es2022 --moduleResolution Bundler --resolveJsonModule --allowSyntheticDefaultImports --strict --skipLibCheck --types @cloudflare/workers-types src/index.ts` from `ts/e2e-tests/runtimes/cloudflare/cf-workers-tool-router-ai` - `pnpm exec vitest run -t "should fetch HackerNews user pg successfully"` from `ts/e2e-tests/runtimes/cloudflare/cf-workers-basic` - `pnpm exec vitest run` from `ts/e2e-tests/runtimes/cloudflare/cf-workers-basic` - `pnpm exec prettier --check ts/e2e-tests/runtimes/cloudflare/cf-workers-basic/test/platform.spec.ts` - `pnpm exec prettier --check ts/e2e-tests/runtimes/cloudflare/cf-workers-tool-router-ai/src/index.ts ts/e2e-tests/runtimes/cloudflare/cf-workers-tool-router-ai/test/tool-router-ai.spec.ts ts/e2e-tests/runtimes/node/mastra-tool-router-zod-v3/e2e.test.ts ts/e2e-tests/runtimes/node/mastra-tool-router-zod-v4/e2e.test.ts` - `git diff --check` CI: - `E2E Test Typescript SDK` passed on https://github.com/ComposioHQ/composio/actions/runs/27768379357 Note: the full Cloudflare AI test was not run locally because this checkout's local `OPENAI_API_KEY` is invalid; CI has the repo secret for that path. |
||
|
|
0720b760e2 | fix(docs): stabilize SDK reference auto PR checks | ||
|
|
08f7072110 |
fix(ci): make CLI releases reliably ship binary assets (#3515)
## Summary Permanently stops the recurring *"release published with zero assets → install/upgrade 404s"* outage (https://github.com/ComposioHQ/composio/issues/3408, https://github.com/ComposioHQ/composio/issues/3269; regressions of https://github.com/ComposioHQ/composio/issues/2625 and https://github.com/ComposioHQ/composio/issues/1820 — 3 of the last 5 stable releases shipped empty). **Root cause:** a concurrent-writer race. On a CLI version bump, `changesets/action` creates an empty `@composio/cli@x.y.z` GitHub Release in seconds, then the slower `build-cli-binaries.yml` fails its `gh release create` because the release already exists — so binaries never attach. > 🥞 **Stacked on #3415** (`pi/exclude-cli-ts-release-42161920`), which removes the *second writer* (changesets no longer creates CLI releases). Review/merge #3415 first; this PR targets that branch. This PR hardens the now-sole writer so a *partial* build can't publish either, and fixes the monitoring that hid the failure. ## What changed (`build-cli-binaries.yml` + canary) - **Draft → verify → publish.** The release is built as a **draft**, then a gate asserts all 6 canonical assets are present **and** `state == "uploaded"` (loud `::error::` + fail otherwise), and only the final step flips it to published. Drafts fire no `release: published` event and are excluded from `/releases/latest`, so no install.sh / Homebrew / redirect consumer can observe an incomplete release. - **`fail-fast: false`** on the build matrix → a single platform failure can never publish a partial set (`needs.build.result` is `success` only if all legs pass). - **Per-tag job-level `concurrency`** so re-runs / quick pushes can't interleave uploads on the same tag, without serializing unrelated betas. - **Beta-safe:** `--prerelease` is set on the draft so betas stay prereleases through the publish flip. - **`promote-stable` guard** uses `gh release view --json isDraft` (the REST tags endpoint 404s on drafts) to resume an existing draft but refuse an already-published tag. - **Least privilege:** workflow defaults to `contents: read`; only the release job opts up to `contents: write`. - **Canary fix** (`cli.install-health-check.yml`): install the newest stable **pinned** tag (`npm view @composio/cli version`) instead of the asset-aware no-arg flow, which self-heals to the last good release and kept the canary green through every outage. ## Idempotency note Re-running a run that failed **before publish** is safe (reuse draft + `--clobber`). An already-**published** tag is refused, not mutated — by design. ## Testing - Extended `test/release-workflow.test.ts` (run via `pnpm test:release-workflow`) to lock in: draft→verify→publish ordering, `fail-fast: false`, per-tag concurrency, preserved `--prerelease`, and the pinned-tag canary. ✅ passes. - `shellcheck` clean on the new `run:` scripts; YAML validated. `actionlint` runs in CI. - Runtime verification of the full green path happens on the next release CI run. ## Out of scope (deferred to their own PRs) Per reviewer consensus, these are real but *not this bug*: a per-release npm↔GitHub asset auditor (needs a grace window), the Homebrew trigger constraint (folds into #3355), `composio upgrade` missing-asset back-off (https://github.com/ComposioHQ/composio/issues/3269), and a rollback runbook. --------- Co-authored-by: Rahul Tarak <cryogenicplanet@gmail.com> |
||
|
|
632757ec60 |
chore(ts/packages/core): add repository, bugs, and homepage metadata (#3573)
## Summary Adds missing npm support metadata to `ts/packages/core/package.json` for the `@composio/core` package: - **`repository`**: adds full object with `type`, `url`, and `directory` (monorepo package path) - **`bugs.url`**: points to `https://github.com/ComposioHQ/composio/issues` - **`homepage`**: points to the package README in the repository ## Verification ```sh npm view @composio/core@0.10.0 repository bugs homepage # Currently returns empty for all three fields ``` No runtime code, dependencies, tests, or build configuration are changed. --------- Co-authored-by: XananasX7 <xananasandy@gmail.com> Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com> Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
a94715f577 |
feat(sdk): forward user_id on triggers.create for trigger 2FA (#3576)
## What `triggers.create(userId/user_id, ...)` already accepts a user id and uses it to resolve the connected account, but **dropped it** when building the `trigger_instances.upsert` body. With [2FA for triggers](https://linear.app/composio/issue/PLEN-2580) on the backend, 2FA-enabled projects need `user_id` on the upsert to verify the pinned connected account belongs to the caller's user. This forwards `user_id` to upsert in **both SDKs**: - **TS** (`ts/packages/core/src/models/Triggers.ts`): adds `user_id` to the upsert params via a `& { user_id?: string }` bridge. - **Python** (`python/composio/core/models/triggers.py`): forwards `user_id` via the client's supported `extra_body` escape hatch. Both bridges exist only until `@composio/client` / `composio-client` regenerate from the updated OpenAPI spec with a native `user_id` field — drop them then. ## Safety Backends without trigger 2FA ignore the extra field, so this is safe to land ahead of / alongside the backend rollout (hermes PR #10635). ## Tests - TS: upsert-body assertion added. - Python: `tests/test_triggers.py::test_create_with_user_id` asserts `extra_body == {"user_id": ...}`. 49 tests pass. Closes part of PLEN-2580 (SDK follow-up to the agreed approach). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
ce4b213361 |
fix(providers): normalize string tool-call arguments across all providers (TS + Python) (#3514)
## Summary Models — and some MCP transports — occasionally emit tool-call arguments as a **JSON string instead of an object/dict**. The most visible trigger is `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI SDK, where streaming fails with: > `messages.3.content.1.tool_use.input: Input should be a valid dictionary` Until now only a handful of providers guarded against this, each with its own slightly different inline check, leaving most providers vulnerable and behaviour inconsistent across the SDK. This PR centralizes the coercion into **one helper per language** and routes **every** provider through it, in both the TypeScript and Python SDKs. Closes https://github.com/ComposioHQ/composio/issues/2406 ## What changed **TypeScript** — new `normalizeToolArguments` in `@composio/core` (exported), used by every provider: `vercel`, `cloudflare`, `openai-agents`, `openai` (ChatCompletions + Responses), `anthropic`, `google`, `langchain`, `llamaindex`, `claude-agent-sdk`, `mastra`. **Python** — new `normalize_tool_arguments` in `composio.utils.shared`, used by every provider: `openai`, `openai-responses`, `anthropic`, `google`, `langchain`, `langgraph`, `crewai`, `autogen`, `llamaindex`, `gemini`, `google-adk`, `openai-agents`, `claude-agent-sdk`. Shared semantics (identical in both languages): | Input | Result | | --- | --- | | object / dict | returned unchanged | | JSON string | parsed to object | | empty / whitespace string | `{}` | | `null` / `undefined` / `None` | `{}` | | array, primitive, unparseable string, JSON that isn't an object | **typed error** (`ComposioInvalidToolArgumentsError` / `InvalidParams`) with the original parse error as cause | The typed error replaces the previous grab-bag of behaviours: a raw `SyntaxError`/`JSONDecodeError`, or — worse — silently forwarding a malformed string downstream. ## Why this supersedes the open PRs This consolidates and extends three open PRs that each addressed a slice of the problem inconsistently. Their authors are credited as co-authors on the relevant commits: - **#3489** (LlamaIndex + Claude Agent SDK, TS) — @srijanarya - **#3438** (Anthropic, Google, LangChain, TS) — @aptsalt - **#3437** (Google ADK empty schemas + name fix, Python) — @pragnyanramtha — its empty-`input_parameters` / missing-description handling and the `gemini` → `google_adk` provider-name fix are folded in here. Compared to the three combined, this PR additionally: covers **every** provider in **both** SDKs (not a subset of one), defines a single source of truth instead of per-provider snippets, normalizes empty/`null` payloads to `{}`, and raises an actionable typed error instead of leaking `SyntaxError` or forwarding a bad string. ## Tests - Exhaustive unit tests for both helpers (object passthrough, JSON-string parse, empty/null → `{}`, malformed/non-object → typed error). - Per-provider regression tests across the touched TypeScript providers (string path, malformed-string path, empty-payload path). - Full `@composio/core` + touched-provider TS suites pass; `typecheck` and `lint` clean. Python `ruff` clean and new test green. ## Changeset Patch bump for all affected TypeScript packages (`@composio/core` + the providers). Python follows its own versioning, so no changeset there. --------- Co-authored-by: srijanarya <74669415+srijanarya@users.noreply.github.com> Co-authored-by: Deepak Singh Kandari <deepaksinghkandari07@gmail.com> Co-authored-by: Pragnyan Ramtha <pragnyanramtha@gmail.com> |
||
|
|
6606c0da92 |
fix(ts): upgrade vitest for GHSA-5xrq-8626-4rwp (#3569)
This PR: - closes #3502 - extracts the useful dependency and Cloudflare config slice from https://github.com/ComposioHQ/composio/pull/3503 onto a clean `next` branch - upgrades `vitest` and `@vitest/ui` through the workspace catalog to resolve the critical advisory - switches the Mastra provider test dependency back to the catalog - migrates the Cloudflare Worker Vitest configs to the `cloudflareTest()` plugin API - preserves existing `COMPOSIO_BASE_URL` bindings that were dropped in #3503 - keeps Vitest 4 compatibility fixes scoped to constructor mocks, spy cleanup, and temp-root project detection - leaves Node, `mise`, release docs, and Effect catalog versions unchanged; the commit includes @Dotify71 as `Co-authored-by` Co-authored-by: Dushyant Acharya <dushyantacharya873@gmail.com> |
||
|
|
74ce25812e |
fix(ci): repair CLI binary and docs sync workflows (#3568)
This PR: - patches the Peekaboo sidecar build to rewrite the resolved `swift-system` `AT_RESOLVE_BENEATH` Darwin guard before compiling on GitHub macOS runners - adds regression coverage for the `swift-system` checkout patch and includes script tests in `@composio/cli-local-tools` Vitest - switches the SDK docs sync workflow to `anthropics/claude-code-action/base-action` so push-triggered automation does not fail on unsupported event parsing - moves Claude tool restrictions to `claude_args` and quotes `$GITHUB_OUTPUT` writes so the workflow passes `actionlint` - verifies the changed package and workflow with targeted tests, build, formatting, YAML parsing, and `actionlint` |
||
|
|
4fe776898a |
chore(toolchain): finish mise migration (#3493)
## Summary Depends on #3492. This completes the Phase 2 migration by removing the transitional version-file layer and making `mise.toml` plus `mise.lock` the repository toolchain source of truth. It also moves runtime test matrices into `toolchain-versions.json`, so CI matrix changes are explicit and reviewable without reintroducing `.nvmrc`, `.dvmrc`, `.bun-version`, or `.python-version`. The Node.js e2e matrix now starts at the latest Node 22 LTS line and also covers the latest Node 24 and Node 25 lines. That removes Node 20 from the well-known e2e versions while keeping us covered on the runtimes SDK users are moving toward. ## Rationale Phase 1 introduced mise side by side with the existing version files to keep the first PR low-risk. Phase 2 removes that compatibility layer so there is one place to update tool versions. That avoids silent drift between local setup, GitHub Actions, Docker E2E images, release docs, and install-time checks. The composite setup actions now install Node, Bun, Python, and uv through mise by default, with explicit version overrides only where a matrix needs them. New GitHub actions added in this PR are pinned by release commit SHA and include the release version comment. ## What changed - Deleted the transitional root/version files: `.nvmrc`, `.bun-version`, `.dvmrc`, root `.python-version`, and `python/.python-version`. - Removed `idiomatic_version_file_enable_tools` from `mise.toml` and added a committed `mise.lock` for linux/macOS x64/arm64 tool resolution. - Replaced `BYPASS_BUN_VERSION_CHECK` with `BYPASS_TOOLCHAIN_CHECK`, and made Docker E2E image installs use that bypass because they receive explicit build args instead of installing mise. - Updated Node/Bun and Python/uv composite actions to default to mise, remove `*-version-file` inputs, report resolved versions, and cache pnpm after `corepack enable`. - Centralized CI runtime matrices in `toolchain-versions.json` for TS E2E, Python tests, and CLI npm fallback coverage. - Updated the Node E2E matrix to `22.22.3`, `24.16.0`, and `25.9.0`, removing Node 20 from the well-known runtime versions. - Updated workflows, docs, E2E helpers, Dockerfiles, and release guidance to reference `mise.toml` / `mise.lock`. ## Verification - Verified latest Node 22/24/25 releases from the official Node dist index: `22.22.3`, `24.16.0`, `25.9.0` (https://nodejs.org/dist/index.json). - `pnpm install --frozen-lockfile` - `pnpm --filter @e2e-tests/utils typecheck` - `pnpm --filter @e2e-tests/utils exec tsc --noEmit --target es2022 --module esnext --moduleResolution bundler --types bun --resolveJsonModule --skipLibCheck --strict scripts/docker-build.ts` - `bash -n ts/scripts/pre-install.sh && bun run ts/scripts/pre-install/check-toolchain.ts && BYPASS_TOOLCHAIN_CHECK=1 bash ts/scripts/pre-install.sh` - `pnpm exec prettier --check ...` on touched YAML/Markdown/TS/JSON files - `ruby -e "require \"yaml\"; ARGV.each { |f| YAML.load_file(f) }" ...` on touched actions/workflows - `mise lock --platform linux-x64,linux-arm64,macos-arm64,macos-x64 && git diff --exit-code mise.lock` - `mise exec node@22.22.3 -- pnpm --filter @e2e-tests/utils typecheck` - `mise exec node@22.22.3 -- pnpm --filter @e2e-tests/node-esm-basic typecheck` - `mise exec node@22.22.3 -- pnpm --filter @e2e-tests/node-cjs-basic typecheck` - `docker manifest inspect node:24.16.0-slim` - `docker manifest inspect node:25.9.0-slim` - `git diff --check` |
||
|
|
56534e2158 |
chore(cli): release 0.2.31 (#3538)
## What Manual version bump for **`@composio/cli` → 0.2.31**, replacing the broken changeset-bot flow. - Bumps `ts/packages/cli/package.json` `0.2.30 → 0.2.31` - Adds the `0.2.31` entry to `ts/packages/cli/CHANGELOG.md` - Consumes (deletes) the 5 CLI changesets it covers: - `composio upgrade <version>` (#3428) - `x-cli-session-id` header (#3430) - refreshed approval fallback page (#3431) - LLM-friendly output (#3450) - fish completion paths (#3162) + 1-hour permission expiry (#3449) - Leaves the `@composio/core` changesets untouched for the bot. ## Why manual The changeset release bot (`ts.release.yml` → `changesets/action`) has been failing on **`HttpError: Bad credentials`** on every run since 2026-06-04 — its `CI_BOT_TOKEN` secret is expired/invalid — so it can no longer regenerate the "Release: update version" PR (#3429, stale since 2026-05-20). `build-cli-binaries.yml` is healthy and independent of that token: on merge to `next` it compares `ts/packages/cli/package.json` against the previous commit, detects the version change, and builds + publishes the stable **`@composio/cli@0.2.31`** GitHub release (cross-platform binaries). The CLI is `"private": true`, so it ships only as the GitHub binary release, not npm — this path fully covers it. ## Validated The pinned-actions binary pipeline was already validated green end-to-end (all 4 platform binaries built + full install-test matrix passed) on a `build-beta` run. Local binary (built from this same source) passed version/whoami/help, LLM-friendly stdout, `upgrade <version>`, and the full Slack + `experimental_subAgent` integration test. ## Follow-up (separate) `CI_BOT_TOKEN` needs refreshing to restore the changeset bot (recommend a GitHub App installation token over a personal PAT to avoid future expiry). Tracked outside this PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
44e5458c35 |
feat(core): remove legacy TypeScript custom tools (#3509)
This PR is **part 2 of 3** splitting https://github.com/ComposioHQ/composio/pull/3505 to make the removal of the old 2025 custom tools easier to review. It carries the **TypeScript** slice. - builds on top of https://github.com/ComposioHQ/composio/pull/3508 (stacked — review/merge the Python slice first; this PR's base is `remove-py-custom-tools`) - removes the legacy `composio.tools.createCustomTool(...)` in-memory registry: the plural `CustomTools` model, registry wiring, legacy-only tests/examples/`ts/docs` pages, and stale error/type exports - preserves the 2026 tool-router APIs: `experimental_createTool`, `experimental_createToolkit`, `ToolRouter`, `ToolRouterSession`, and inline custom-tool execution - regenerates the TypeScript SDK reference so raw tool listing no longer describes the removed local registry and points to session-scoped custom tools - adds a `@composio/core` minor changeset ## Notes - The committed SDK reference is **verified self-consistent**: `pnpm --filter @composio/core generate:docs` reproduces it with zero diff. - This slice is a byte-identical subset of #3505 — the three split branches recombine to that PR's exact tree. See #3505 for the original verification logs (`typecheck`, `test`, `lint`, `cf-workers` e2e); CI re-runs per PR. |
||
|
|
b69cef123f |
fix(mastra): tolerate dangling $ref in tool schemas [PLEN-2451] (#3400)
## Summary Closes [PLEN-2451](https://linear.app/composio/issue/PLEN-2451/mastraprovider-dollardefsdollarref-schema-is-dangling-at-the-api). Fixes https://github.com/ComposioHQ/composio/issues/3307. The Composio API ships some tools (e.g. `GMAIL_FETCH_EMAILS`) with `outputParameters` like: ```jsonc { "type": "object", "properties": { "data": { "$ref": "#/$defs/FetchEmailsResponse" }, // ← points into $defs "error": { "type": "string" }, "successful": { "type": "boolean" } }, "required": ["data", "successful"] // ← no `$defs` block anywhere } ``` Since [PLEN-2244](https://linear.app/composio/issue/PLEN-2244) introduced strict `dereferenceJsonSchema`, this caused `composio.tools.get(...)` to throw `JsonSchemaRefResolutionError: Cannot resolve $ref #/$defs/FetchEmailsResponse` upfront via `MastraProvider.wrapTool`, making every Gmail / Slack / Google Calendar tool unusable through the Mastra integration. ### Approach Two-tier fix that keeps the architectural boundary from PLEN-2244 (only the provider that owns the validation contract pre-processes the schema): 1. **`@composio/core`** — add an additive opt-in to `dereferenceJsonSchema`: ```ts dereferenceJsonSchema(schema, { onUnresolved: 'sentinel', // default 'throw' unchanged onReplace: (ref, reason) => { /* observe */ }, }) ``` In `'sentinel'` mode the unresolvable branch is replaced with the same `{ type: 'object', additionalProperties: true }` cycle-break sentinel the resolver already uses for `$ref` cycles. Safety caps (`MAX_REF_CHAIN_DEPTH`, `MAX_NODE_DEPTH`) keep throwing in both modes. Default behavior is byte-for-byte unchanged — first-party / custom-tool authors who typo a `$ref` still see a hard error. 2. **`@composio/mastra`** — opt `wrapTool` into sentinel mode for both `inputParameters` and `outputParameters` and emit one `logger.warn` per `(toolSlug, ref)` pair via a provider-scoped dedup `Set`. The warning names the tool / toolkit / pointer and links to https://github.com/ComposioHQ/composio/issues/3307. ### Why not centralize the dereferencing in `Tools.ts`? - Mastra is the only provider that wraps `outputParameters`. Vercel / OpenAI / Anthropic / Google bind tool input schemas to function-calling APIs that ignore output shapes. - Centralized dereferencing would silently mutate `tools.get()`'s public output shape for callers iterating raw schemas. - PLEN-2244 set the precedent — the provider that owns the validation contract pre-processes its own schemas. This PR mirrors that boundary. ### API-side follow-up The proper long-term fix is for the Composio API to emit a top-level `$defs` block alongside any `$ref` it produces. That belongs in a separate backend ticket — the SDK fix is intentionally independent so we don't block on a backend rollout. The SDK now degrades gracefully regardless of which API version it talks to. ## Test plan Red-green TDD — every new test was confirmed red against the pre-fix `dereferenceJsonSchema` / `MastraProvider`, then green after the fix. - [x] **Core** — 9 new cases in `ts/packages/core/test/utils/jsonSchema.test.ts` under `describe("with onUnresolved: 'sentinel' lenient mode")` covering: missing-target, missing-key, malformed-pointer (non-`#/` ref), mixed schema (resolvable + dangling siblings), `onReplace` reasons, safety-cap-still-throws (chain & node depth), explicit `'throw'` matches default, `onReplace` not called in strict mode. - [x] **Mastra** — new file `ts/packages/providers/mastra/test/mastra-dangling-defs.test.ts` (6 cases, un-mocked `@mastra/schema-compat`): replicates the exact `GMAIL_FETCH_EMAILS.outputParameters` shape; verifies no throw, no surviving `$ref` in the produced schema, exactly one warn per `(slug, ref)` pair across repeat wraps, a separate warn per slug, symmetric tolerance on `inputParameters`, and a regression guard for resolvable `$defs` (PLEN-2244 contract). - [x] **Regression** — all 908 core tests, all 39 existing `mastra.test.ts` cases, and all 3 `mastra-ref.test.ts` cases continue to pass. Full TS workspace test run: 14 packages green, 1322 tests total. - [x] `pnpm typecheck` passes in both `@composio/core` and `@composio/mastra`. - [x] `pnpm lint` on touched files produces no new warnings. - [ ] **Manual** (post-merge, against live API): `composio.tools.get(userId, { tools: ['GMAIL_FETCH_EMAILS'] })` returns a `MastraTool` whose `outputSchema` is defined; SDK logs exactly one warning per process per `(slug, ref)` pair. ## Files ``` ts/packages/core/src/index.ts # new type exports ts/packages/core/src/utils/jsonSchema.ts # opts + lenient branch ts/packages/core/test/utils/jsonSchema.test.ts # 9 new cases ts/packages/providers/mastra/src/index.ts # opt-in + dedup warn ts/packages/providers/mastra/test/mastra-dangling-defs.test.ts # 6 new cases .changeset/fix-mastra-dangling-defs-ref.md # core/mastra patch plans/fix-mastra-dangling-defs-ref.md # design + acceptance log ``` --------- Co-authored-by: jkomyno <12381818+jkomyno@users.noreply.github.com> |
||
|
|
07160d14fa |
feat(toolchain): introduce mise.toml as single source of truth (#3492)
## Summary This PR starts Phase 1 of PLEN-1368 by adding `mise.toml` as the repo-owned source for the primary toolchain versions: - Node `20.20.2` - Bun `1.3.10` - Deno `2.6.7` - Python `3.12` - uv `0.8.19` It also keeps pnpm corepack-driven through `package.json#packageManager`, adds `devEngines` for Node/pnpm visibility, documents the new `mise install && corepack enable && pnpm install` bootstrap path, ignores `.mise.local.toml`, and fixes stale release-doc prerequisites. ## Why The repo already has real toolchain drift, not just duplicated version strings. The internal release docs had stale Node/Bun/pnpm versions, Deno is repeated across workflows, Dockerfiles, docs, and e2e helpers, and Python local setup still has a separate `3.11` venv path while the repo pins `3.12`. A composite-action cleanup would improve CI, but it would not solve local development. `mise.toml` gives us one file that declares the versions and lets contributors install or switch them with one command. That is the main value proposition here: make the repo declare its own toolchain, then let CI consume the same declaration in the next phase. ## Rollout This is deliberately additive. It does not remove `.nvmrc`, `.bun-version`, `.python-version`, or `.dvmrc`, and it does not change CI behavior yet. Contributors who do not use mise can keep working as before; contributors who do use mise get managed Node/Bun/Deno/Python/uv immediately. Phase 2 can migrate the existing composite actions to `jdx/mise-action@v4`. Phase 3 can remove the legacy version files and add the lockfile once the transition is complete. ## Out of Scope - Migrating CI to `jdx/mise-action@v4` - Removing legacy version files - Adding `mise.lock` - Updating nested TS package/example `packageManager` fields that still say `pnpm@10.28.0`; that is pre-existing metadata drift and should be handled separately to avoid broadening Phase 1 ## Verification - `git diff --check origin/next...HEAD` - `mise install && mise current` - `pnpm --version` -> `10.28.2` - `pnpm dlx prettier@3.8.1 --check package.json` Refs: PLEN-1368 |