Replace direct Node filesystem and path access throughout the touched installer paths and tests. Preserve cleanup and replacement guarantees with focused lifecycle coverage.
Adds a --no-browser flag to `composio link` (and `composio dev
connected-accounts link`) that prints the authorization URL instead of
auto-opening the browser, while still waiting for the connection to
become ACTIVE. Also respected on the unmanaged-auth dashboard fallback.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
```markdown
The `homepage` field in every published TypeScript package points at
`https://github.com/ComposioHQ/composio/tree/main/ts/packages/<pkg>#readme`. The
repository's default branch is `next` and there is no `main` branch, and GitHub
does not redirect `tree/<branch>` paths to the default branch when that branch
does not exist. As a result each of these homepage links returns a 404 wherever
it is surfaced (the package page on npmjs.com, editor/IntelliSense hovers, and
`npm view <pkg> homepage`).
This repoints each `homepage` to `tree/next/...` so the links resolve. The
`repository.url` (plain `.git` URL) and `bugs.url` fields were already correct
and are left untouched.
The metadata was originally added in #3611; this only corrects the branch in the
`homepage` URLs.
Fixes #
## Changes
- Update the `homepage` field from `tree/main/...` to `tree/next/...` in the 14
published packages that carry one: `@composio/core`, `@composio/slim`,
`@composio/experimental`, `@composio/json-schema-to-zod`, and the providers
`@composio/anthropic`, `@composio/claude-agent-sdk`, `@composio/cloudflare`,
`@composio/google`, `@composio/langchain`, `@composio/llamaindex`,
`@composio/mastra`, `@composio/openai`, `@composio/openai-agents`,
`@composio/vercel`.
- Add a Changeset (patch) covering those 14 published packages.
## Type of change
- [x] Bug fix
- [ ] New feature
- [ ] Refactor/Chore
- [ ] Documentation
- [ ] Breaking change
## How Has This Been Tested?
Metadata-only change, so there is no unit test to add. Verified with:
- Confirmed the target branch: `git ls-remote --symref origin HEAD` reports
`refs/heads/next`, and `git ls-remote --heads origin main` is empty (no `main`).
- Confirmed the links resolve: `https://github.com/ComposioHQ/composio/tree/main/ts/packages/core`
returns HTTP 404 while `.../tree/next/ts/packages/core` returns HTTP 200
(same for the provider packages).
- All 14 edited `package.json` files still parse as valid JSON.
- `pnpm exec prettier --check` on the 14 `package.json` files and the changeset:
all pass.
- `pnpm changeset status --since=origin/next` lists exactly these 14 packages
bumping at `patch` (no missing or extra packages), so the changeset CI check is
satisfied.
Environment: node 26, pnpm 10.28.0.
## Screenshots (if applicable)
## Checklist
- [x] I have read the Code of Conduct and this PR adheres to it
- [x] I ran linters/tests locally and they passed
- [ ] I updated documentation as needed
- [x] I added tests or explain why not applicable
- [x] I added a changeset if this change affects published packages
## Additional context
The `homepage` URLs pin an explicit branch (`tree/<branch>`), so they will break
again if the default branch is ever renamed. If you would prefer a
branch-agnostic form (for example dropping the `tree/<branch>` segment so the URL
tracks the default branch, or linking to the published README), I am happy to
switch to that instead; I went with the smallest correct fix of pointing at the
actual default branch.
```
Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
## 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 & why
Two v1 non-negotiable gates from the [SDK road to
v1](https://github.com/ComposioHQ/composio) plan are currently red. Both
are self-contained and closed here.
### ① SSRF guard on URL file inputs — `@composio/core`
`composio.files.upload(url)` and automatic file upload during tool
execution did a **raw `fetch()`** on user-supplied URLs
(`readFileContentFromURL`, `fileUtils.node.ts`) with no guard. Because
URL inputs deliberately bypass the local-path allowlist, a caller — or
an LLM-produced tool argument — could point the SDK at internal
infrastructure (`http://169.254.169.254/…` cloud metadata, RFC1918
hosts) or a public URL that 3xx-redirects into internal space.
New `ssrfGuard.node.ts`:
- resolves the host and refuses **private, loopback, link-local (incl.
`169.254.169.254`), CGNAT, TEST-NET, benchmarking, multicast, and
reserved** ranges (IPv4 + IPv6, incl. IPv4-mapped / NAT64);
- validates the **resolved address**, not the hostname string, so
decimal/octal/hex IP obfuscation doesn't slip through;
- rejects non-`http(s)` schemes;
- follows redirects **manually** so every hop is re-validated;
- blocked requests throw the new `ComposioBlockedInternalUrlError`.
Behaviour for legitimate public URLs is unchanged. Node-only path. Known
residual (documented in-file): a DNS-rebinding TOCTOU window between our
lookup and `fetch`'s connect — closing it fully needs connect-time IP
pinning (a custom dispatcher), out of scope here.
### ② Telemetry secret redaction — `@composio/core`
Error telemetry shipped `error.message` / `error.stack` **verbatim** to
`telemetry.composio.dev` with zero redaction. New `redact.ts` runs at
the single chokepoint (`prepareAndSendErrorTelemetry`) and strips URL
query strings, `Authorization` bearer/basic credentials, and secret-like
`key=value` pairs (API keys, tokens, client secrets, passwords) before
transport.
## Tests
- `ssrfGuard.test.ts` — IP classification (v4/v6, mapped, CGNAT,
metadata), scheme rejection, resolve-to-internal, "any resolved address
internal" pinning bypass, redirect-into-internal, redirect budget.
- `redact.test.ts` — URL query, bearer/basic, key/value secrets, quote
preservation, benign text untouched.
- Updated `fileUtils.test.ts` to mock DNS (hermetic) and expect
`redirect: 'manual'`.
- `tsc --noEmit` clean; lint clean.
## Notes
- `patch` changeset included.
- Adds `ComposioBlockedInternalUrlError` to the public error exports.
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
</details>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: jkomyno <alberto@composio.dev>
## What
Two changes to the docs install experience:
**1. Package-manager picker on install code blocks.** New
`PackageInstall` client component
(`docs/components/package-install.tsx`): the code block's copy button
opens a dropdown of package managers. Selecting one rewrites the
displayed command and copies it to the clipboard.
- TypeScript blocks: npm (default, `npm install`) / pnpm / bun / yarn
(`<pm> add`)
- Python blocks: uv (default, `uv add`) / pip (`pip install`)
- The menu is portaled to `document.body` so it isn't clipped by the
`Tabs`/`CodeBlock` overflow containers, and closes on outside click,
Escape, or real scroll movement.
**2. `@composio/slim` callout in the quickstart.** The quickstart
TypeScript install blocks carry display-only `#` comment lines noting
that `@composio/core` ships its docs and TypeScript source in the
package (inspectable to coding agents) and that `@composio/slim` is the
smaller install with the same API. Comment lines are rendered muted and
are **never copied** — the picker copies only the command.
## Where
Converted every plain `npm install` / `pip install` code block under
`docs/content` (quickstart, all provider pages, single-toolkit MCP,
custom tools, migration guides, standup example, SDK reference indexes).
The TS reference index's manual npm/pnpm/yarn/bun tabs collapse into one
picker. The two SDK-reference generators
(`ts/packages/core/scripts/generate-docs.ts`,
`python/scripts/generate-docs.py`) emit the new component so
regeneration keeps it. Flagged upgrade commands (`pip install
-U/--upgrade`) and the bun-first examples were left as-is.
## Verification
- `bun run types:check` — pass
- `bun run build` — pass
- `eslint` on touched TS files — clean (remaining repo lint errors are
pre-existing on `next`)
- Visual check via dev server + browser: comment lines render muted
below the command, picker opens npm/pnpm/bun/yarn (uv/pip on Python
tabs), selecting a manager rewrites the command, and the clipboard
receives exactly the command with no comment lines.
Docs-only behavior change; no changeset (generator-script edits don't
touch published code).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Summary
Rewrites the stale root `README.md` against the current docs (structure
modeled on [earendil-works/pi](https://github.com/earendil-works/pi)'s
README).
- **Quickstarts** now use the session-based API (`composio.create()` →
`session.tools()`) that the docs treat as canonical, replacing the
legacy `composio.tools.get()` flow
- **Positioning** matches the docs welcome page: 1000+ pre-authenticated
toolkits, per-user sessions, auth, triggers, and a sandbox
- **New sections**: CLI install (curl / Homebrew / npm),
sessions-via-MCP note, repository layout, minimum runtime versions
- **Provider table** merged with package links; adds the Claude Agent
SDK providers (both SDKs) and the experimental Pi provider
(`@composio/experimental`); documents the Python
`composio-gemini`/`composio-google`/`composio-google-adk` split
- **Removed**: Rube section (no longer in docs), cover banner (replaced
with theme-adaptive brand logomark from brand.composio.dev),
master-branch pointer, internal OpenAPI-pull section, private
`@composio/ts-builders` listing
Docs-only change — no changeset needed.
## Preview
Rendered view: [README.md on this
branch](https://github.com/ComposioHQ/composio/blob/docs/refresh-root-readme/README.md)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This PR:
- supersedes https://github.com/ComposioHQ/composio/pull/3642 and
credits @shamsharoon for the original Eve provider and iMessage work
- rebases the integration onto the current `next` branch and resolves
its lockfile conflicts
- publishes an isolated `@composio/experimental/eve` entry with the
correct Eve 0.12+ peer contract
- adds recoverable per-session discovery, native Eve context
propagation, and durable approval policies for direct and batched side
effects
- centralizes circular-safe auth-link extraction across Eve and Pi and
replaces dense conditional expressions with named control flow
- hardens the iMessage example against `osascript` option injection,
unsafe trigger scoping, and third-party prompt injection
- preserves completed local-tool results when a mixed local/remote batch
loses its remote transport
- consolidates the example documentation onto shared components,
documents direct model-provider credentials, and clarifies that the
browsable source is not yet a standalone fixture
- verifies the change with 999 core tests, 32 experimental tests,
package builds/typechecks, packed peer-isolation smokes, and a
production docs build
---------
Co-authored-by: shams haroon <144290365+shamsharoon@users.noreply.github.com>
Co-authored-by: shams haroon <shamsharoon7@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: shams haroon <shams@composio.dev>
This PR:
- builds on top of https://github.com/ComposioHQ/composio/pull/3790
- makes the OpenAI example executable in direct-tools, Tool Router,
smoke, and Cloudflare Workers modes
- keeps direct and Tool Router agents in separate files so each
execution model is explicit and readable
- requires a successful Composio tool execution before either agent can
accept a final model response
- adds OpenAI to the nightly staging matrix without weakening the
existing Mastra coverage
- updates two stale experimental OpenAI usages so the complete example
package typechecks against current SDK APIs
## Testing
- `pnpm --filter openai-example run typecheck`
- `pnpm --filter openai-example run lint` (0 errors; 4 pre-existing
warnings)
- `pnpm --filter openai-example run smoke` (staging: 5 direct tools and
6 Tool Router tools)
- `pnpm --filter openai-example run cf:dry-run`
- `pnpm run typecheck`
- `./node_modules/.bin/eslint ts/packages --ext .ts,.tsx`
- `pnpm run typecheck:examples`
- `pnpm run lint:examples`
- `pnpm exec turbo cf:dry-run --filter='./ts/examples/*'`
- [branch
nightly](https://github.com/ComposioHQ/composio/actions/runs/29079263571):
OpenAI and Mastra both passed smoke, direct-tools, and Tool Router
against staging
## Post-deploy validation
- dispatch `ts.examples-nightly.yml` on `next` after merge and confirm
both matrix legs stay green
- healthy runs finish within the 20-minute job budget and log successful
tool-backed responses for both agent modes
- revert this PR if the OpenAI leg blocks the nightly; examples
maintainers own the first post-merge run and the next 06:00 UTC schedule
---
[](https://github.com/EveryInc/compound-engineering-plugin)

## 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:
- makes example typecheck, lint, and Workers dry-runs discover packages
by script presence instead of a Mastra-only filter
- runs the nightly staging suite as a provider matrix, starting with
Mastra and failing when a matrix package is missing
- keeps direct and Tool Router agents explicit while sharing the Tool
Router agent between Node.js and Cloudflare Workers
- keeps root ESLint rules in Turbo's lint cache key and documents both
Worker secrets
## Testing
- `pnpm run typecheck`
- `pnpm run typecheck:examples`
- `./node_modules/.bin/eslint ts/packages --ext .ts,.tsx` (0 errors; 72
existing warnings)
- `pnpm run lint:examples`
- `pnpm exec turbo cf:dry-run --filter='./ts/examples/*'`
- confirmed Turbo includes `eslint.config.mjs` in the Mastra lint task
inputs
- confirmed a missing provider package fails instead of silently passing
the nightly smoke step
## Post-Deploy Monitoring & Validation
- after merge, dispatch `ts.examples-nightly.yml` on `next` and confirm
the `mastra examples against staging` matrix leg runs smoke,
direct-tools, and Tool Router successfully
- healthy signal: smoke reports wrapped tools and both agent steps
return non-empty output
- failure signal: workflow logs contain `SMOKE FAIL`, `Agent returned
empty output`, `No projects matched`, or a secret preflight error;
revert this PR if the foundation gate begins false-passing or blocking
unrelated example work
- validation window and owner: the first post-merge dispatch plus the
next scheduled 06:00 UTC nightly, owned by the TypeScript examples
maintainers
---
[](https://github.com/EveryInc/compound-engineering-plugin)

## What
Add `stopWhen: stepCountIs(10)` to both Mastra example agent entries
(`src/index.ts`, `src/tool-router.ts`).
## Why
The first nightly `workflow_dispatch` run of `ts.examples-nightly`
(after #3788 merged) failed on the **tool-router** example with `Agent
returned empty output`. `agent.generate(...)` had no step budget, so the
multi-step Tool Router flow (router `search` + `multi-execute`
meta-tools, then summarize) stopped after the first tool call and
produced no final text. The non-empty-output assertion added in #3788
turned that silent no-op into a loud failure — working as intended.
Every other tool-router example/e2e in the repo already sets a step
budget; this matches the Mastra tool-router e2e reference
(`ts/e2e-tests/runtimes/node/mastra-tool-router-zod-v4/e2e.test.ts` →
`stopWhen: stepCountIs(10)`).
## Verification
- `pnpm run lint:examples` and `pnpm run typecheck:examples` green.
- Nightly re-dispatch to confirm the tool-router example now returns
non-empty output against staging (follow-up once merged).
## Notes
- No changeset — example package is private/unpublished.
- `index.ts` passed without the budget but gets the same
`stepCountIs(10)` for robustness/consistency.
## What
Turns the **Mastra** example into an executable reference that doubles
as a test, and wires the examples into CI for the first time. First
slice of the v1 "executable examples" workstream — the pattern here is
the template for the other TS providers, then Python.
## Why
Examples under `ts/examples/*` were unverified: the only CI tie-in is
`validate-examples.ts`, which is transpile-only (no cross-file types, no
lint, no execution) and passes on exactly the schema-conversion
regressions we keep hitting in the Mastra provider — all closed but
unprotected:
- https://github.com/ComposioHQ/composio/issues/2109 (zod→json
serialization crash)
- https://github.com/ComposioHQ/composio/issues/3307 (`$defs` dropped
while `$ref` kept)
- https://github.com/mastra-ai/mastra/issues/13909 (already-JSON-Schema
tools crash serialization)
## Changes
**Canonical Mastra example** (`ts/examples/mastra`)
- `src/index.ts` — direct tools on the unauthenticated `HACKERNEWS`
toolkit
- `src/tool-router.ts` — v1-canonical `composio.sessions.create(...)` →
`session.tools()` (drops the deprecated bare `composio.create` alias)
- `src/cloudflare.ts` — the same wrapping path on the Cloudflare Workers
runtime (`export default { fetch }`)
- `src/smoke.ts` — deterministic wrapping + tool-router assertions
(needs only `COMPOSIO_API_KEY`)
- Removed the old Gmail/MCP entries that used deprecated APIs and
required auth
- Runnable via `bun ts/examples/mastra/src/{index,tool-router}.ts`
**Real typecheck + lint** (examples previously escaped both)
- Example now has a `typecheck` script (Node + Worker tsconfigs);
`typecheck:examples` / `lint:examples` scripts added
- eslint now covers `ts/examples/**` with type-safety rules kept
(`no-explicit-any`) and console noise allowed
**Two-tier CI**
- `ts.examples.yml` — per-PR **blocking**, no secrets: build → typecheck
→ lint → `wrangler deploy --dry-run`. This is the tier that catches the
schema-conversion regressions, since a dropped `$defs`/`$ref` surfaces
as a **type** error.
- `ts.examples-nightly.yml` — **non-blocking**, scheduled: executes
against the **staging** backend with secrets; runs the deterministic
smoke always and the full agent examples when an LLM key is set.
## Verification
Locally against staging: `smoke` passes (5 direct + 6 tool-router
tools), `typecheck:examples` and `lint:examples` are green, the Worker
`wrangler deploy --dry-run` bundles, and seeded regressions (a type
error / an `any`) correctly fail their gate.
## Notes
- No changeset — example packages are private/unpublished.
- Follow-ups (separate PRs): template the pattern to the other TS
providers, then port to `python/examples/*`.
This PR:
- replaces `decompress` (unmaintained; the zip-slip fix in the GHSA
advisory was never published to npm past 4.2.1) with `extract-zip`
across the CLI's four zip-extraction call sites: `install-skill.ts`,
`run-companion-modules.ts`, `upgrade-binary.ts`, and
`cli-local-tools/bundled-binaries.ts`
- `extract-zip` validates each entry resolves inside the target
directory before writing, closing the path-traversal issue `pnpm audit`
flags as critical
- replaces the one `.tgz`-extracting call site
(`scripts/_acp-adapters.ts`, used to vendor ACP adapter binaries at
build time) with `tar`, since `extract-zip` only handles zip archives
- removes `@types/decompress`; both `extract-zip` and `tar` ship their
own type definitions
- `pnpm audit --prod --audit-level=high` now passes (previously failing
on `next`, unrelated to #3766)
Verified `extract-zip` and `tar` extract nested directories correctly
with a local smoke test (real `.zip`/`.tgz` archives), in addition to
the existing `@composio/cli` (724 tests) and `@composio/cli-local-tools`
(13 tests) suites passing.
This PR:
- fixes a `startsWith('http')` check in the TypeScript SDK that
misclassified local paths such as `http_export/report.csv` or
`http_backup/.ssh/id_rsa` as URLs
- the misclassification routed those paths through the URL-fetch branch,
which skips both the upload allowlist (`assertPathInsideUploadDirs`) and
the sensitive-path denylist (`assertSafeFileUploadPath`, added in #3763
for GHSA-hp3h-89pf-5q58)
- replaces the naive prefix check with `isHttpUrl()`, a `new
URL()`-based check in `fileUtils.node.ts`, matching the Python SDK's
existing `_is_url()` behavior (`urlparse` + scheme check), which was not
affected by this bug
- updates the three call sites that used the naive check:
`fileUtils.node.ts` (`readFile`, `getFileDataAfterUploadingToS3`) and
`FileToolModifier.node.ts` (`beforeFileUpload` source classification)
- adds a regression test asserting a path like `http_export/.aws/creds`
is still blocked by the sensitive-path denylist rather than routed to
`fetch`
The captured-errors pretty-printer in the CLI has two files that export
the same
`maybeAdviseSpansUsage` symbol:
-
`ts/packages/cli/src/effect-errors/pretty-print/captured-errors/maybe-advise-spans-usage.ts`
(the live one, spelled `advise`), re-exported by
`captured-errors/index.ts` and
consumed by `format-captured-error.ts`.
-
`ts/packages/cli/src/effect-errors/pretty-print/captured-errors/maybe-advice-spans-usage.ts`
(spelled `advice`), which is never exported through the barrel and never
imported
anywhere.
The `advice` file is a leftover from an `advice -> advise` rename: both
files were
originally introduced identical, and only the `advise` copy was later
updated (its
leading `''` became `' '` for color-contrast spacing) and wired up. The
`advice`
copy has since sat as unreferenced dead code that still returns the
stale
pre-fix value. It ships in the bundle as dead code and is a copy-paste /
confusion
hazard. This PR deletes it.
No linked issue (self-identified cleanup).
Fixes #
## Changes
- Delete the unused, stale duplicate
`ts/packages/cli/src/effect-errors/pretty-print/captured-errors/maybe-advice-spans-usage.ts`.
The live `maybe-advise-spans-usage.ts` is unchanged and remains the only
exported/imported copy.
## Type of change
- [ ] Bug fix
- [ ] New feature
- [x] Refactor/Chore
- [ ] Documentation
- [ ] Breaking change
## How Has This Been Tested?
Confirmed the removed file is unreferenced, then ran the CLI's checks
with the
deletion in place (local Node 26 / pnpm 10; CI will verify the pinned
toolchain):
- Unreferenced check: searched the repo for both the path
`maybe-advice-spans-usage`
and the token `advice` under `ts/packages/cli/src/` -> 0 matches. Only
the live
`./maybe-advise-spans-usage` is re-exported by
`captured-errors/index.ts`, and
`format-captured-error.ts` imports `maybeAdviseSpansUsage` through that
barrel.
- Build workspace deps: `pnpm run build:packages` -> all packages built.
- Typecheck (required when touching CLI code, per
`ts/packages/cli/AGENTS.md`):
`pnpm exec turbo typecheck --filter=@composio/cli --force` -> pass,
fully
re-executed (no cache). A missing-module error would surface here if
anything had
imported the deleted file.
- Lint: `pnpm exec eslint ts/packages/cli/src/effect-errors --ext .ts`
-> clean.
- Tests: `pnpm exec vitest run` in `ts/packages/cli` -> 720 passed, 1
skipped,
80 files. No regressions.
## Screenshots (if applicable)
N/A.
## Checklist
- [x] I have read the Code of Conduct and this PR adheres to it
- [x] I ran linters/tests locally and they passed
- [x] I updated documentation as needed (none needed; internal dead
code)
- [x] I added tests or explain why not applicable
- [x] I added a changeset if this change affects published packages
Tests: not applicable. This is a pure deletion of unreachable code with
no
behavior change; nothing references the file, so there is nothing to
regression-test. The live `maybeAdviseSpansUsage` path is already
exercised by the
existing effect-errors tests via `format-captured-error`.
Changeset: not needed. The CLI package is not published to npm and this
is an
internal, non-published-surface change.
## Additional context
Git history for the curious: both files were introduced identical in
`333b03c3b`;
the live `maybe-advise-spans-usage.ts` diverged in `fe5acc7d7` (leading
`''` -> `' '`
for color contrast) and became the wired-up copy, leaving
`maybe-advice-spans-usage.ts`
behind as the stale duplicate removed here.
Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
This PR:
- fixes#3746 / GHSA-hp3h-89pf-5q58 — the CLI's tool file-upload path
bypassed the sensitive-file denylist
- **root cause:** the denylist was enforced at the *caller* layer, so
`@composio/cli`'s duplicate upload path (`readFileFromDisk`) read and
uploaded any local path a tool argument pointed at — `~/.ssh/id_rsa`,
`~/.aws/credentials`, `.env`, etc. — enabling credential exfiltration
via `composio execute` / `composio run` (incl. LLM-driven agents hit by
prompt injection)
- **unified fix:** one canonical guard, exported from `@composio/core`
and enforced at the read primitive in every SDK
- `core`: the guard now routes fs/path access through the internal
`#platform` abstraction (adds a `realpathSync` method), so it carries
**no static `node:*` imports** and is exported from the package root:
`assertSafeFileUploadPath`, `isBlockedSensitiveFileUploadPath`,
`BUILTIN_FILE_UPLOAD_PATH_DENY_SEGMENTS`
- `cli`: `readFileFromDisk` calls the shared `assertSafeFileUploadPath`
before `fs.readFile`; URLs and `File` objects are unaffected (matches
core)
- `python`: already routes all uploads through the guarded
`FileUploadable.from_path` — no functional change; adds regression tests
for parity
- supersedes #3755, which exported the then-node-only guard from core's
**main** `index.ts` and would have injected `node:fs` into the shared
`dist/index.mjs` (the workerd/edge-light entry), likely breaking
Cloudflare Workers / Vercel Edge
- changesets: `@composio/core` minor (new public exports),
`@composio/cli` patch (security fix)
## Verification
- core: full suite passes (997), incl. new root-export +
realpath-symlink cases
- **edge bundle stays clean**: built `dist/index.mjs` has no static
`node:*` imports and reaches the platform only via `#platform`
- cli: new regression test drives the real `uploadToolInputFiles →
readFileFromDisk → guard` chain — `~/.ssh/id_rsa` and `.env` throw
`ComposioSensitiveFilePathBlockedError` before any `createPresignedURL`
call; a normal file still uploads
- python: `test_files.py` + sensitive-path suite pass (145), incl. 3 new
`from_path` guard tests
- `tsgo` typecheck + eslint clean for core and cli
## Summary
Toolkit version maps are keyed by **normalized (lowercase) slugs** on
the *write* side — env vars (`COMPOSIO_TOOLKIT_VERSION_*`) and
user-supplied dicts both get their keys lowercased when the map is
built. But the *lookup* read the map with the **raw slug**. A pin
configured under a different casing (e.g. `{ "GitHub": "20250101_00" }`
or `COMPOSIO_TOOLKIT_VERSION_GITHUB` looked up as `GitHub`) therefore
missed the map and silently fell back to `"latest"`, which
`Tools.execute` then rejects with a version-required error — discarding
the user's explicit pin.
Rather than patch only the read side, this **centralizes the
normalization rule** into a single helper per SDK and routes **both**
the map-building (write) and lookup (read) paths through it, so the two
sides can never drift apart again:
- **Python** — `normalize_toolkit_slug()` in
`python/composio/utils/toolkit_version.py`
- **TypeScript** — `normalizeToolkitSlug()` in
`ts/packages/core/src/utils/toolkitVersion.ts`, imported by
`getToolkitVersionsFromEnv` in `sdk.ts`
Behavior is now identical across the two SDKs. This **supersedes
#3759**, which patched only the Python read side and left TS diverging.
## Backend verification
Confirmed against the backend (staging, **1403 toolkits across 15
pages**): every toolkit `slug` is already lowercase (the mixed-case
`name` field is display-only). So the *primary* execute path — which
resolves via `tool.toolkit.slug` — was never broken in practice. This
change:
- **hardens** the direct-util and mixed-case-config paths (a user
passing `{ "GitHub": "…" }` now resolves as expected), and
- **removes a latent cross-SDK divergence**: the TS suite previously
asserted `getToolkitVersion` was *case-sensitive* (`versions.test.ts`),
locking in the bug for an input the production code path can't actually
produce. That test is flipped to assert case-insensitive resolution.
## Tests
- **Python** (`python/tests/test_toolkit_version.py`): case-insensitive
lookup via `get_toolkit_versions` round-trip, and against a raw
pre-normalized map. `18 passed`.
- **TypeScript** (`ts/packages/core/test/core/versions.test.ts`):
replaced the case-sensitivity assertion with case-insensitive resolution
+ a write→read round-trip of a mixed-case user pin. Full core suite `997
passed`.
- Typecheck (`tsc --noEmit`) clean; `ruff` clean.
## Notes
- Changeset added: `@composio/core` patch.
- No public API change; the fix is behavioral (case-insensitive) plus an
internal shared helper.
This PR:
- consolidates https://github.com/ComposioHQ/composio/pull/3733,
https://github.com/ComposioHQ/composio/pull/3735 through
https://github.com/ComposioHQ/composio/pull/3744, and
https://github.com/ComposioHQ/composio/pull/3752
- refreshes the pnpm workspace dependencies under the existing
`minimumReleaseAge` supply-chain gate
- keeps latest compatible pins for ESLint 9, AI SDK 6, and Cloudflare
workers types 4 where latest majors conflict with the current workspace
- keeps zod-v3 runtime fixtures on `zod@3.25.76` while bumping the
workspace catalog to zod 4
- bumps Python `composio-client` to `1.42.0` and refreshes `uv.lock`
- updates the SHA-pinned Claude and Codex workflow actions from the
Dependabot action group
- adds a patch changeset for the versioned package manifests touched by
dependency updates
- verifies the rollup with frozen pnpm/uv locks, peer checks, audit
threshold checks, typecheck, lint, builds, TypeScript tests, and Python
`nox -s chk`
## Context
`pnpm audit --prod --audit-level=moderate` passes. The remaining
production audit item is a low `@ai-sdk/provider-utils` advisory through
Mastra transitive dependencies; forcing it higher would require leaving
the compatible stable Mastra dependency path.