This PR:
- prepares the coordinated September 4 changelog for CLI `0.4.1`, Python
SDK `0.21.1`, and the TypeScript SDK release
- gives DevRel one customer-facing source for credential security, file
transfers, JSON Schema behavior, and custom-tool routing
- records the TypeScript provider and schema-converter package matrix,
including the releases added after #4316 merged
- adds the `@composio/core` `0.18.1` row that the refreshed release PR
#4285 now requires
- corrects the download-limit guidance and documents the fallback for a
`$ref` without matching `$defs`
The listed versions are coordinated release targets. They are not all
published yet, so this changelog and the release PRs still need to be
sequenced together.
## Verification
- `pnpm exec prettier --check
docs/content/changelog/09-04-26-cli-and-sdk-releases.mdx`
- `cd docs && bun run types:check`
- `cd docs && bun run lint:links`
- `cd docs && bun run test` (541 passed)
- `pnpm test:release-workflow`
## Summary
Applies the top findings from a multi-reviewer code review of #4335
(which merged before these could land on the PR branch). Four validated
findings, all small and behavior-preserving outside the fixes
themselves:
- **Cross-tab theme fight (P1):** #4335 routed the product-derived theme
through next-themes' shared `theme` localStorage key -- written by the
root layout's inline head script on every hard load and by `setTheme` on
every client switch. next-themes listens for cross-tab storage events on
that key, so two docs tabs on different products (Platform dark / For
You light) silently repaint each other with no self-heal (the provider
effect's deps are `[product, setTheme]`, so the flipped tab never
corrects). The product theme is derived state, not a preference: this PR
applies it directly to the document element (`applyProductTheme`) and
passes `forcedTheme: initialTheme` from the server-resolved product so
hydration cannot flip a stale stored value. No `theme` localStorage
writes remain anywhere.
- **theme-color meta (P2):** the two `prefers-color-scheme`-keyed metas
meant mobile browser chrome mismatched the forced page theme (white
chrome over dark Platform pages for light-OS users). Now a single meta
keyed to the product theme.
- **Switcher current-option href (P2):** the popover option marked
`aria-current="page"` resolved to the product landing route, so
middle-click, hover status bar, and copy-link all pointed at the wrong
URL. It now hrefs the current pathname.
- **Explore-card aria-label (P3):** `aria-label` replaced the link's
accessible name, so the product description inside the card was not
announced. Dropped; heading + description now form the name.
## Changes
- `docs/app/layout.tsx` -- inline script no longer writes localStorage
(pre-paint class priming unchanged); single product-keyed `theme-color`
meta; `forcedTheme: initialTheme` on `RootProvider`.
- `docs/components/docs-product-context.tsx` -- `setTheme`/`useTheme`
removed; new `applyProductTheme` used in the product effect and the
flushSync commit.
- `docs/components/product-switcher.tsx` -- `destination = isCurrent ?
pathname : docsProductDestination(...)`.
- `docs/components/home-surfaces.tsx` -- Explore-card `aria-label`
removed.
- `docs/tests/static/product-navigation.test.ts` -- pins the new
invariants (`applyProductTheme`, `forcedTheme: initialTheme`, and a
negative assertion that `localStorage.setItem('theme'` stays out).
## Testing
- `bun test tests/static/` -- 541 pass / 0 fail
- `bun run types:check` -- clean
- `bun run lint` -- only pre-existing warnings (`home-surfaces.tsx:102`
`no-img-element` is in `ForYouVisual`, untouched)
- Worth a manual check: two tabs on different products no longer repaint
each other (static tests cannot prove cross-tab storage isolation)
## Notes
- Docs-only change; no changeset required.
- Review context: follow-up to #4335. Remaining review findings
(navigation state-machine races, theme-scope design call, decision
record) are tracked separately.
## Summary
- preserve boolean, empty, null, type-array, enum, const, and
scalar-constraint semantics across every Python conversion entry point
- intersect Zod enum and const values with declared types and
constraints, including compound JSON values
- default unversioned exact validation to Draft 7 and apply inclusive
and numeric exclusive bounds independently
- run one byte-identical corpus through Python, Zod, and Effect so
accepted and rejected inputs stay aligned
- keep exact JSON Schema acceptance separate from Pydantic default
materialization
## Review follow-up (second push)
- Python: exact Draft 7 acceptance now wraps all three entry points
(`json_schema_to_pydantic_type`, `json_schema_to_model`,
`pydantic_model_from_param_schema`), so they can no longer disagree
- Python: draft-4 boolean `exclusiveMinimum`/`exclusiveMaximum` (OpenAPI
3.0 style) no longer crash conversion — exact validation falls back to
Draft 4, and the library input is translated to the numeric spelling
- Python: ECMA-only regex patterns (look-around) no longer crash
pydantic model builds — Rust-incompatible patterns fall back to Python
`re`
- Python: type arrays with sibling constraints no longer raise
`TypeError` on valid input — constraints are scoped per member before
the library sees them
- Python: integral floats satisfy `integer`, `const` intersects `enum`,
annotation-only schemas accept anything, and an optional property with
an empty `enum` tolerates absence
- Zod: typeless scalar constraints apply per instance type, and string
lengths count Unicode code points instead of UTF-16 code units
- Effect: draft-4 boolean exclusive bounds are enforced instead of
silently ignored
- `multipleOf` uses decimal scaling in all three converters (declared
`divergesFromJsonSchema` on the corpus case)
- shared corpus grows by 13 primitive cases; new property-based tests
check acceptance against real Draft 7 oracles (hypothesis + `jsonschema`
in Python, fast-check + Ajv in TypeScript)
## Verification
- Python `make chk` (ruff + mypy)
- Python pytest: 1,572 passed (5 langchain-extra tests need an env this
sandbox lacks; unchanged from base)
- `@composio/json-schema-to-zod`: 187 passed incl. 300-run fast-check
property test; typecheck + build
- `@composio/json-schema-to-effect-schema`: 133 passed; typecheck
- `@composio/core` corpus ingress tests: 61 passed
- shared Python/TypeScript corpus files are byte-identical
(shasum-verified)
- `git diff --check`
## Contributor context
This replaces four narrow proposals after independent local
reproduction:
- [#4301](https://github.com/ComposioHQ/composio/pull/4301) ·
[Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4301)
- [#4302](https://github.com/ComposioHQ/composio/pull/4302) ·
[Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4302)
- [#4303](https://github.com/ComposioHQ/composio/pull/4303) ·
[Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4303)
- [#4307](https://github.com/ComposioHQ/composio/pull/4307) ·
[Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4307)
---------
Co-authored-by: simpleqt <89645338+simpleqt@users.noreply.github.com>
## Summary
Add a product context switcher that separates the docs into two paths
just like the Dashboard:
- Composio For You, for connecting apps to existing AI clients
- Composio Platform, for building agents with the SDK
The selected product controls the docs sidebar and persists across
navigation. Product-specific URLs set the matching context, while shared
pages remain open when readers switch products.
https://github.com/user-attachments/assets/e3f6905d-7238-43a2-a51e-145a01688c04
## Changes
- Define product labels, landing pages, route ownership, sidebar groups,
and equivalent destinations in one configuration.
- Build separate Fumadocs page trees for For You and Platform.
- Add an accessible product switcher and a separate home link to the
site header.
- Force dark mode for Platform and light mode for For You, with no
separate theme toggle.
- Update the homepage cards to select the matching product.
- Add a reduced-motion-aware transition with a 1.5-second navigation
timeout.
- Test route classification, destination mapping, complete page-tree
coverage, and switcher semantics.
## Testing
- `bun test tests/static/ --reporter=dot` — 541 passed
- `bun run types:check`
- `bun run lint`
- `bun run build`
- Tested both product directions in the local docs site.
- Verified that the home link works, Platform stays dark, For You stays
light, and shared Security pages remain open when switching products.
## Notes
This is a docs-only change and does not require a changeset.
## 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
## Summary
Clarifies that the managed auth page covers OAuth apps, not every
credential type.
Fixes #
## Changes
- Renames the page and tabs around managed OAuth.
- Explains the difference between OAuth app credentials and user
authorization.
- Clarifies how toolkits with multiple authentication methods appear.
- Keeps the browser and agent-readable versions consistent.
- Replaces the API-key-based PostHog example with Gmail.
## Type of change
- [ ] Bug fix
- [ ] New feature
- [ ] Refactor/Chore
- [x] 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
## Summary
Auto-generated Python SDK reference docs from `python/composio/`.
Regenerates pages at `docs/content/reference/sdk-reference/python/` to
reflect changes in the Python package's public API (new methods, updated
signatures, changed types).
Co-authored-by: jkomyno <12381818+jkomyno@users.noreply.github.com>
## Summary
Automated docs update triggered by SDK source changes on `next`.
- Claude reviewed the SDK diff and updated guides, FAQs, or examples
that reference changed APIs or features.
- The docs `@composio/*` dependencies were realigned to their latest
published releases so Twoslash snippets and example apps validate
against versions users can actually install.
## Review checklist
- [ ] Changes accurately reflect the new SDK behavior
- [ ] No unrelated docs were modified
- [ ] Code examples are correct and complete
- [ ] If a documented feature is not published yet, the Twoslash build
will fail — wait for the release instead of working around it
Generated by Claude Code via GitHub Actions.
Co-authored-by: jkomyno <12381818+jkomyno@users.noreply.github.com>
## Summary
The Python SDK treated a custom tool's `original_slug` as globally
unique, rejecting valid custom toolkits that reuse common child names
such as `SEARCH`, `VERSION`, or `GREP` even though the backend-assigned
final slugs are toolkit-qualified (`LOCAL_ALPHA_GREP`,
`LOCAL_BETA_GREP`).
This ports the toolkit-qualified lookup from #3360 to Python, then fixes
three response-mapping bugs found in review and applies the same fixes
to the TypeScript SDK so both stay in parity.
## Changes
### Python (`composio`)
- Scope custom-tool collision detection and response matching by toolkit
plus original slug.
- Keep bare original-slug aliases only when unambiguous;
`session.execute("GREP")` raises with the final slugs to use when the
slug is shared.
- Preserve toolkit-qualified final slugs in `custom_toolkits()`.
- `build_custom_tools_map_from_response`: raise when a response tool has
local handles but no exact toolkit match instead of silently dropping it
or binding another toolkit's handler; only fall back to a bare match
when the response carries no toolkit identity; reject duplicate
qualified response entries; derive bare-slug ambiguity from local
definitions so omitting a sibling in the response never makes the
survivor callable by bare name.
- `custom_toolkits()` only reuses a bare alias that belongs to the same
toolkit.
- Docstring and Python session reference page state that bare-slug
execution requires a unique original slug.
### TypeScript (`@composio/core`)
- Same four fixes in `buildCustomToolsMapFromResponse` and the same
guard in `customToolkits()`.
- JSDoc and TypeScript session reference page updated.
- Changeset: patch for `@composio/core`.
### Not changed
- `COMPOSIO_MULTI_EXECUTE_TOOL` still aborts the whole batch when one
item uses an ambiguous bare slug, matching current TS behavior.
Switching to per-item errors is a cross-SDK design change left for a
follow-up.
## Type of change
- [x] Bug fix
- [ ] New feature
- [ ] Refactor/Chore
- [ ] Documentation
- [ ] Breaking change
## How Has This Been Tested?
Python:
- `pytest tests/test_custom_tools.py tests/test_tool_router.py`: 181
passed.
- ruff (project config) clean; mypy reports no errors in the touched
files.
- New tests: sibling routing, multi-execute, preload rejection, listing
guard, and five response-mapping cases (no exact match, cross-toolkit
binding, standalone bare fallback, unknown response tools skipped,
ambiguity from local definitions, duplicate qualified entries).
TypeScript:
- `vitest run` in `ts/packages/core`: 53 files, 1251 passed, 2 expected
failures.
- `tsc --noEmit` clean; prettier and oxlint via pre-commit hook.
- New tests: cross-toolkit reuse in `buildCustomToolsMap` and a new
`buildCustomToolsMapFromResponse` block mirroring the Python cases.
Python and TypeScript CI do not run automatically on this fork PR; a
maintainer needs to approve the workflow run.
## Checklist
- [x] I have read the Code of Conduct and this PR adheres to it
- [x] I ran linters/tests locally and they passed
- [x] I updated documentation as needed
- [x] I added tests or explain why not applicable
- [x] I added a changeset if this change affects published packages
## Additional context
Reviewed with a second opinion from Codex (gpt-5.6-sol), which flagged
the wrong-handler binding and response-derived ambiguity bugs fixed in
the follow-up commits.
https://claude.ai/code/session_01Y7Ni3QEBDGShSrEtwQS5bA
EOF -R ComposioHQ/composio
---------
Signed-off-by: CoralGarden52 <2193436736@qq.com>
Co-authored-by: jkomyno <alberto@composio.dev>
Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
## Problem
`composio.utils.mimetypes.guess()` maps `.jpg` to `"image/jpg"`:
```python
".jpe": "image/jpeg",
".jpeg": "image/jpeg",
".jpg": "image/jpg", # <- non-standard
```
`image/jpg` is not a registered IANA media type. It is inconsistent
with:
- its own sibling extensions `.jpe`, `.jpeg`, and `.jfif-bnl`, which all
map to `image/jpeg`
- Python's standard library: `mimetypes.guess_type("x.jpg")` returns
`image/jpeg`
- IANA, which registers `image/jpeg`
`guess()` feeds the `Content-Type` used for presigned uploads
(`_upload_to_presigned_url`), so `.jpg` files are uploaded and stored
with a non-standard content type.
## Fix
Map `.jpg` to `image/jpeg`. One line.
The reverse `_MIME_TO_EXT` map keeps its `"image/jpg": "jpg"` entry on
purpose: the SDK should still accept `image/jpg` when a server sends it
in a `Content-Type` header (liberal in what it accepts, strict in what
it produces).
## Tests
Added coverage in `tests/test_mimetypes.py`: `.jpg`/`.jpeg` in the
known-extensions table plus a dedicated test documenting that `.jpg`
resolves to `image/jpeg`. Verified the new tests fail on the old value
and pass after the fix; the full `test_mimetypes.py` (35 tests) passes.
## Summary
- Apply the existing authConfigId subscription filter to incoming
trigger events.
- Add a V3 regression test for mismatched auth configurations.
- Add a patch changeset for @composio/core.
Previously, a subscription filtered by authConfigId still invoked its
callback for events belonging to a different auth configuration.
## Verification
- Vitest targeted tests: 76 passed
- Vitest core suite: 1244 passed, 2 expected failures
- TypeScript typecheck and Prettier passed
Fixes the missing authConfigId filtering in trigger subscriptions.
---------
Co-authored-by: jkomyno <alberto@composio.dev>
## Summary
- map Python file-fetch failures that occur after response headers into
the documented upload and download errors
- map TypeScript RemoteFile connection and streamed-body failures into
RemoteFileDownloadError while preserving blocked-URL errors
- close or cancel response bodies on every exit and apply the shared 100
MiB response limit to TypeScript RemoteFile downloads
This supersedes the Python-only proposal in #4305 and carries the same
failure category across both SDKs.
## Independent reproduction
A response double returned one chunk and then raised a connection-reset
error. On current next:
- Python _fetch_file_from_url leaked ConnectionError, although it did
close the response
- Python Tool Router URL fetch leaked ConnectionError and left the
response open
- TypeScript RemoteFile leaked the native fetch/body TypeError instead
of RemoteFileDownloadError
## Verification
- Python make chk
- Python make tst: 1,490 passed
- TypeScript core typecheck
- TypeScript core tests: 1,245 passed, 2 expected failures
- TypeScript package build: 19 packages
- focused Python regression tests: 3 passed
- focused TypeScript RemoteFile tests: 17 passed
## Summary
- route attacker-controlled URL sources and API-provided presigned
upload destinations through the runtime-conditional core SSRF guard
- expose that guard through a Node/workerd-aware core subpath
- validate and revalidate DNS on redirects, pin Node and Bun connections
to validated addresses, and preserve configured proxy routes
- fail closed for user-chosen URL uploads in edge runtimes
- cancel ignored response bodies and cover both upload boundaries at the
public CLI pipeline
- avoid adding `Content-Length: 0` to bodyless Bun GET requests
## Local reproduction
On `next`, the CLI pipeline used bare `fetch` for both targets. A local
loopback source and an internal presigned destination reached the
network path. With this branch, the real `uploadToolInputFiles` pipeline
blocks a `127.0.0.1` source before presigning and a `169.254.169.254`
destination before sending bytes.
A direct Bun proof also confirmed the pinned transport reaches a
validated address without calling native `fetch`, while retaining the
original Host header. Focused tests preserve native Bun fetch when an
environment proxy is configured.
## Cross-SDK parity
Python already applies public-address validation, redirect checks, DNS
pinning, and response limits to URL uploads. Its focused URL-safety and
upload suite remains green: 62 passed. No Python behavior change was
needed.
## Verification
- `pnpm build:packages`: 19 packages built
- root `pnpm typecheck`: 14 package checks passed
- TypeScript core: 53 files, 1,246 passed and 2 expected failures
- focused core SSRF and pinned-transport tests: 31 passed
- Python URL-safety/upload tests: 62 passed
- real Bun execution of both CLI upload boundaries: blocked before
presign/send
- `git diff --check`
The CLI Effect test file contains source and presigned-destination
regressions and typechecks. Its local runner is blocked on current
`next` by the repository-wide `@effect/vitest` config failure; hosted
CLI checks exercise that boundary.
## Contributor context
Supersedes [#4299](https://github.com/ComposioHQ/composio/pull/4299) ·
[Glen](https://app.tryglen.com/ComposioHQ/composio/pull/4299) after
independently reproducing the attack path. The report is valid and
useful, but the proposed root export hard-coded a Node module into an
edge-capable package, its happy-path test did not execute the returned
Effect, and it omitted destination protection, response cleanup, and Bun
address pinning.
This PR:
- closes#4284
- makes `mise.toml` the editable source of truth for Bun and pins
`1.4.1-canary.1+d9b769812`, the first Bun build whose compiled macOS
binaries carry strictly valid signatures
- maps that revision to the immutable per-platform `@oven/bun-*` npm
tarballs via mise's `http` backend, so mise extracts the exact
checksum-verified binary with no npm lifecycle, Node dependency, or
postinstall script
- installs Bun through mise in CI and Docker E2E images, removing the
independent `bun-version` input and the `oven-sh/setup-bun` channel
- updates the checksum-verified mise installer to `2026.8.15`, reuses it
in the Docker E2E images, and regenerates `mise.lock` with that release
- teaches the preinstall toolchain check to compare the full Bun
revision (via `Bun.version_with_sha`, in-process) when the pinned
version carries build metadata
- verifies the exact `bun --revision`, a strictly valid Bun-compiled
macOS signature, a Linux container install, the release-workflow
contract, and formatting/linting
EOF -R ComposioHQ/composio
## Summary
- write CLI user data, pending login sessions, and agent identities
through one atomic `0600` helper
- repair `0644` credential files created by older CLI versions before
reading them
- redact credential-shaped structured values from CLI user-context
diagnostics
- redact secret-shaped text at both TypeScript and Python SDK log-output
boundaries, including Pusher `auth` responses and exception tracebacks
- preserve Python logger compatibility: errors remain untruncated,
disabled levels remain lazy, and malformed placeholders cannot expose
arguments
## Local reproduction
Under the normal `022` umask, `next` created a plaintext credential file
with mode `0644`. The pre-fix CLI user-context and TypeScript SDK debug
paths also emitted sentinel credentials. The private atomic writer
changes an existing `0644` target to `0600`, and the upgrade tests now
prove all three legacy credential files are tightened without changing
their contents.
## Verification
- CLI permission upgrade tests: 31 passed across user data, pending
login, and agent identity paths
- CLI source and test typechecks passed
- TypeScript core logging, redaction, and Pusher tests: 17 passed
- TypeScript core source and type-test typechecks passed
- Python logging regression tests: 5 passed
- focused Ruff, Prettier, Oxlint, and `git diff --check` passed
The focused CLI runner needed a temporary local alias for the
pre-existing missing `#ssrf_guard` mapping in the CLI Vitest config. The
alias was removed after verification and is not part of this PR.
## Contributor context
Credit to **Syed Anas Mohiuddin**, independent security researcher, for
reporting the legacy CLI credential-file permission issue.
Supersedes [#4300](https://github.com/ComposioHQ/composio/pull/4300) ·
[Glen review](https://app.tryglen.com/ComposioHQ/composio/pull/4300).
The implementation also covers agent credentials, retains atomic writes,
and applies redaction at the shared SDK logging boundary.
## Summary
Composio's new pricing went live on Aug 15, 2026: **Hobby** ($0) /
**Pro** ($29/mo) / **Enterprise** (custom). This PR updates the public
docs to describe only the current offering and reprices premium tools as
pass-through (provider cost + 5% platform fee).
## Principle
- Docs describe the **current** plans only (Hobby / Pro / Enterprise).
No Starter/Growth tables, no dual documentation.
- Where a legacy note is genuinely useful (rate limits), exactly one
sentence pointing pre-Aug-15 customers to
https://composio.dev/pricing/legacy.
- **Link to https://composio.dev/pricing instead of repeating numbers**,
so future price changes are a one-place edit.
## Files changed
- `docs/content/toolkits/pro-tools.mdx` — replaced the "3x the cost"
line and the Totally Free / Ridiculously Cheap / Serious Business tier
table with pass-through pricing copy: paid third-party providers,
provider price + 5% platform fee (no markup), per-call prices on the
pricing page's "Premium tools" section, Hobby includes up to $2/mo of
premium tool usage, prices depend on provider and can change with
advance notice. Retitled the page from "Pro Tools" to "Premium Tools"
(matches the pricing page and avoids confusion with the new Pro plan).
URL slug `/toolkits/pro-tools` is unchanged so no links break. Rest of
the page (what counts as a premium tool, rate limits) intact.
- `docs/content/reference/rate-limits.mdx` +
`docs/content/reference/v3/rate-limits.mdx` (manual copies, updated
identically) — plan table now Hobby 2,000 req/min · Pro 10,000 req/min ·
Enterprise Custom (kept the doc's existing per-minute unit and "Custom"
wording for Enterprise). No legacy/grandfathering note — docs describe
current plans only; grandfathered customers are served by the dashboard
and composio.dev/pricing/legacy.
- `docs/app/llms.mdx/[[...slug]]/route.ts`,
`docs/components/toolkits/toolkits-landing.tsx` — label text "Pro Tools"
→ "Premium Tools" (link targets unchanged).
## Not changed / notes for reviewers
- `docs/content/docs/common-faq.mdx` no longer exists on `next` (removed
in the sessions-first rewrite, #3637); a grep for self-host / on-prem
across `docs/content` found **no** page advertising self-hosting as an
Enterprise feature, so nothing to remove there.
- Grep sweep (case-insensitive) over `docs/content` for: Starter, Growth
plan/tier, Ridiculously, Serious Business, Totally Free, on-prem,
self-host(ed), $229, $599, 20k tool calls, 200k, per seat, per-seat, 3x
the cost. All customer-facing hits were in the three files above and are
fixed. Left alone:
- `changelog/*` — historical entries (self-hosted Supabase/PostHog
instances, "self-hosted deployments need backend version X"); these
refer to third-party instances or historical SDK notes, not to an
Enterprise plan feature.
- `docs/auth-configuration/custom-auth-configs.mdx:23`,
`docs/authentication/custom-app-vs-managed-app.mdx:30` — "self-hosted"
refers to the *customer's* self-hosted third-party app (e.g. Salesforce
subdomain), unrelated to Composio plans.
- `docs/configuring-sessions.mdx`, `docs/sandbox/remote.mdx` —
"Sandboxes are not billed today" note; not part of this change, flagging
in case sandbox billing status changed with the new pricing.
- `pro-tools.mdx` "Rate limits" table: **fixed in `421789d90`** —
dropped the "Standard Tool Calls" column (100/min · 5,000/min
contradicted `reference/rate-limits.mdx`), kept only the
premium-execution limiter mapped to plan names (Hobby 1,000/hr · Pro
10,000/hr · Enterprise Custom) with a note that it is separate from the
org API limit. Also added Pro's premium allowance bullet.
## Validation
- `bun run lint` (oxlint): passes; only pre-existing warnings in
untouched files.
- `bun run lint:links` (`scripts/validate-links.ts`): 0 errors.
## Related PRs
- landing: https://github.com/ComposioHQ/landing/pull/289
- platform: https://github.com/ComposioHQ/platform/pull/12078
- dashboard: https://github.com/ComposioHQ/dashboard/pull/1326🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01P3JgWs8DcjeQTRKoJd8gmQ
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This PR:
- closes#4323
- removes the unsupported Go SDK setup from the harness integration
example
- removes the dead `ComposioHQ/composio-go` link that breaks the nightly
external-link sweep
- verifies both internal and external docs link validation
## Summary
Automated sync of backend data into the docs site. Triggered by:
`schedule`.
## What changed
- **Toolkit catalog** (`docs/public/data/toolkits.json`,
`toolkits-list.json`) — refreshed list of available toolkits, auth
schemes, and tools from the backend API
- **OpenAPI specs** (`docs/public/openapi.json`,
`docs/public/openapi-v3.json`, `docs/public/openapi-webhooks.json`) —
latest v3.1 and v3.0 API specifications plus the webhook-events spec,
fetched from production
- **API reference pages** (`docs/content/reference/api-reference/`,
`docs/content/reference/v3/api-reference/`) — regenerated index pages
for both API versions
- **Meta tools reference** (`docs/public/data/meta-tools.json`,
`docs/content/toolkits/meta-tools/*.mdx`) — updated meta tool schemas
and reference docs
Co-authored-by: Sushmithamallesh <19796925+Sushmithamallesh@users.noreply.github.com>
## What the vulnerability was
The CLI's TypeScript generator wrote API-supplied tool and trigger slugs
straight into generated `.ts` files at two places where the slug becomes
**code**, not data:
- **object-literal property keys** (`PropertyValue.write`), and
- **type-declaration names** (`TypeDeclaration.write`).
Slugs are unconstrained strings (`ToolAsEnum = Schema.String`, no
character allowlist) and the threat model treats API responses as
untrusted. So a crafted slug could close the key and inject arbitrary
top-level TypeScript, which then executes when the generated SDK is
imported or built. That is remote code execution on the developer or CI
host running `composio ts generate`.
The sibling `Property` builder already got this right: it tests
`isValidJsIdentifier` and falls back to a JSON-encoded computed key.
`PropertyValue` and `TypeDeclaration` simply lacked the same guard.
## Why this change addresses it
- `PropertyValue` now applies the exact guard `Property` uses. A name
that is not a valid identifier becomes a quoted computed key
(`["..."]`), so everything it contains is parsed as a string, never as
an expression.
- `TypeDeclaration` refuses a non-identifier name with a `TypeError`. A
type name has no quoted or computed form, so quoting is not available
and failing loudly is the only safe option. The guard sits after the
existing early return for a string type body, which never writes the
name.
Output is byte-identical for every name that is already a valid
identifier, so no existing generation changes. The CLI codegen snapshots
contain only identifier-shaped slugs
(`GITHUB_ACCEPT_A_REPOSITORY_INVITATION_INPUT`) and no computed keys,
and they are unchanged.
Worth noting on the throw: the two sinks differ in exposure.
`typeDeclaration` receives the full `tool.slug` (for example
`GITHUB_FOO`), which is identifier-shaped in practice. `propertyValue`
receives `stripPrefix(tool.slug)`, which can plausibly start with a
digit for real data and now gets safely quoted rather than throwing. Any
name that would now throw was already producing syntactically invalid
TypeScript, so this converts a confusing downstream compile error into a
clear codegen error.
## Tests
**Exploit tests**, not behaviour-pinning. Nine new tests across the two
files.
The key one builds the generated object literal from a malicious slug,
evaluates it, and asserts the injected IIFE did not run:
```ts
const evaluated = new Function(`return { ${out} };`)() as Record<string, unknown>;
expect(Object.keys(evaluated)).toEqual([malicious]);
expect((globalThis as Record<string, unknown>).__PWNED__).toBeUndefined();
```
Each test was confirmed to fail against the unpatched builders and pass
after the change. On the vulnerable code the generator emits `["k"]: (()
=> { globalThis.__PWNED__ = 1; })(), ["SEND"]: "SLACK_SEND"`, which is
the payload as live code.
Existing behaviour is also pinned: valid identifier keys, well-known
symbols, optional properties, generic parameters, doc comments, and the
string-type-body short circuit.
## What was not verified
- **No end-to-end run of `composio ts generate` against a real or
malicious API response.** The fix and its tests are at the builder
layer. The call sites in
`ts/packages/cli/src/generation/typescript/generate-toolkit-sources.ts`
were read to confirm they pass attacker-controlled slugs into these
builders, but were not exercised.
- **No model-layer slug validation was added.** The finding also
suggests validating slugs against `^[A-Za-z0-9_]+$` at the model layer.
That is deliberately out of scope here: it would change what the CLI
accepts from the API and belongs in a separate, human-reviewed change.
This PR closes the two injection sinks only.
- **The `TypeDeclaration` throw is a new failure mode.** If any live
toolkit ships a slug that is not a valid identifier, codegen will now
fail loudly for it instead of emitting broken TypeScript. No such slug
appears in the repo's fixtures, but the full set of production slugs was
not enumerated.
- Python codegen was not reviewed. This finding and fix are
TypeScript-only.
## Testing performed
Clean-HEAD baseline captured before any edit: full monorepo `pnpm run
test` green, 26/26 turbo tasks, exit 0.
After the change, the same full suite is green, 26/26 tasks, exit 0.
`@composio/ts-builders` goes from 131 to 140 tests, all passing.
`@composio/cli` unchanged at 1304 passed. 20 example packages validated.
No new failures and no snapshot churn.
## Finding
Parameter finding #91 (CWE-94), validated live at
`abc8e038218305cab7f3373b82a37144fa15e630`.
Linear: https://linear.app/composio/issue/SEC-579
parameter-finding: kcy19unq784bp2z34b3299w0
Co-authored-by: Saransh <saranshrana@Saranshs-MacBook-Pro.local>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Summary
- listen for `support-knowledge-updated` repository dispatches and
validate the dispatched commit against upstream `main`
- coalesce delayed or out-of-order events to the newest mainline commit
that changed a `public.md` leaf
- ignore customer-safe-only and repository-maintenance changes without
advancing public snapshot provenance
- serialize refresh runs with latest-wins concurrency so overlapping
events cannot race on the shared proposal branch
- preserve the existing semantic artifact across atomic imports so
unchanged vectors can be reused
- fail dispatched refreshes when `source_commit` is missing, upstream
access is unavailable, or source ancestry cannot be verified
- keep a six-hour reconciliation run as a self-healing backstop for
missed events and docs-side semantic drift
- keep source-resolution logic in a dedicated script tested directly
against temporary Git histories
- update the durable public KB decision record and executable workflow
regression coverage
## Verification
- `bun run test` — 533 tests passed
- `bun run types:check` — passed
- `bun run lint` — passed with pre-existing warnings only
- `bun run lint:links` — 0 errors
- `bash -n scripts/resolve-kb-refresh-source.sh` — passed
- exact isolated import of support-knowledge `3103ed2` — 132 public
guides, 135 generated files, 0 customer-safe leaves, no `sourceCommit`
frontmatter
- semantic reuse check — 850 of 944 vectors reused; 94 embeddings
required
No generated KB corpus files are included in this PR; the first
successful refresh will propose them separately on
`docs/auto-update-kb`.
## Stack
1. **This PR** removes noisy per-page `sourceCommit` frontmatter
2. #4277 makes validated support-knowledge dispatches the primary
refresh path
Merge this PR first. Keep `codex/remove-kb-source-commit-frontmatter`
until #4277 is retargeted to `next`.
## Why
Every support-knowledge repin rewrote all generated KB pages because
each page duplicated the upstream commit in frontmatter. That made small
refreshes look much larger than their actual content changes.
## What changed
- remove per-page `sourceCommit` from generated KB frontmatter and its
schema
- keep the source pin centrally in `docs/kb/manifest.json`
- keep semantic/eval artifact provenance unchanged
- add a regression test proving a commit-only repin produces
byte-identical generated pages
- regenerate 126 guides (each generated diff is only the one-line
removal)
## Verification
- 523 static tests
- typecheck, lint, link validation, and production build
- semantic artifact current: 888 records
## Summary
- Re-exports `AnthropicTool`, `InputSchema`, and `CacheControlEphemeral`
types from `@composio/anthropic` entry point
(`ts/packages/providers/anthropic/src/index.ts`).
- Allows TypeScript users to import schema types directly without
reaching into internal paths.
## Test Plan
- Verified index exports in `@composio/anthropic`.
## 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
## Summary
`composio upgrade` (and any other command with a long spinner message)
scrolls endlessly in terminals narrower than the message, printing the
spinner line hundreds of times instead of animating in place:
```
◐ New version available:
◓ New version available:
◑ New version available:
◒ New version available:
... (hundreds of lines while the binary downloads)
```
Root cause is in `@clack/prompts` (present in the published `1.7.0` and
in the vendored submodule): the spinner's `clearPrevMessage` computes
how many lines to erase by re-wrapping the **raw message**, but each
frame actually renders `${frame} ${message}${dots}` — three prefix
columns plus up to three animated dots that the erase math never sees.
Once those extras push the rendered frame across a wrap boundary, the
erase under-counts by a line and every 80ms tick leaks one. The upgrade
message (`New version available: @composio/cli@x.y.z (current:
@composio/cli@a.b.c). Downloading...`) is ~90 columns, so any narrower
terminal triggers it.
This fixes it downstream in `TerminalUI` by clamping live spinner
messages (start and `message()` updates) to a single terminal row —
truncated with an ellipsis using the same `getColumns` width source
clack wraps with — so the redraw arithmetic cannot diverge. Stop/error
messages are single writes with no redraw loop and stay untouched.
## Changes
- `terminal-ui.ts`: add `clampSpinnerMessage` (newline-collapse + width
clamp, ANSI-bearing messages left alone) and apply it to
`withSpinner`/`useMakeSpinner` start messages and live
`SpinnerHandle.message` updates.
- `terminal-ui.test.ts`: unit tests for the clamp behavior plus a
fake-timer regression test that renders a live frame at 40 columns with
the real upgrade message and asserts it no longer wraps.
## Type of change
- [x] Bug fix
- [ ] New feature
- [ ] Refactor/Chore
- [ ] Documentation
- [ ] Breaking change
## How Has This Been Tested?
- `pnpm --filter @composio/cli test` — 124 files, 1297 passed / 1
skipped, plus `validate:skills` and `validate:boundaries` green.
- `pnpm --filter @composio/cli typecheck` — clean for these files (ran
on node 22 locally, so CI on the pinned toolchain is the authoritative
pass).
- Reproduced the original failure on macOS with the released CLI 0.3.2
upgrading to 0.4.0 in a narrow terminal pane.
## Screenshots (if applicable)
VHS recordings at a 43-column terminal (an every-tick leak width for
this message), driving the real `makeTerminalUI.useMakeSpinner` —
baseline from `next` on the left, this PR on the right:
| Before (`next`) | After (this PR) |
| --- | --- |
| 
| 
|
## Checklist
- [x] I have read the Code of Conduct and this PR adheres to it
- [x] I ran linters/tests locally and they passed
- [x] I updated documentation as needed
- [x] I added tests or explain why not applicable
- [x] I added a changeset if this change affects published packages
(`@composio/cli` is in the changeset ignore list, so none needed)
## Additional context
The same erase under-count exists upstream in `bombshell-dev/clack`
`packages/prompts/src/spinner.ts` (`clearPrevMessage` wraps
`_prevMessage`, the raw message, while the write path wraps the
frame-prefixed output). Worth an upstream issue/PR too, but this keeps
the CLI correct regardless of the pinned clack version. No VHS
recording: internal rendering fix, no documented workflow change.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Keep live spinner updates within both Clack's construction-time width and the current terminal width, so resizing wider cannot reintroduce wrapped frames while narrower terminals remain clamped.
Segment messages by grapheme cluster before measuring display width so keycap and joined emoji cannot be under-counted or split.
## Summary
Automated refresh of the toolkit slugs the CLI knows without asking
the API, generated by
`ts/packages/cli/scripts/generate-toolkit-slugs.ts`.
Toolkits added since the last refresh currently cost users one
toolkit-list fetch (~2 s) the first time they run one of that
toolkit's tools. Merging this makes them free.
The generator refuses to write a list that is short, malformed, or
missing staple toolkits, so a bad fetch opens no PR at all.
## Summary
Automated sync of backend data into the docs site. Triggered by:
`schedule`.
## What changed
- **Toolkit catalog** (`docs/public/data/toolkits.json`,
`toolkits-list.json`) — refreshed list of available toolkits, auth
schemes, and tools from the backend API
- **OpenAPI specs** (`docs/public/openapi.json`,
`docs/public/openapi-v3.json`, `docs/public/openapi-webhooks.json`) —
latest v3.1 and v3.0 API specifications plus the webhook-events spec,
fetched from production
- **API reference pages** (`docs/content/reference/api-reference/`,
`docs/content/reference/v3/api-reference/`) — regenerated index pages
for both API versions
- **Meta tools reference** (`docs/public/data/meta-tools.json`,
`docs/content/toolkits/meta-tools/*.mdx`) — updated meta tool schemas
and reference docs
Co-authored-by: Sushmithamallesh <19796925+Sushmithamallesh@users.noreply.github.com>
Both spinner entry points constructed a clack spinner on stderr and
started it with a clamped message, and useMakeSpinner built a throwaway
lambda purely to relay that same clamp into createClackSpinnerHandle.
Give makeTerminalUI a startSpinner helper that both entry points call,
and let createClackSpinnerHandle take the output stream it clamps
against instead of a pre-bound closure. Behaviour is unchanged.
Claude-Session: https://claude.ai/code/session_0191yFPnWgJnzPPnCYrVm6X7
One-line `ENGINE_REF` bump for the docs-agent-eval shim: the pin
predates the judge calibration (docs-agent-eval-ci PRs #4–#7 —
evidence-scoped scans, proxy-log ground truth, infra-vs-agent error
classification, corrected package taxonomy, renamed secret). Until this
merges, label/deployment-triggered evals run the old
false-positive-prone judge; dispatched runs already use current main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Soumya Medapati <soumyamedapati@mac.local.meter>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Follow-ups from review of the narrow-terminal spinner fix.
- Budget and truncate by display columns instead of UTF-16 code units.
Clack wraps the rendered frame by width, so a CJK message sat under a
`.length` budget and wrapped anyway, reinstating the leak. Walking whole
code points also keeps the cut off a surrogate pair.
- Drop MIN_SPINNER_MESSAGE_COLUMNS. The 8-column floor exceeded the row
below ~15 columns and restored the scroll it was meant to prevent. The
budget now degrades to the ellipsis, so the one-row invariant holds
wherever it can hold at all.
- Cover the paths production actually drives: live `message()` updates and
`withSpinner`'s start message. Reverting either clamp previously left the
whole suite green.
- Advance fake timers past clack's dot animation so the three dot columns
SPINNER_RENDER_OVERHEAD reserves are exercised, and run these cases
through `it.live` per the package's Effect test convention.
- Add the CHANGELOG entry this user-facing fix needs; `@composio/cli` is
changeset-ignored, so release notes go there directly.
Claude-Session: https://claude.ai/code/session_0191yFPnWgJnzPPnCYrVm6X7