## Summary
Adds TypeSafe Jev providers for TypeScript and Python that turn tool
schemas and a request into a call, a partial call, or an abstention.
## What changed
- adds `@composio/typesafe`, a provider for TypeSafe's Jev model. Jev
has no tool calling, so `composio.tools.get()` compiles tools into typed
questions and `decide` returns a `call`, a `partial` call, or an
`abstain`, each with a confidence
- adds `execute` for a user ID or a session: caller arguments complete a
`partial`, and a tool tagged `destructiveHint` routes at a fixed floor
of 0.9 and needs `confirm: true`
- adds the companion helpers `shortlistTools` and `confidenceGate` (a
`beforeExecute` modifier that fails closed) for use with other providers
- adds `composio-typesafe`, the Python counterpart with sync and async
clients; both test suites compile one shared question corpus, so both
SDKs ask Jev the same questions for the same tool
- registers the package in the provider-compatibility release gate, adds
a `minor` changeset, the `ts/examples/typesafe` example, a Python demo,
and a dedicated `py.test.yml` step
- exempts only `@typesafe-ai/sdk@0.6.0` from `minimumReleaseAge`
(publisher, SLSA provenance, and the absence of install scripts were
checked by hand), and sets `engines.node` to `>=24.17.0` for this
package because the SDK terminates the process after a handled
cancellation on older Node.js releases (typesafe-ai/typesafe-sdk-js#2)
## Usage
```typescript
const provider = new TypesafeProvider();
const composio = new Composio({ provider });
const toolSet = await composio.tools.get('user_123', { tools: ['GITHUB_LIST_REPOSITORY_ISSUES'] });
const decision = await provider.decide(toolSet, 'List the closed issues of ComposioHQ/composio');
if (decision.kind !== 'abstain') {
// Jev binds closed-set arguments (enums, booleans, arrays of enums). Free text comes from you.
await provider.execute('user_123', decision, { arguments: { owner: 'ComposioHQ', repo: 'composio' } });
}
```
## Behavior notes
- `abstain` means only that the model judged so. A failed request throws
one `TypesafeApiError` whose `reason` tells rate limits, timeouts, and
rejections apart, and a malformed response throws
`TypesafeMalformedResponseError`. No error holds state, argument values,
response content, or the SDK's own error.
- Routing and the action gate see `request` only, so text in `context`
cannot change which tool is picked. `contextScope: 'all'` opts out.
- State is never truncated: over-budget state, unknown top-level state
keys, and non-JSON values throw.
- The options are `client`, `apiKey`, `model`, `thresholds`, and
`contextScope`. The provider builds its client at log level `warn`, so
`TYPESAFE_LOG_LEVEL=debug` cannot print request bodies.
- Root-level `allOf`, `anyOf`, and `oneOf` schemas are rejected
explicitly in both SDKs, including after `$ref` resolution, so composed
requirements cannot silently disappear. Property-level composition
remains supported as documented.
- Completing a partial decision requires an own, non-`undefined`
argument value in TypeScript; inherited names such as `toString` do not
satisfy required arguments. Supplied `__proto__` keys are preserved as
own data properties.
## Validation
- 147 TypeScript provider tests and 141 Python provider tests pass. The
11 new missing-argument regression cases fail on the original
implementation and pass with the fixes.
- Typecheck, Oxlint, Prettier, the tsdown build with ATTW/publint, Ruff,
mypy, type-inference, and release-gate checks passed locally.
- All 13 opt-in live tests passed across the TypeSafe-only and
Composio-backed suites against real Jev 1.13.0. These tests make
decisions without executing external tools.
- The actual TypeScript and Python Hacker News examples both ran end to
end against production APIs: fetch tools, decide, detect the missing
username, supply `pg`, and execute the read-only lookup. Both returned
the live profile for `pg`.
Not in this PR: the docs page, which needs the first npm publish so its
snippets compile. The first npm and PyPI publishes and a
`TYPESAFE_API_KEY` CI secret are manual steps.
```mermaid
flowchart LR
A[composio.tools.get] --> B[compile tools into questions]
B --> C[decide: state + questions]
C --> D{Jev answers}
D -->|none fits, no action, low confidence| E[abstain]
D -->|required arguments missing| F[partial]
D -->|everything bound| G[call]
F -->|caller arguments| H[execute]
G --> H
H -->|destructive tool| I[needs confirm: true]
```
## Why
Requirement 1 of the PRD: sessions accept all four verdict hints. The
Configuring Sessions tag table listed the four MCP-spec hints, two of
which (idempotentHint, openWorldHint) are set on a minority of tools.
Every tool carries at least one of readOnlyHint, createHint, updateHint,
destructiveHint.
## What
- Tag table leads with the four verdict hints and says every tool
carries at least one; idempotentHint and openWorldHint noted as accepted
with partial coverage.
- Callout: the v3 tools endpoints default to the pinned version
00000000_00, sessions read latest.
- Python example uses createHint. The TypeScript twoslash example stays
on readOnlyHint so docs CI passes against the published SDK; switch it
to createHint when merging, after #4467 is released.
- Python and TypeScript SDK reference docs list the widened enum.
## Merge after
API: platform#12843 (accept createHint and updateHint). SDK:
composio#4467 released.
PRD:
https://app.notion.com/p/composio/Session-Governance-via-hints-Across-toolkits-3daf261a6dfe80df8e0ce337a2b26e08
Linear workstream:
https://linear.app/composio/project/sessions-execution-governance-a0942233a0d0
Stack order (merge top to bottom, each after its API change is
deployed): D1 verdict hints, D2 precedence, D3 proxy execute toolkit
lists, D4 MCP classification, D5 proxy execute API key permission.
Verification, run in `docs/` at the top of the stack (D5 head, which
contains this PR): `bun run types:check` passes, `bun run build`
compiles (twoslash blocks type-check against the published
`@composio/core`), `bun run lint:links` reports 0 errors, `bun run test`
568 pass. `pnpm exec prettier --check` flags the changed mdx files on
`next` already, so no reformatting was applied.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01VHkYsmhteM1jJQoaoruiP3
The handleAssistantMessage, waitAndHandleAssistantStreamToolCalls, and
waitAndHandleAssistantToolCalls methods target the OpenAI Assistants
API, which shuts down on August 26, 2026. Add a deprecation warning
pointing new flows at OpenAIResponsesProvider.
- Use gpt-5 in the Responses API examples; gpt-4 predates the Responses
API and the repo's other Responses examples use gpt-5.
- Print response.output_text instead of indexing into content items,
which assumes non-empty message content.
- Add the OpenAIResponsesProvider type surface to the Type Definitions
section, which previously only showed the chat completions provider.
Every tool carries at least one of readOnlyHint, createHint, updateHint
or destructiveHint, but the tag table and SDK reference docs only listed
the four MCP-spec hints, two of which (idempotentHint, openWorldHint)
are set on a minority of tools. Lead with the four verdict hints and
note the pinned-version gotcha on the v3 tools endpoints. The TypeScript
twoslash example stays on readOnlyHint until @composio/core ships the
widened enum (composio#4467).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHkYsmhteM1jJQoaoruiP3
Updated documentation to reflect changes in OpenAI Provider usage, including the transition from OpenAI Assistants to OpenAI Responses API and the introduction of OpenAIResponsesProvider.
This PR:
- reverts https://github.com/ComposioHQ/composio/pull/3780
- restores the TypeScript `composio.create()` and `composio.use()`
session aliases, with their runtime and type-test coverage
- restores the prior deprecation state for
`BaseProvider.wrapMcpServerResponse`
- removes the pre-v1 breaking-change changeset so the release PR no
longer advertises this removal
The v1 API-freeze change should be recreated later as a draft PR and
kept out of the merge queue until v1 is ready.
## What and why
This makes the intended v1 API cleanup real rather than postponing it to
v2:
- Remove the TypeScript root aliases `composio.create(...)` and
`composio.use(...)`. Session creation and reuse now live only at
`composio.sessions.create(...)` and `composio.sessions.use(...)`. This
is a deliberate breaking change, reflected by a major changeset.
- Retain `BaseProvider.wrapMcpServerResponse` as the stable v1 provider
SPI. Its earlier deprecation pointed to a method that was never
introduced.
The scope is intentionally narrow: it does **not** remove unrelated
deprecated APIs, and Python keeps its supported `Composio.create/use`
API. The TypeScript docs, examples, providers, runtime fixtures,
generated SDK reference, and API-reference indexes now use the
namespaced TypeScript API. Historical changelog examples are left as
history.
## Verification
- `pnpm typecheck`
- `pnpm --filter @composio/core test -- --run test/core/session.test.ts`
(42 files, 1,018 tests)
- `pnpm exec eslint ts/packages/core/src/composio.ts`
- `pnpm --filter @composio/core generate:docs`
- `cd docs && bun run generate:api-index`
- `cd docs && bun run types:check`
- `git diff --check` and targeted scans for removed TypeScript aliases
I also attempted the affected Node and Cloudflare runtime E2E suites.
They cannot initialize in this checkout without `COMPOSIO_API_KEY` (and,
for Cloudflare, `COMPOSIO_BASE_URL` and `OPENAI_API_KEY`); they did not
report a product assertion failure.
## What
Removes orphaned files and stale config that nothing references — a
follow-up to the root `Dockerfile` cleanup (#3783), which surfaced that
we'd accumulated dead code.
| Removed | Why it's dead |
|---|---|
| `test-exports.ts` | Throwaway debug script — `console.log`s schema
internals and imports from `ts/packages/core/dist/` (a build artifact
absent on a clean checkout). Zero references. |
| `.codex` | Empty 0-byte file, accidentally committed. Every `.codex`
reference in the code means `~/.codex` (the Codex CLI home dir), not
this. |
| `ts/docs/api/mcp.old.md` | Orphaned `.old` doc, superseded by
`ts/docs/api/mcp.md`. Not linked anywhere. |
| `pnpm-workspace.yaml` → `ts/packages/wrappers/*` | Workspace glob
pointing at a directory that doesn't exist. |
| `docs/examples/{app-connections-dashboard, workplace-search,
support-agent, background-agent}` | Example dirs not wired into the docs
build **and** absent from `docs/decisions/cookbooks-revamp-plan.md` —
unlike their siblings (`chat-app`, `pr-review-agent`, …) which are
explicitly staged WIP cookbooks. Zero references in the tree. |
## Verification
Each item was traced with `git grep` over tracked files (excluding
lockfiles / generated data) and confirmed to have no consumers in code,
CI, docs nav, or build config. Python side was audited too and is clean
(no `.bak`/`.old` files, all scripts/providers/modules wired).
## Note
If any of the four `docs/examples/` dirs are undocumented cookbook
drafts rather than truly abandoned, shout and I'll restore them — they
just aren't referenced or tracked in the revamp plan today.
A follow-up PR adds **Knip** (TS) + **Vulture** (Python) to CI so this
class of rot gets caught automatically.
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
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
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.`
## 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`
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.
## 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
## Summary
Automatic tool file upload/download for `file_uploadable` fields is
**off by default** in TypeScript and Python. Callers must explicitly opt
in, and uploads from local paths are constrained by a fail-closed
allowlist.
## Changes
- **Removed (breaking):** `autoUploadDownloadFiles` (TS) /
`auto_upload_download_files` (Python) — the legacy default-on flag is
gone, not just deprecated.
- **New opt-in:** `dangerouslyAllowAutoUploadDownloadFiles` (TS) /
`dangerously_allow_auto_upload_download_files` (Python). When `true`,
`tools.get(...)` collapses `file_uploadable` schemas to `{ type:
'string', format: 'path' }` and the SDK stages local paths/URLs at
execute time.
- **New:** `fileUploadDirs?: string[] | false` — fail-closed allowlist
for local upload paths. `undefined` → `[<home>/.composio/temp]`; `false`
→ reject all local paths (URLs / `File` objects unaffected); explicit
`string[]` replaces the default. Components are matched on a path
boundary after `realpath`.
- **New:** `fileDownloadDir?: string` — directory where
`file_downloadable` results are staged.
- **New:** `beforeFileUpload` hook receives `source: 'path' | 'url' |
'file'` (TS) / `'path' | 'url'` (Python) so it can branch on input type.
- **New (TS):** when auto-upload is **off** and an LLM-driven
`tools.execute` is called against a tool with `file_uploadable` inputs,
the SDK emits a one-shot warning per tool slug pointing at
`composio.files.upload()` for manual staging.
## Migration
To restore previous behavior:
```ts
new Composio({
apiKey: process.env.COMPOSIO_API_KEY!,
dangerouslyAllowAutoUploadDownloadFiles: true,
// Optional: tighten the allowlist beyond the default ~/.composio/temp
fileUploadDirs: ['/srv/uploads'],
});
```
```python
Composio(api_key="...", dangerously_allow_auto_upload_download_files=True)
```
If you previously passed the legacy flag, remove it. There is no
transitional warning — TS and Python both reject the unknown property at
the type/keyword-arg level.
## Versioning
| Package | Bump |
| ------- | ---- |
| `@composio/core` | minor |
| `composio` (Python) | minor |
| Other `@composio/*` packages | patch (via changesets
`updateInternalDependencies: "patch"`) |
See
`docs/content/changelog/04-24-26-legacy-auto-upload-config-removal.mdx`
for the full migration writeup.
## Summary
Security-hardening for automatic file upload in `@composio/core` (patch
release per changeset).
### Changes
- **Default denylist** for local paths before auto-upload /
`files.upload`: blocks common credential directories (e.g. `.ssh`,
`.aws`) and credential-like filenames (e.g. `.env`, default SSH private
keys). Resolves symlinks when the path exists.
- **Config:** `sensitiveFileUploadProtection`,
`fileUploadPathDenySegments` on `Composio`.
- **`beforeFileUpload`** hook (e.g. with `composio.tools.get` /
`tools.execute`): rewrite path, return `false` to abort, or throw.
- **Errors:** `ComposioSensitiveFilePathBlockedError`,
`ComposioFileUploadAbortedError`; file modifier errors exported from
`@composio/core` errors entry.
- **Changeset:** patch bump for `@composio/core`.
### Notes
- URLs and `File` blobs are not subject to the path denylist
(unchanged).
- Opt out of path checks only if required:
`sensitiveFileUploadProtection: false`.
### Tests
- `pnpm test` in `ts/packages/core` (799 tests) passed locally before
commit.
Made with [Cursor](https://cursor.com)
## Summary
- Add support for beta CLI releases in the binary publish workflow,
including beta tag validation and promotion to stable from an existing
prerelease tag.
- Update `composio upgrade` to support a `--beta` flag that stays on the
prerelease channel.
- Refresh release documentation and CLI README to describe the new
beta/stable release model.
- Expand upgrade binary tests to cover prerelease resolution and
channel-specific behavior.
## Testing
- Not run (PR content generation only).
- Checked the workflow and CLI changes for beta/stable tag handling
paths.
- Reviewed updated release documentation for the new promotion flow.
- Add session.customTools({ toolkit? }) and session.customToolkits() methods
returning registered tools with final slugs, schemas, and resolved toolkit
- Early slug length validation in createCustomTool (standalone + extension)
and createCustomToolkit (per tool), with buildCustomToolsMap as safety net
- Reject COMPOSIO_ prefix in CustomToolSlugSchema (prevents meta tool shadowing)
- Rename execute fn param from session → ctx everywhere (type def, docs, examples, tests)
- SessionContextImpl as singleton on ToolRouterSession (created once in constructor)
- Rename internal byPrefixed/byOriginal → byFinalSlug/byOriginalSlug
- Store resolved toolkit on map entries for session.customTools() lookups
- Add RegisteredCustomTool and RegisteredCustomToolkit types to Session interface
- JSDoc on extendsToolkit: must be a valid Composio toolkit slug
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>