Files
composiohq__composio/AGENTS.md
T
Alberto Schiabel 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.`
2026-06-19 22:22:17 +04:00

9.5 KiB

AGENTS.md

This file provides guidance to AI coding agents (Codex, Claude Code, etc.) when working with code in this repository.

Overview

This is the Composio SDK v3 repository containing both TypeScript and Python SDKs. The main development focus is on the TypeScript SDK located in /ts/ directory. The project uses a monorepo structure with multiple packages and examples.

Notes

  • For documentation tasks, refer to docs/CLAUDE.md
  • Vendor submodules in ts/vendor/ are read-only reference only — do not modify them

Common Development Commands

Build and Development

pnpm build              # Build all packages
pnpm build:packages     # Build only TypeScript packages
pnpm clean              # Clean build artifacts
pnpm lint               # Lint code
pnpm lint:fix           # Lint and auto-fix
pnpm format             # Format code
pnpm test               # Run tests

Package Management

mise install            # Install Node, Bun, Deno, pnpm, Python, uv (one-time; needs mise — https://mise.jdx.dev/installing-mise.html)
pnpm install            # Install dependencies
pnpm check:peer-deps    # Check peer dependencies
pnpm update:peer-deps   # Update peer dependencies

Creating New Components

pnpm create:provider <provider-name> [--agentic]
pnpm create:example <example-name>

Release Management

pnpm changeset            # Create changeset for releases
pnpm changeset:version    # Version packages
pnpm changeset:release    # Publish packages

Project Architecture

Repository Structure

composio/
├── ts/                      # TypeScript SDK (main development)
│   ├── packages/
│   │   ├── core/           # Core SDK functionality
│   │   ├── providers/      # AI provider integrations (OpenAI, Anthropic, etc.)
│   │   ├── cli/           # Command-line interface (Effect.ts + @clack/prompts)
│   │   ├── json-schema-to-zod/ # Schema conversion utility
│   │   └── ts-builders/   # TypeScript code generation utilities
│   ├── vendor/            # Read-only reference submodules (Effect, Clack)
│   └── examples/          # Usage examples for different providers
├── python/                # Python SDK
├── docs/                  # Documentation (Fumadocs)
└── examples/              # Cross-platform examples

Core Packages

@composio/core — Main SDK functionality:

  • src/composio.ts — Main Composio class
  • src/models/ — Core models (Tools, Toolkits, ConnectedAccounts, etc.)
  • src/provider/ — Base provider implementations
  • src/services/ — Internal services (telemetry, pusher)
  • src/types/ — TypeScript type definitions
  • src/utils/ — Utility functions and helpers

Provider Packages — AI integrations:

  • @composio/openai, @composio/anthropic, @composio/google
  • @composio/langchain, @composio/vercel, @composio/mastra

Key Concepts

  • Tools — Individual functions (e.g., GITHUB_CREATE_REPO, GMAIL_SEND_EMAIL)
  • Toolkits — Collections of related tools grouped by service (github, gmail, slack)
  • Connected Accounts — User authentication/authorization for external services
  • Auth Configs — Configuration for different authentication methods
  • Custom Tools — User-defined tools with custom logic
  • Providers — Integrations with AI frameworks (OpenAI, Anthropic, etc.)
  • Modifiers — Middleware to transform tool inputs/outputs

Development Workflow

Testing

  • Unit tests use Vitest — run with pnpm test
  • Tests are in test/ directories within each package
  • Mock implementations in test/utils/mocks/

Code Quality

  • ESLint — config in eslint.config.mjs
  • Prettier — for code formatting
  • TypeScript — strict mode enabled
  • Husky — pre-commit hooks for quality checks

TypeScript E2E Tests

pnpm test:e2e              # All (Node.js + Deno + Cloudflare)
pnpm test:e2e:node         # Node.js ESM and require(esm) interop (Docker)
pnpm test:e2e:deno         # Deno npm: specifier compatibility (Docker)
pnpm test:e2e:cloudflare   # Cloudflare Workers

Environment Variables

COMPOSIO_API_KEY            # Required: Your Composio API key
COMPOSIO_BASE_URL           # Optional: Custom API base URL
COMPOSIO_LOG_LEVEL          # Optional: silent, error, warn, info, debug
COMPOSIO_DISABLE_TELEMETRY  # Optional: Set to "true" to disable telemetry

Key Files and Locations

  • Main SDK Entry: ts/packages/core/src/index.ts
  • Core Composio Class: ts/packages/core/src/composio.ts
  • Type Definitions: ts/packages/core/src/types/
  • Error Classes: ts/packages/core/src/errors/
  • Documentation: docs/
  • Build Configs: turbo.jsonc, tsconfig.base.json, tsdown.config.base.ts

Common Patterns

Tool Execution

const composio = new Composio({ apiKey: 'your-key' });
const result = await composio.tools.execute('TOOL_NAME', {
  userId: 'user-id',
  arguments: {
    /* tool args */
  },
});

Provider Integration

import { OpenAIProvider } from '@composio/openai';
const provider = new OpenAIProvider({ apiKey: 'openai-key' });
const tools = await composio.tools.get('user-id', { toolkits: ['github'] });
const wrappedTools = provider.wrapTools(tools);

Custom Tool Creation

import { Composio, experimental_createTool } from '@composio/core';
import { z } from 'zod';

const composio = new Composio();

// Custom tools are session-scoped: define a local tool, then attach it when
// creating a Tool Router session. Use `extendsToolkit` + `ctx.proxyExecute`
// to inherit a toolkit's managed auth and call its API.
const myTool = experimental_createTool('MY_TOOL', {
  name: 'My Tool',
  description: 'Tool description',
  inputParams: z.object({
    param: z.string().describe('Parameter description'),
  }),
  execute: async input => {
    return { result: input.param };
  },
});

const session = await composio.create('user-id', {
  experimental: { customTools: [myTool] }
});

Python SDK Development

Setup

cd python
make env                 # Create virtual env with all dependencies
source .venv/bin/activate

Commands

make fmt       # Format code (ruff)
make chk       # Check linting and type issues
make tst       # Run tests
make snt       # Run sanity tests
make build     # Build packages

Code Quality

  • Formatter: Ruff (Black-compatible, 88 char line length)
  • Linter: Ruff
  • Type Checker: mypy with strict optional typing
  • Test Framework: pytest with markers (core, openai, langchain, agno)
  • Python Version: >=3.10, <4

Maintenance Tasks

When Updating GitHub Actions

Toolchain versions are coalesced in mise.toml. To check the current values:

  • Node.js: mise current node
  • Bun: mise current bun
  • Deno: mise current deno
  • Python: mise current python
  • uv: mise current uv
  • pnpm: mise current npm:pnpm

To bump, edit mise.toml, then run:

mise lock --platform linux-x64,linux-arm64,macos-arm64,macos-x64

Commit mise.lock with the version bump. The "Prerequisites" section in ts/docs/internal/release.md references mise.toml so it stays current automatically.

When Bumping the Generated SDK Client (@composio/client / composio-client)

The Stainless-generated clients are published from separate repos (ComposioHQ/composio-base-ts → @composio/client on npm; ComposioHQ/composio-base-py → composio-client on PyPI). Nothing auto-bumps them here — there is no Stainless→consumer bot, and Dependabot does not touch the pnpm catalog: pin (TS) or the exact composio-client== pin (Python). Bumping is manual, and the pin lives in multiple files that must move together:

  1. Confirm the version is actually published first. Check npm view @composio/client version / pip index versions composio-client. The Python PyPI publish in composio-base-py can fail silently (e.g. 403 Forbidden from a stale PYPI_TOKEN) even when the GitHub release/tag exists — verify the package resolves before bumping.

  2. TypeScript — edit the catalog pin, refresh the lockfile, add a changeset:

    # edit pnpm-workspace.yaml -> catalog: '@composio/client'
    pnpm install --lockfile-only          # updates pnpm-lock.yaml
    # add a patch changeset for @composio/core and @composio/cli (both consume it)
    
  3. Python — the pin lives in three places; update all of them or uv sync / published metadata will be inconsistent:

    • python/pyproject.toml (dependencies)
    • python/setup.py (install_requires) — easy to miss; setuptools fallback metadata
    • root uv.lock — regenerate from the workspace root: uv lock --upgrade-package composio-client
  4. A version bump is not enough across a breaking regen. The generated client can remove or restructure modules between releases (e.g. composio-client 1.41.0 deleted types.tool_router_create_session_params), which surfaces as an import-time ModuleNotFoundError that reds every job importing the SDK. After bumping, actually import/typecheck the consuming code (uv run --package composio python -c "import composio", pnpm typecheck) and migrate any references the new client dropped. Prefer decoupling from churny generated types — define small local TypedDicts instead of importing internal client submodules.

This monorepo uses pnpm workspaces and Turbo for efficient builds and development.