Commit Graph

1026 Commits

Author SHA1 Message Date
sdkrelease[bot] fdbd44f9eb Release: update version (#3906)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to next, this PR will
be updated.


# Releases
## @composio/core@0.14.1

### Patch Changes

- 577a3d4: Replace the backtracking leading/trailing-slash-trim regexes
in the Cloudflare Workers/Edge platform path helpers with index-walk
loops, closing a polynomial-time regular expression denial-of-service
(CodeQL js/polynomial-redos) on long runs of slash characters. Output is
unchanged for every input.
- 503b50a: Refresh runtime dependencies across the TypeScript SDK
packages.
- 2f63fe5: Guard Tool Router session URL uploads against SSRF,
revalidate redirect targets, and enforce a streamed 100 MiB response
limit across TypeScript URL upload paths.
## @composio/experimental@0.2.1

### Patch Changes

- 503b50a: Refresh runtime dependencies across the TypeScript SDK
packages.
## @composio/mastra@0.10.3

### Patch Changes

- 503b50a: Refresh runtime dependencies across the TypeScript SDK
packages.
## @composio/slim@0.14.1

### Patch Changes

- 503b50a: Refresh runtime dependencies across the TypeScript SDK
packages.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-30 16:25:33 +05:30
Kshitij Jhunjhunwala fc9745040a feat(cli): make headless composio login agent-friendly (PRDE-1138) (#3945)
## What

Closes the PRDE-1138 gap: an unattended agent hitting plain `composio
login` in a pipe dead-ended at browser OAuth. Now the default headless
path either completes unattended or tells the agent exactly what to run
— without ever auto-creating an account for a human.

- **Offer**: non-interactive login instructions gain a second hint —
"For unattended agents: run `composio login --agent`" — alongside the
existing URL + `composio login --poll` flow.
- **Perform (reuse only)**: when `~/.composio/agent.json` already holds
a READY agent identity, plain headless `composio login` completes
unattended by reusing it. New `getStoredReadyAgent` in
`services/agents.ts` is the reuse-only counterpart of
`getOrSignupReadyAgent` — it never falls back to signup.
- **Guardrail** (the design constraint from the ticket): account
creation stays exclusively behind the explicit `composio login --agent`
/ `composio signup` commands. A human piping `composio login` with no
stored agent identity gets the same URL instructions as before and no
account. Asserted at both unit and E2E level (`/api/signup` is never
called on the default path).
- `install.sh --agent` login now sets
`COMPOSIO_CLI_INVOCATION_ORIGIN=installer` for analytics attribution.
- Root `.dockerignore`: host `node_modules` (pnpm store symlinks that
break outside the host) no longer enter the e2e image build context;
without this, e2e images cannot build from a checkout that has run `pnpm
install`.

## Verification

- `pnpm typecheck` green; `@composio/cli` suite green — 1001 passed, 1
skipped; `validate:boundaries` clean.
- New Docker E2E suite `ts/e2e-tests/cli/agent-signin` (10/10) against a
mock `agents.composio.dev` (`COMPOSIO_AGENTS_BASE_URL`): unattended
nothing→authenticated (`login --agent` then `whoami` in one container),
headless-human guardrail (instructions printed, zero signup calls),
stored-identity reuse (login completes + whoami reports `account_type:
agent`). No real accounts are created.

## Notes

- No VHS recordings: the visible change is piped-mode text output, which
VHS interactive recordings don't demonstrate.
- No changeset by design (`@composio/cli` is Changesets-ignored);
human-facing note added to `ts/packages/cli/CHANGELOG.md` under 0.3.0.
2026-07-28 13:48:28 -07:00
Kshitij Jhunjhunwala 0c6b09df19 fix(cli): never reuse a stored agent identity the agents API rejected
getStoredReadyAgent collapsed every whoami failure into Option.none via
Effect.option and fell back to the on-disk identity, so a revoked agent
key (HTTP 401/403) still completed headless login. Auth rejections now
return no agent; only transport-shaped failures keep the offline
fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 13:33:28 -07:00
Kshitij Jhunjhunwala 14011b9057 fix(e2e): scope node_modules dockerignore to the CLI image only
A root .dockerignore excluding **/node_modules broke Dockerfile.node's
runtime fixtures: suites without a setup phase (claude-agent-sdk) resolve
deps through node_modules copied in via COPY ts/e2e-tests/. BuildKit's
per-Dockerfile ignore keeps the exclusion for Dockerfile.cli, which
installs inside the image and must not see host pnpm symlinks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 13:21:49 -07:00
jkomyno 6725d1c078 Merge branch 'next' into kj/decommission-npm-brew 2026-07-28 22:05:06 +05:30
Alberto Schiabel 27706a5081 fix(install): stop swallowing the CLI's PATH guidance (#3951)
This PR:

- follows up https://github.com/ComposioHQ/composio/pull/3943, replacing
its hardcoded message with a fix for the cause it worked around
- stops `install.sh` redirecting `composio install`'s stderr to a temp
file — that redirect is what silenced the command, since the CLI gates
every log, note and outro on stderr being a terminal, so the captured
file was always empty and the replay printed nothing
- probes `"$exe" --version` before delegating, so a binary that cannot
run still falls back to inline setup without spilling loader errors —
the reason the redirect existed in the first place
- makes `composio install` fall back to plain `ui.error` lines when
there is nothing to decorate, so container and CI installs that pipe the
session to a build log still get the report; interactive output is
unchanged. `canDecorate` is constant for a run, so each channel binds to
its decorated or plain form once rather than re-testing per message
- drops the unconditional "Composio was added to your PATH" line, which
printed on three paths where nothing was written: shell-not-detected and
unsafe-install-dir (both exit 0 after reporting manual setup), and the
inline fallback's unsupported-shell and unwritable-rc branches
- adds regression tests using the production `makeTerminalUI`
implementation with captured streams, so the real decoration gate is
exercised — the shared test double reports `canDecorate: false` but
prints everything anyway, which is why this went unnoticed through two
prior attempts

## Context

Measured against the released binary, `composio install` writes 0 bytes
when its stderr is not a terminal, and exits 0 without touching any rc
file when `$SHELL` is unset. Both were invisible to the installer:

| case | exit | stderr bytes | PATH block written |
| --- | --- | --- | --- |
| `2>file`, as `install.sh` did | 0 | 0 | yes |
| `$SHELL` unset | 0 | 0 | no |
| stderr on a terminal | 0 | full report | yes |

With the redirect gone, the delegated path prints what the user was
missing — `Updated ~/.zshrc` and `Restart your shell to apply changes` /
`source ~/.zshrc` — and the manual-setup text on the paths where PATH
genuinely was not touched. The closing block no longer needs a PATH
claim of its own, and the fallback keeps printing its own
`refresh_command`.

067c67b695 already tried to surface this
output by capturing it to a temp file and replaying it on success. That
replay has been a no-op for the same reason, so this guidance has never
reached a user since the delegation was introduced.

## How Has This Been Tested?

- new tests fail with `expected '' to contain 'PATH: will add'` (empty
output — the production bug) without the `install.cmd.ts` change, and
pass with it
- full CLI suite: 957 passed, 1 skipped; `pnpm typecheck` 14/14;
`validate:boundaries`, eslint and prettier clean, no new eslint disables
- released binary in a sandboxed `$HOME`: stderr to a file yields 0
bytes; stderr on a pty yields the full report
- CLI from source with stderr captured: 155 bytes of plain guidance
where it previously wrote nothing; decorated output on a terminal
unchanged
- `install.sh` driven through a harness built from the real script under
a pty: a working binary's stderr reaches the terminal, a broken binary's
loader noise stays suppressed and the fallback writes the PATH block and
prints `source ~/.zshrc`. The same harness against `next` shows the
swallow.

No changeset: `@composio/cli` is Changesets-ignored and `install.sh` is
not a published package. The CLI note went into `CHANGELOG.md` directly.

`install.sh` is served from `next`, so the installer half of this
reaches new installs on merge and works with already-released binaries.
The `install.cmd.ts` half only affects captured-stderr installs and
rides the next CLI release.
2026-07-28 21:40:48 +05:30
Alberto Schiabel 2b9571b17f fix(core): resolve typedoc against the TS6 compiler API (#3972)
This PR:
- restores TypeDoc generation after
https://github.com/ComposioHQ/composio/pull/3966 by resolving
`@composio/core`'s TypeScript dev dependency from `catalog:ts6`
- keeps core typechecking on the root TypeScript 7 compiler while
TypeDoc receives the TS6 compiler API
- documents the additional compiler-API consumer in the shared
TypeScript catalog
- commits the regenerated `session-files.mdx` output that could not be
produced while the workflow was broken
- verifies `generate:docs`, `typecheck`, and `build` for
`@composio/core`
2026-07-28 21:03:06 +05:30
Kshitij Jhunjhunwala fc1460995b feat(cli): make headless composio login agent-friendly (PRDE-1138)
Teach non-interactive login to reuse an existing READY agent identity without creating one implicitly. Preserve explicit --no-browser and --no-wait contracts, and branch on TerminalUI.canPrompt so piping stdout alone remains interactive.

Add unattended guidance, installer analytics attribution, and focused unit and Docker E2E coverage. Exclude host node_modules from Docker build contexts.
2026-07-28 20:13:57 +05:30
Alberto Schiabel 2deb239e94 fix(cli): separate prompt, output, and decoration capabilities (#3964)
## Summary

`TerminalUI` classified the terminal with a single aggregate:

```ts
isInteractive = stdinIsTTY && stdoutIsTTY && stderrIsTTY;
```

and reused it for three unrelated decisions: whether Clack may prompt,
whether `ui.output()` emits machine data, and caller behavior in login,
setup, and the update notice. That let output routing change program
behavior:

- Redirecting stdout made `login` look non-interactive even though stdin
and stderr were still usable, so `composio login | tee log` silently
switched to the headless instructions path.
- Redirecting only stdin made `ui.output()` write machine data onto a
human-visible stdout terminal, because the output gate keyed off the
prompting aggregate instead of stdout.
- Capturing stderr changed stdout data emission, even though it is
purely a reporting-mode concern.

This PR separates the three capability axes so each stream owns exactly
one decision:

| Axis | Rule |
| --- | --- |
| Prompting | `canPrompt = stdinIsTTY && stderrIsTTY` — stdout is
irrelevant |
| Machine output | `ui.output(data)` writes only when stdout is **not**
a TTY, or with explicit `{ force: true }` (unchanged) |
| Decoration | `canDecorate = stderrIsTTY` (unchanged) |

The aggregate `isInteractive` capability is removed rather than renamed,
so no caller can reintroduce the conflation.

## Changes

- `services/terminal-ui.ts`: `TerminalCapabilities` now exposes
`canPrompt` instead of `isInteractive`; `output()` gates on
`stdoutIsTTY` alone. The live implementation is now built by an exported
`makeTerminalUI(streams)` factory so tests can exercise real behavior
against injected streams; `TerminalUILive` passes
`process.{stdin,stdout,stderr}` as before.
- `commands/login.cmd.ts`: waiting for browser login is a prompting
decision (`canPrompt`); intro/notes are stderr chrome (`canDecorate`).
Piping stdout no longer reroutes authentication.
- `commands/setup.cmd.ts`: the `--yes` guards for local changes now key
off `canPrompt`.
- `services/update-check.ts`: the update notice is advisory output for a
human watching stderr, not part of any command's result, so it now keys
off `canDecorate` — piping stdout no longer suppresses it, and it still
never touches stdout, so piped data stays clean.
- Telemetry continues to receive the raw per-stream TTY bits
(unchanged).
- `ts/packages/cli/AGENTS.md`: output conventions now document the three
independent stream contracts.

## Tests

- Full 8-combination TTY matrix at the classifier level.
- Behavioral tests with fake sink streams and mocked Clack prompts,
proving behavior rather than returned booleans:
- stdin redirected, stdout+stderr TTY: prompts fall back to defaults
without invoking Clack, `ui.output()` does **not** leak JSON to the
visible terminal, `force` still writes, decoration renders;
- stdout piped, stdin+stderr TTY: the Clack prompt actually runs,
machine data emits, decoration renders;
- stderr captured, stdin+stdout TTY: no prompts, no decoration, stdout
suppressed unless forced;
  - everything redirected: machine data still emits.
- Login regression: with stdout piped but stdin/stderr TTYs, `composio
login --no-browser` enters the interactive wait (poll spinner) path and
does not print the headless/agent instructions.
- Update-notice regressions: still shown when stdout is piped or stdin
is redirected; suppressed only when stderr is captured.

## Notes

This is the shared prerequisite for
https://github.com/ComposioHQ/composio/pull/3951 (captured-stderr plain
renderer) and https://github.com/ComposioHQ/composio/pull/3945
(agent-friendly headless login). It intentionally implements neither
feature; both PRs should rebase onto this once merged. No changeset:
`@composio/cli` is Changesets-ignored.

## Verification

- `pnpm typecheck` (repo root)
- `pnpm --filter @composio/cli test` (validate:skills,
validate:boundaries, full vitest suite)
- `eslint` and `prettier --check` on all touched files

VHS recordings skipped: internal capability wiring, no visible
command-surface change.
2026-07-28 19:57:07 +05:30
Alberto Schiabel f233e46937 chore(repo): migrate eslint to oxlint and typecheck to TypeScript 7 (#3966)
This PR:

- replaces ESLint with oxlint across the pnpm workspace and the
Bun-based docs site, porting the rules to `.oxlintrc.json` /
`docs/.oxlintrc.json` with behavior parity (restricted-syntax selectors
kept via `oxlint-plugin-eslint`)
- migrates typecheck to TypeScript 7 (`typescript@^7.0.2` catalog) and
keeps a TS6 pin for JS compiler API consumers via a named `ts6` pnpm
catalog (`ts/scripts/validate-examples.ts`, the `@composio/cli` generate
pipeline). The CLI's `typescript` dependency rebinds only the
compiler-API import — its typecheck still runs the root TS7 `tsc`, since
the alias package only ships a `tsc6` bin (documented in
`ts/packages/cli/AGENTS.md`)
- removes the `paths` mappings that pointed `@composio/core` (and, in
`experimental`, `@composio/json-schema-to-zod` plus core-internal
`#`-imports) at sibling `src` directories: under TS7, tsdown's
tsgo-based dts step emitted stray `.d.ts` files next to those
out-of-root sources on every dependent package build. Workspace deps now
resolve through their built dist types, which turbo's `dependsOn:
^build` already guarantees exist — and which the deep-path exports
(`@composio/core/*`) always used anyway
- renames the cli boundary tooling `eslint-boundaries*` →
`lint-boundaries*` and hardens the scanner to reject `oxlint-disable`
spellings so the disable manifest cannot be bypassed
- rewrites inline `eslint-disable` comments to oxlint rule names
(comment-only; no runtime changes), and adds **one new** declared
boundary: `tool-file-uploads.ts` needs `no-restricted-imports` disabled
for `node:crypto` (MD5 for the presigned-upload checksum is not in Web
Crypto), because oxlint also catches dynamic `await import()` where
ESLint did not. The manifest grows 46 → 47 deliberately
- updates CI path filters, `turbo.jsonc` lint inputs, and the docs
typescript-check workflow (renamed to "Docs - Lint and TypeScript
Validation" since it now lints too); drops `eslint`,
`typescript-eslint`, `eslint-config-next`, and `globals` from the
dependency graphs
- ships no changeset: I built `@composio/core` and `@composio/anthropic`
on this branch and on the pre-migration base and diffed the emitted
`dist/**/*.d.mts`. The provider output is byte-identical. Core's output
is **semantically identical but not byte-identical**: TS7 changes quote
style (`"x"` → `'x'`), object-property and union-member ordering in
inferred types, and picks equivalent shorter re-export alias paths for
five signatures (e.g. `OpenAI.Beta.Threads.Runs.Run` →
`OpenAI.Beta.Threads.Run` — verified both names alias the same type in
the shipped typings). Chunk-name hashes shift as a consequence. No type
gains, losses, or shape changes; `attw` and `publint` pass on the TS7
build

## Context

First of a three-PR split of #3958. The type-safety refactors are
stacked on this branch and merge after it:

- docs: https://github.com/ComposioHQ/composio/pull/3967
- `@composio/core`: https://github.com/ComposioHQ/composio/pull/3968
2026-07-28 19:16:57 +05:30
Alberto Schiabel 022ecf6176 docs(agents): require zod and effect/Schema at data boundaries (#3969)
This PR:

- codifies the schema-parsing convention in agent guidance: parse
untyped/external data once at the boundary with zod (`z.infer`
downstream) in the docs site and SDK packages, and with `effect/Schema`
in the CLI
- bans hand-rolled structural guards (`'x' in obj` / `typeof` chains),
`as`-casts of parsed JSON, and fake validators such as `z.custom(() =>
true)`
- updates `docs/AGENTS.md`, `ts/AGENTS.md`,
`ts/packages/core/AGENTS.md`, and the existing trust-boundary rule in
`ts/packages/cli/AGENTS.md`

## Context

Follow-up to the review feedback that led to the #3958 split (#3966 /
#3967 / #3968): the docs type-safety work initially shipped a suite of
conditional structural utility helpers instead of zod schemas. This
records the convention so agents and contributors reach for schemas by
default.
2026-07-28 18:44:01 +05:30
Bill a84d05fc99 fix(cli): resolve pusher-js constructor across ESM/CJS interop shapes (#3926)
## Summary

`composio listen` crashes with `TypeError: Object is not a constructor`
immediately after realtime init in every compiled release binary that
bundles pusher-js 8.5.0 (reproduced on darwin-arm64 0.2.33-beta.291–298
and 0.2.32-beta.289, and linux-x64 0.2.33-beta.298). Full diagnosis in
#3918.

**Root cause (corrected after independent verification):** pusher-js
changed the shape of its CJS export between versions — 8.4.x exports the
constructor itself (`module.exports = Pusher`), **8.5.0's Node bundle
exports a namespace object** (`module.exports = { Pusher }`; its webpack
bundle ends with `module.exports.Pusher = __webpack_exports__`), and
8.6.0 exports the constructor again. This is an undocumented upstream
packaging regression — reported in
[pusher/pusher-js#935](https://github.com/pusher/pusher-js/issues/935),
fixed by
[pusher/pusher-js#936](https://github.com/pusher/pusher-js/pull/936),
first released in 8.6.0 (2026-07-23) whose release notes do not mention
it. With 8.5.0 (what the crashing published binaries bundled),
`pusherModule.default` is not callable under **either Node or Bun, from
source or compiled** — the constructor is only reachable via the named
`Pusher` export. The earlier "build-target-dependent Bun interop" theory
in this PR's first revision and in #3918 is not correct: compiled
interop probes built with bun 1.3.10 for darwin-arm64 and bun-linux-x64
embed byte-identical bundles, so the shape varies by pusher-js version,
not by build target. "Works from source" reports came from non-frozen
installs resolving `^8.5.0` to 8.6.0.

Note: `next` now pins pusher-js 8.6.0 (#3955), which incidentally
restores a callable `module.default` in new builds — and this branch has
since been merged up with `next`, so it resolves 8.6.0 too. This PR
makes that robustness deliberate instead of incidental: pusher-js has
changed its export shape twice within two minor versions, and the new
real-module regression test has now passed against both shapes (8.5.0
pre-merge, 8.6.0 post-merge).

Fixes #3918

## Changes

- `ts/packages/cli/src/services/triggers-realtime.ts`:
- `resolvePusherConstructor` probes the interop shapes in precedence
order — `module.default.default` (defensive double-wrap),
`module.default` (8.4.0/8.6.0 class export), the module object itself,
then the named `Pusher` export on either level (`module.default.Pusher`
is the 8.5.0 shape that crashed release binaries) — returning the first
callable candidate via `Arr.findFirst` with
`Predicate.hasProperty`/`Predicate.isFunction` narrowing (no `as`
casts).
- Resolution failure surfaces as the service's typed
`TriggerRealtimeSubscriptionError` instead of an untyped `TypeError`.
- `new Pusher(...)` itself is now wrapped in `Effect.try`, so a
callable-but-non-constructable candidate would also land on the typed
error channel instead of escaping as an untyped defect past the listen
commands' `catchTag`.
- `ts/packages/cli/test/src/services/triggers-realtime.test.ts`:
regression tests pinning every interop shape, precedence (including
`default.Pusher` isolated from the top-level `Pusher` fallback), the
module-object fallback, the no-constructor failure case, and the **real
installed pusher-js module** — so the next packaging drift fails the
unit suite rather than only the compiled binary.
- `ts/packages/cli/CHANGELOG.md`: Patch Changes entry under 0.3.0 (the
CLI is Changesets-ignored, so notes go in the changelog directly),
stating the verified root cause.

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Refactor/Chore
- [ ] Documentation
- [ ] Breaking change

## How Has This Been Tested?

- `pnpm exec vitest run test/src/services/triggers-realtime.test.ts` — 9
passed.
- `pnpm --filter @composio/cli test` — full package suite green
(includes `validate:skills` and `validate:boundaries`).
- CLI package `pnpm typecheck` (src + test) and `prettier --check`
clean.
- Independent empirical verification with the release toolchain (bun
1.3.10, pinned in `mise.toml`):
- pusher-js **8.5.0**: `module.default` is `{ Pusher }` (not callable)
under Node 24, Bun from source, and `bun build --compile --production`
binaries — the pre-fix `new (module.default)(...)` throws the exact
#3918 `TypeError` in all three environments; the resolver picks the
named `Pusher` export and constructs successfully.
- pusher-js **8.4.0** and **8.6.0**: `module.default` is the
constructor; the resolver picks it directly and constructs successfully.
- darwin-arm64 vs cross-compiled bun-linux-x64 binaries embed
hash-identical pusher bundles — the interop shape does not vary by build
target, only by pusher-js version.
- Compiled-binary before/after on darwin-arm64 and linux-x64 as
described in the original report (crash reproduced from a pre-fix `next`
binary; fixed binary subscribes, holds the full `--timeout` window,
disables the temporary trigger on exit, exit 0); end-to-end Gmail
trigger delivery verified twice.

## Screenshots (if applicable)

N/A — no visible command-surface change (internal wiring fix).

## Remaining follow-up (out of scope for this PR)

- Release CI (`build-cli-binaries.yml`) smoke-tests binaries with only
`--version`/`--help`; a per-target step exercising the bundled
`import('pusher-js')` + constructor resolution would catch the next
interop drift before publishing (requested in #3918).
- The listen commands append network/login remediation hints to every
`TriggerRealtimeSubscriptionError`, which is misleading for packaging
failures, and the probed module shape is not logged on failure; a
`reason` discriminant on the error would make a future variant easier to
diagnose.

## 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 (CLI `CHANGELOG.md`; no
docs-site change — behavior matches what `composio listen` already
documents)
- [x] I added tests (`test/src/services/triggers-realtime.test.ts`)
- [x] I added a changeset if this change affects published packages —
not applicable: `@composio/cli` is excluded from Changesets by design;
changelog entry added instead

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-07-28 18:36:08 +05:30
Alberto Schiabel 24151cbe14 fix(cli): make source-map enrichment best-effort end to end (#3963)
This PR:
- fixes #3925
- treats source maps as best-effort metadata end to end: a missing,
malformed, invalid, consumer-rejected, or unreadable map — and a valid
map pointing at an original source that was never shipped — all fall
back to the raw stack location instead of aborting error capture
- decodes untrusted source-map JSON with Effect Schema and wraps
`source-map-js` exceptions in a typed `SourceMapResolutionError`
- splits the fallible work into `enrichFromMapFile` and recovers from it
with `orElseSucceed`, so `getSourcesFromMapFile`,
`getErrorRelatedSources`, `maybeMapSourcemaps` and `captureErrors` all
end up with a `never` error channel
- models mapped diagnostic variants with `Data.TaggedEnum`, using its
constructors and `$is` refinements instead of hand-written tagged
records
- removes the unchecked generic JSON cast, and drops a redundant
`node_modules` filter that interpolated an unescaped working directory
into a `RegExp`
- adds regression coverage for invalid JSON, invalid shapes, invalid
mappings, nullable optional keys, missing original sources, unreadable
maps, valid enrichment, and end-to-end diagnostic preservation
- records the user-visible fix in the CLI changelog without adding a
Changeset

## Context

`captureErrors` runs inside the top-level `Effect.catchAll` in
`cli-main.ts`, so any failure while enriching a diagnostic was the CLI's
error reporter crashing while reporting an error. #3925 reported one
trigger (malformed `.map` JSON), but three more paths had the same
effect and are fixed here:

- a **valid map whose original source was never shipped** — the common
shape for a compiled binary — failed on the `PlatformError` from reading
that source
- an **unreadable map**, e.g. a directory sitting at the `.map` path,
failed with `EISDIR`
- a **`.ts` stack frame** skips source maps entirely and reads the file
directly, with the same unguarded read

The Effect Schema decoder also has to be no stricter than
`source-map-js` itself, or valid maps silently lose enrichment. `file`
and `sourceRoot` are now `NullOr`: bundlers that emit no output filename
write `"file": null` rather than omitting the key, and the consumer
accepts it.

Two incidental correctness fixes come along with the rewrite: original
source paths are resolved with the platform path separator instead of a
hardcoded `/`, and the `node_modules` filter no longer mistakes
directories such as `node_modules_local` for real dependency paths. Both
matter on Windows.

## Verification

- `pnpm --filter @composio/cli exec vitest run test/src/effect-errors` —
32 tests passed
- `pnpm --filter @composio/cli exec vitest run` — 973 passed, 1 skipped,
109 files
- every new behavioural test was confirmed to fail against `next`, with
the source tree reverted underneath them
- `pnpm typecheck` — 14 tasks passed across 19 TypeScript packages
- `pnpm --filter @composio/cli build` — passed
- boundary validation (46 registered disables, none added), targeted
ESLint, and Prettier — passed
2026-07-28 15:12:14 +05:30
Devdatta Talele 3fc91bc65d docs: fix GitHub issue links in example READMEs (#3957)
## Summary

Fix the "GitHub Issues" links shared by ten TypeScript example READMEs.
They pointed to `github.com/composio/composio/issues`, which returns 404
because the repository belongs to the `ComposioHQ` organization.

The links now point to `github.com/ComposioHQ/composio/issues`, which
returns 200.

## Changes

- Correct the GitHub organization in ten `ts/examples/*/README.md`
files.
- Leave the root README star-badge target unchanged, per review
feedback.

## Type of change

- [x] Documentation

## How has this been tested?

```console
$ curl -sSIL -o /dev/null -w '%{http_code}\n' -A 'Mozilla/5.0' \
    https://github.com/composio/composio/issues
404

$ curl -sSIL -o /dev/null -w '%{http_code}\n' -A 'Mozilla/5.0' \
    https://github.com/ComposioHQ/composio/issues
200

$ git diff --check origin/next...HEAD
# no output
```

## Checklist

- [x] I have read the Code of Conduct and this PR adheres to it.
- [x] I updated documentation as needed.
- [x] Tests are not applicable because this changes Markdown links only.
- [x] A changeset is not needed for documentation-only changes.
2026-07-28 13:33:44 +05:30
Saransh Rana 577a3d4013 security: fix 3 CodeQL alerts (2026-07-22) (#3909)
## Summary

Automated weekly CodeQL remediation run for 2026-07-22. Closes three
`js/polynomial-redos` code scanning alerts (all severity high) in the
Cloudflare Workers / Edge platform shim.

`ts/packages/core/src/platform/workerd.ts` implements the `Platform`
interface for edge runtimes, where Node's `path` module is unavailable,
so path joining and basename extraction were done with regexes. Three of
those regexes (`/\/+$/` twice, `/^\/+|\/+$/g` once) backtrack
quadratically on long runs of `/`, so a path segment made of many
slashes turns a trim into a hang. They are replaced with index-walk
helpers that scan character codes, which is linear and allocates nothing
when the string is already trimmed.

Output is unchanged for every input. Equivalence was verified
exhaustively over all 137,257 strings of length 6 or less drawn from
`{'/', 'a', '\n', '\r', '.', ' ', '\\'}`, plus 300k randomized
multi-segment `joinPath` compositions and a Unicode surrogate spot
check, with zero mismatches against the original regexes. The `\n` and
`\r` cases matter because JavaScript `$` without the `m` flag anchors to
end of input only, so `/\/+$/` must not trim `"a/\n"`; the index walk
agrees.

All 15 open alerts on `next` were triaged. 3 are fixed here, 12 are
skipped with a recommended disposition for a human to action. Details
below.

## Changes

- `ts/packages/core/src/platform/workerd.ts`: add module private
`trimTrailingSlashes` and `trimSurroundingSlashes`, use them in
`joinPath` and `basename`. No export surface change, no `Platform`
interface change, no other platform implementation touched.
- `ts/packages/core/test/platform/workerd.test.ts`: new behavior pinning
test file.
- `.changeset/fix-workerd-slash-trim-redos.md`: patch bump for
`@composio/core`.

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Refactor/Chore
- [ ] Documentation
- [ ] Breaking change

## Fixed

| Alert | Rule | Severity | File | Fix | Test that proves it |
|---|---|---|---|---|---|
|
[#55](https://github.com/ComposioHQ/composio/security/code-scanning/55)
| `js/polynomial-redos` | High |
`ts/packages/core/src/platform/workerd.ts:21` | `joinPath` first
segment: `/\/+$/` replaced with `trimTrailingSlashes` | `joinPath
matches the pre-fix regex implementation for single segments` |
|
[#56](https://github.com/ComposioHQ/composio/security/code-scanning/56)
| `js/polynomial-redos` | High |
`ts/packages/core/src/platform/workerd.ts:23` | `joinPath` later
segments: `/^\/+\|\/+$/g` replaced with `trimSurroundingSlashes` |
`joinPath matches the pre-fix regex implementation for every segment
pair` |
|
[#57](https://github.com/ComposioHQ/composio/security/code-scanning/57)
| `js/polynomial-redos` | High |
`ts/packages/core/src/platform/workerd.ts:43` | `basename`: `/\/+$/`
replaced with `trimTrailingSlashes` | `basename matches the pre-fix
regex implementation for every input` |

On test design: the equivalence tests compare the new implementation
against a copy of the pre-fix regex logic, so they pin behavior but pass
in both states by construction. The test that is actually red before the
fix and green after is `rejects the backtracking slash regexes at the
source level`, which asserts the two vulnerable patterns are absent from
`workerd.ts`. That is what would catch a silent revert to the regex
form. A timing based "pathological input completes within N ms" test was
deliberately not written, because it is flaky in CI.

## Skipped

Twelve alerts were read and deliberately not fixed. None were dismissed
via the API; that decision is yours.

### Test only code, recommend `dismiss: used in tests`


**[#47](https://github.com/ComposioHQ/composio/security/code-scanning/47)**
`py/incomplete-url-substring-sanitization`, High,
`python/tests/test_files.py:2518`. The flagged line is `assert
"example.com" in sanitized`, a pytest assertion checking that a logging
redaction helper preserved the host. It is not a URL authorization
check.

```
gh api -X PATCH /repos/ComposioHQ/composio/code-scanning/alerts/47 \
  -f state=dismissed -f dismissed_reason="used in tests" \
  -f dismissed_comment="Substring check is a pytest assertion on a log-redaction helper, not an authorization check. codeql-autofix 2026-07-22, PR #3909"
```


**[#54](https://github.com/ComposioHQ/composio/security/code-scanning/54)**
`js/incomplete-sanitization`, High,
`ts/packages/core/test/models/verifyWebhook.integration.test.ts:339`.
`fixture.payload.replace('{', '{ ')` deliberately corrupts a payload so
the test can assert webhook verification fails on whitespace change. It
is the opposite of a sanitizer.

```
gh api -X PATCH /repos/ComposioHQ/composio/code-scanning/alerts/54 \
  -f state=dismissed -f dismissed_reason="used in tests" \
  -f dismissed_comment="Deliberate payload corruption in a negative webhook-verification test, not a sanitizer. codeql-autofix 2026-07-22, PR #3909"
```

### False positive, recommend `dismiss: false positive`


**[#105](https://github.com/ComposioHQ/composio/security/code-scanning/105)**
and
**[#106](https://github.com/ComposioHQ/composio/security/code-scanning/106)**
`js/prototype-polluting-assignment`, Medium,
`ts/packages/core/src/utils/jsonSchema.ts:255-256`.

The flagged statements are `delete out.$defs` and `delete
out.definitions`. Both use hardcoded literal keys, never attacker
controlled. `out` can never alias `Object.prototype`: every return path
in `walk()` constructs a fresh object via `Object.fromEntries` or object
spread, except the primitive passthrough at line 197, which cannot
return `Object.prototype` because the top level call is seeded with a
value already checked by `isPlainObject`. Two upstream guards further
block the flow CodeQL believes exists: `cloneChildren` filters
`POLLUTING_KEYS` (`__proto__`, `constructor`, `prototype`) at line 171,
and `tryStep` gates property access with
`Object.prototype.hasOwnProperty.call` at line 91. CodeQL cannot see
through that guard because it is wrapped in a tagged union return.

```
gh api -X PATCH /repos/ComposioHQ/composio/code-scanning/alerts/105 \
  -f state=dismissed -f dismissed_reason="false positive" \
  -f dismissed_comment="delete uses a literal key and out is always a freshly constructed object; POLLUTING_KEYS filter and hasOwnProperty guard block the modeled flow. codeql-autofix 2026-07-22, PR #3909"
```

```
gh api -X PATCH /repos/ComposioHQ/composio/code-scanning/alerts/106 \
  -f state=dismissed -f dismissed_reason="false positive" \
  -f dismissed_comment="Same flow as alert 105, on delete out.definitions. codeql-autofix 2026-07-22, PR #3909"
```

### Needs a decision, recommend `keep open`

These are not obviously wrong, but the correct fix changes generated or
rendered output, which is a product decision rather than an automated
one. They are left open on purpose.

| Alert | Rule | File | Why it needs a human |
|---|---|---|---|
|
[#48](https://github.com/ComposioHQ/composio/security/code-scanning/48)
| `js/incomplete-sanitization` |
`docs/app/llms.mdx/[[...slug]]/route.ts:626` | The only runtime site in
this group. Escapes `\|` for a markdown table cell without escaping
backslash first, so a description containing `\|` can break out of the
cell. Impact is table formatting in an LLM facing docs endpoint, not
injection into a code sink. Fixing it changes rendered docs output, and
this file is outside the `@composio/core` test baseline used for this
run. |
|
[#49](https://github.com/ComposioHQ/composio/security/code-scanning/49)
| `js/incomplete-sanitization` |
`docs/scripts/generate-meta-tools.ts:176` | Escapes `"` into YAML
frontmatter without escaping backslash first. Build time, first party
tool metadata. Backslash first escaping changes generated docs. |
|
[#50](https://github.com/ComposioHQ/composio/security/code-scanning/50)
| `js/incomplete-sanitization` |
`ts/packages/core/scripts/generate-docs.ts:1008` | `escapeTextForMdx`,
same class. Build time only. |
|
[#51](https://github.com/ComposioHQ/composio/security/code-scanning/51)
| `js/incomplete-sanitization` |
`ts/packages/core/scripts/generate-docs.ts:1008` | Second instance on
the same chain as #50. |
|
[#88](https://github.com/ComposioHQ/composio/security/code-scanning/88)
| `js/incomplete-sanitization` |
`docs/scripts/generate-meta-tools.ts:134` | Markdown table escaping of
`\|` over first party metadata. |
|
[#89](https://github.com/ComposioHQ/composio/security/code-scanning/89)
| `js/incomplete-sanitization` |
`docs/scripts/generate-meta-tools.ts:136` | Same as #88 on the fallback
description branch. |
|
[#90](https://github.com/ComposioHQ/composio/security/code-scanning/90)
| `js/incomplete-multi-character-sanitization` |
`docs/agent/lib/docs.ts:232` | `toCleanMarkdown` strips JSX and HTML
tags in a single pass, so `<<div>div>` leaves `<div>`. It cleans MDX for
LLM context and its output is never rendered as HTML, so it is not a
security sanitizer. Duplicated verbatim in #92. |
|
[#92](https://github.com/ComposioHQ/composio/security/code-scanning/92)
| `js/incomplete-multi-character-sanitization` |
`docs/scripts/build-agent-index.ts:155` | Verbatim duplicate of the
`toCleanMarkdown` helper in #90. If one is addressed, both should be,
ideally by extracting the shared helper. |

## Scanning config recommendations

Six of the twelve skips (#49, #50, #51, #88, #89, #92) share one root
cause: CodeQL scans build and docs generation scripts, and flags string
escaping over first party metadata that never crosses a trust boundary
at runtime.

This repo uses CodeQL **default setup** (`analysis_key` is
`dynamic/github-code-scanning/codeql:analyze`, and there is no committed
CodeQL workflow), so `paths-ignore` is not available. The two options
are to migrate to advanced setup with a committed workflow so
`paths-ignore` can exclude these directories, or to bulk dismiss the
alerts individually.

**Recommendation: do neither blindly, and do not add `paths-ignore` for
`**/scripts/**`.** These are real findings in real code, just low
priority ones over trusted input. Excluding the directory would also
blind the scanner to genuine future vulnerabilities in build tooling,
which is where supply chain problems tend to live. The better path is a
single follow up PR that fixes the escaping properly, escaping backslash
before the other characters, with the generated output diff reviewed as
part of that PR. If the team decides the churn is not worth it,
dismissing each as `won't fix` with that justification is the honest
alternative, and it keeps future scanning intact.

## How Has This Been Tested?

```
pnpm --filter "@composio/core^..." run build   # build workspace deps once
pnpm --filter @composio/core run test
pnpm --filter @composio/core run typecheck
```

- Pre-fix baseline on clean `next` at `2f63fe540`: **44 test files, 1034
tests, 0 failures.**
- On this branch: **45 test files, 1040 tests, 0 failures.** That is the
baseline plus this PR's 6 new tests.
- Typecheck: clean.
- There were no pre-existing failures, so nothing here is masking one.

Toolchain note for reproduction: the workspace `preinstall` hook
requires `mise`, and `@composio/json-schema-to-zod` must be built before
the `@composio/core` suite will resolve its imports. Without that build,
9 test files fail with `Failed to resolve entry for package
"@composio/json-schema-to-zod"`, which is an environment artifact and
not a real failure.

## Remaining backlog

- Fixable alerts beyond the 15 alert fix cap: **0**. All 15 open alerts
on `next` were triaged in this run.
- Untriaged alerts beyond the 50 alert triage bound: **0**. The bound
was not reached.
- Open alerts remaining after this PR merges: **12**, all listed above
with a recommended disposition. Four of them have ready to run dismissal
commands, which would permanently shrink every future run's fetch.

## Post-fix security scan

`composio-security:security-diff-scan` was run on this PR's diff as the
final gate, because a remediation can introduce its own bug and CodeQL
will not re-run until CI does.

**Verdict: clean. Zero reportable findings.**

Scope: merge-base `2f63fe540` to head `558d730e6`, all three changed
files. Every deep-review row has a completion receipt, so coverage is
complete rather than sampled. Discovery emitted no technically plausible
candidates, so validation and attack-path analysis were skipped under
the scan's documented zero-candidate rule.

Checks that were run and came back negative:

- The fix removes the denial-of-service primitive rather than relocating
it. No regex remains on the trimming paths, and all three loops strictly
progress toward their bound, so there is no unbounded-work or
infinite-loop path.
- Surrogate-pair safety. `charCodeAt` compares UTF-16 code units, and
`/` is U+002F, which can never appear as either half of a surrogate
pair. No astral-plane input can make the index walk disagree with the
regex it replaced.
- No security control changed. `joinPath` feeds `resolvePath`, which
`sensitiveFileUploadPaths.ts:39` consumes for its upload denylist. That
unchanged consumer was inspected as a negative control: byte-identical
output means it cannot observe a difference, and it independently
re-splits on slash runs at line 48 anyway.
- No new allocation or memory amplification. Both helpers return the
input unchanged when nothing needs trimming, so the fix allocates
strictly less than the `.replace()` calls it replaced.
- The new test file reproduces the two pre-fix regex literals as
reference implementations. They are applied only to a literal `CASES`
array, so there is no taint source, and `test/` is absent from this
package's `files` allowlist, so they are never published to SDK
consumers.

No new critical or high finding was introduced by this diff, so no
commit needed reverting and no alert was reclassified.

## 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 (no documented behavior changed)
- [x] I added tests or explain why not applicable
- [x] I added a changeset if this change affects published packages

## Additional context

Generated by the `codeql-autofix` skill. The bot never dismisses,
closes, or modifies alerts, and never merges or enables auto merge.
Alerts close only because merged code no longer contains the flaw.

---------

Co-authored-by: Saransh <saranshrana@Saranshs-MacBook-Pro.local>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-07-28 13:26:41 +05:30
Kshitij Jhunjhunwala a205b68b0f chore(cli): decommission npm and Homebrew install channels (PRDE-1155, PRDE-1156)
curl install.sh is now the only CLI install channel.

- INSTALL.md: drop npm/pnpm/yarn section; Windows guidance is WSL
- install.sh: Windows error no longer advises the dead npm package
- build-cli-binaries.yml: strip npm section from generated release INSTALL.md
- cli.install-health-check.yml: drop stale npm dist-tag/Homebrew comment
- cli.test-installation.yml: delete no-op npm-fallback job and its
  toolchain-versions feeder job; prune summary references
- delete cli.bump-homebrew-tap.yml + bump-homebrew-formula.py (automation
  never fired: GITHUB_TOKEN-published releases suppress release-triggered
  workflows and HOMEBREW_TAP_TOKEN is dead)
- ts/packages/cli/package.json: drop unused publishConfig (package is private)
- cli-release skill + CLI AGENTS.md: remove Homebrew steps from release docs
- toolchain-versions.json: drop now-unused node_install_compat matrix

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 17:56:09 -07:00
Alberto Schiabel 503b50ab02 chore(deps): refresh SDK, Python, and CI dependencies (#3955)
This PR:

- splits https://github.com/ComposioHQ/composio/pull/3953 in two: this
PR carries every dependency and GitHub Actions bump **except** the docs
site, which follows in a stacked PR
- consolidates and supersedes Dependabot PRs #3915, #3916, and #3934
through #3942
- adopts TypeScript 7.0.2 for primary compilation while retaining the
`@typescript/typescript6` API lane that TypeScript-ESLint still
requires, following the upstream side-by-side guidance
- refreshes Python core and provider dependencies, lockfiles, and the
Ruff 0.16 lint configuration
- updates every GitHub Action with a verified newer official release,
including majors, while retaining immutable commit SHA pins and
migrating setup-uv cache pruning
- deletes four per-package `eslint.config.mjs` shims: under ESLint 10
the default per-file config lookup re-anchors the root config's globs
into each package, so `pnpm lint` stayed green while the CLI's
try/catch, `process.env` and node-builtin bans went unenforced
- bounds and documents the new `brace-expansion` and `@hono/node-server`
security overrides, raising the `@hono/node-server` floor to 2.0.10 to
clear GHSA-9mqv-5hh9-4cgg
- preserves intentional compatibility fixtures and lanes for AI SDK 6,
Zod 3, TypeScript 5.8, Mastra AI SDK 5, and Python provider constraints

## Context

The docs site is a separate Bun workspace with its own `bun.lock` and is
not a pnpm workspace member, so the two halves share no lockfile and no
build. Splitting them keeps the Fumadocs 11 migration (a breaking API
change with real refactoring) reviewable on its own, independently of
the mechanical version bumps here.

The `brace-expansion` override deliberately spans majors:
GHSA-mh99-v99m-4gvg (HIGH) is published as a single `<=5.0.7` range with
no 1.x or 2.x backport, so narrowing it to the 5.x line puts
`brace-expansion` 2.1.2 back under `core>minimatch>brace-expansion` and
`pnpm audit --prod --audit-level=high` exits 1. Verified both ways; the
trade-off it buys is recorded inline in `pnpm-workspace.yaml`.

Verified on this branch standalone: `pnpm install --frozen-lockfile`,
`pnpm lint`, `pnpm typecheck`, `pnpm build:packages`, `pnpm test` (963
tests, 26/26 tasks), and `pnpm audit --prod --audit-level=high`.
2026-07-27 17:57:29 +05:30
Alberto Schiabel 473e9d20ad fix(cli): make version update checks reliable (#3952)
This PR:

- builds on top of https://github.com/ComposioHQ/composio/pull/3944
- reports `checkStatus: "unknown"` instead of fabricating an up-to-date
result when GitHub refreshes fail
- preserves the last successful release cache and throttles failed
attempts separately
- uses `Command.provideEffectDiscard` to start background checks after
parsing and skips explicit `composio version --check` invocations
- validates installed release versions before semver comparison
- adds deterministic unit concurrency coverage and compiled-binary CLI
E2E coverage

## Verification

- `pnpm --filter @composio/cli test` (`963` passed, `1` skipped)
- `pnpm typecheck` (`14/14` tasks)
- `pnpm build:packages` (`19/19` tasks)
- `pnpm --filter @e2e-tests/cli-version test:e2e:cli` (`9` passed)
- `pnpm build:binary` followed by `pnpm install:binary`
- installed Mach-O binary smoke checks for plain version, live and
cached `version --check`, unknown status, and both leading `--log-level`
forms
- scoped ESLint on all changed TypeScript files
- `git diff --check`

Repository-wide `pnpm lint` remains red on 52 existing errors across
unrelated docs and scripts; the changed TypeScript files pass scoped
ESLint.
2026-07-27 12:49:17 +05:30
Kshitij Jhunjhunwala 304e2fda3c feat(cli): add composio version --check for machine-readable update status
The startup upgrade banner is TTY-gated, so scripts and agent-driven (piped)
CLI usage never learn a newer stable exists. `composio version --check`
prints a JSON status to stdout — {current, latestStable, updateAvailable,
lastChecked} — reusing the update notice's self-throttled release cache
(fresh <24h: no network; stale: synchronous fetch, 10s timeout, failures
degrade to the cached/absent state).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:22:22 -07:00
pandec 03f21d4236 fix(cli): preserve symlinked shell configs during install (#3923)
## Summary

`composio install` uses a temporary file and rename so shell config
updates are atomic. When the config path is a symlink, that rename
replaces the symlink itself. This breaks setups that use tools such as
GNU Stow to manage and sync dotfiles across machines.

Fixes #3922

## Changes

- Resolve valid symlink chains before choosing the temporary file and
rename target.
- Keep regular shell config files on the existing write path.
- Add a regression test using a relative `.zshrc` symlink.

## Type of change

- [x] Bug fix
- [ ] New feature
- [ ] Refactor/Chore
- [ ] Documentation
- [ ] Breaking change

## How Has This Been Tested?

- `mise exec -- pnpm --filter @composio/cli exec vitest run
test/src/commands/install.cmd.test.ts` — 13 passed
- `mise exec -- pnpm --filter @composio/cli typecheck`
- ESLint and Prettier on the two changed files

## Screenshots (if applicable)

Not 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
- [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

No changeset is needed because `@composio/cli` is ignored by the
repository's Changesets configuration.

The current `next` branch is missing the `#ssrf_guard` alias in the CLI
Vitest config. I added that alias locally to run the focused test, then
removed it before committing this patch.

---------

Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-07-23 15:26:44 +01:00
Shrey Singla 2f63fe5407 fix(core): secure Tool Router session URL uploads (#3900)
## Summary

- route Tool Router session URL uploads through the SDK's SSRF-safe
fetch path
- validate every redirect target and reject destinations that resolve to
non-public network addresses
- stream URL response bodies with a 100 MiB limit instead of buffering
unbounded responses
- fail closed for URL uploads on edge runtimes where DNS validation is
unavailable
- apply the bounded response reader to the existing core URL-upload path
as well
- add a patch changeset for `@composio/core`

## Root cause

Tool Router session file uploads fetched string URL inputs directly and
buffered the entire response. That bypassed the SSRF checks already used
by other SDK upload paths and allowed an untrusted URL to target
internal network resources or return an unbounded response body.

## Impact

Node.js and Bun URL uploads now validate resolved addresses before
connecting and revalidate redirect hops. Cloudflare Workers and other
edge runtimes reject URL inputs with a typed error; callers can fetch
trusted content themselves and pass a `File` or `ArrayBuffer` instead.

## Validation

- focused SSRF and file upload tests: 59 passed
- full `@composio/core` suite: 1,034 passed
- TypeScript checks with both tsgo and tsc
- ESLint and Prettier
- package build, ATTW, and publint
- changeset validation
- Cloudflare Workers live E2E: 5 passed
- live Node Tool Router upload/list/download/delete flow
- live Node Tool Router HTTPS URL upload and cleanup

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/ComposioHQ/codesmith/composio/pr/3900"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1787224599&installation_model_id=428187&pr_number=3900&repository=ComposioHQ%2Fcomposio&return_to=https%3A%2F%2Fgithub.com%2FComposioHQ%2Fcomposio%2Fpull%2F3900&signature=ef519ac58a7945667565c1c143983f353b8947c988079d76c04afa9ab3b111d1"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->
2026-07-22 14:51:39 +05:30
Kshitij Jhunjhunwala f8ad9538f4 feat(cli): track plugin setup lifecycle and runtime setup telemetry
Re-lands 4e68e652 (reverted by 333d2296) and extends it:

Part 1 (re-land):
- Dedicated CLI_INSTALL_* / CLI_SETUP_* lifecycle families; 'setup' added
  to KNOWN_COMMAND_TOKENS so it is no longer misattributed to
  command_path='composio'
- Per-host CLI_PLUGIN_SETUP_SUCCEEDED / CLI_PLUGIN_UNINSTALL_SUCCEEDED
  fired from installSetupTargets/uninstallSetupTargets
- install.sh auto-setup runs with COMPOSIO_CLI_INVOCATION_ORIGIN=installer

Part 2 (extensions, fired from inside setup.cmd.ts / setup.ts):
- CLI_SETUP_HOST_DETECTED per host after detection, with agent_host,
  available, supported, host_version and normalized unsupported_reason_code
  (codex_too_old | no_json_inspection | host_command_failed | unknown)
  threaded through SetupTargetDetection
- CLI_PLUGIN_SETUP_FAILED per host (phase install|uninstall) tapped before
  the existing mapError rethrow
- CLI_SETUP_CANCELLED (user declined confirm) and CLI_SETUP_SKIPPED
  (--if-present with no supported host)
- stdout_is_tty added to the setup command properties

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 12:35:30 -07:00
Rahul Tarak a987c0da1b fix(cli): stabilize multi-account selection (#3897)
## Summary
- promote CLI multi-account selection out of the experimental feature
system so stale `multi_account: false` config can no longer disable
`execute --account`, `listen --account`, or `link --alias`
- add `proxy --account <alias|word_id|connected-account-id>` and pin the
resolved connected account into the Tool Router session
- expose account selection consistently in command help and the
generated `composio-cli` skill
- make duplicate-alias link errors explain how to select and inspect the
existing account

## Root cause
Multi-account selection was introduced behind `multi_account` in #3129.
#3163 enabled it by default for stable builds, but the command
implementations still consulted the experimental setting and the custom
help surface still omitted the selector. That left stale or explicit
`multi_account: false` config able to disable selection even though the
feature was intended to be stable. `proxy` never received equivalent
account-selection wiring.

This is an incomplete graduation of the feature rather than a recent
code regression. Existing implicit/default account selection behavior
remains unchanged; this PR does **not** require explicit selection.

Closes #3894.

## Tests
- `vitest run` in `ts/packages/cli`: **84 files passed, 785 tests
passed, 1 skipped**
- focused proxy/link/help regression suite: **26 tests passed**
- CLI source and test typechecks via `tsgo --noEmit`: passed
- `tsx ./scripts/validate-skills.ts`: passed for stable and beta builds
- Prettier and `git diff --check`: passed

## Notes
- No changeset: `@composio/cli` is excluded from Changesets; the CLI
changelog is updated directly.
- No VHS recording: this is a narrow selector-wiring/help regression
covered by command-level tests and requires a real multi-account fixture
to demo safely.
- The root Turbo typecheck entrypoint could not run in this worktree
because the local environment does not have `mise` on `PATH`; the
directly equivalent CLI source/test `tsgo` checks passed.

- GitHub CI: queued/pending at PR creation; no background CI watch
started per workspace policy.
2026-07-21 12:34:16 +04:00
Alberto Schiabel 332cb71214 fix(ci): clear dependency audit and cli test failures on next (#3898)
This PR:

- bumps the transitive `brace-expansion` resolution to 2.1.2
([GHSA-3jxr-9vmj-r5cp](https://github.com/advisories/GHSA-3jxr-9vmj-r5cp),
high)
- adds a scoped `js-yaml` override to `>=4.3.0 <5`
([GHSA-52cp-r559-cp3m](https://github.com/advisories/GHSA-52cp-r559-cp3m),
high) — `@redocly/openapi-core@1.34.x` (via `openapi-typescript`)
exact-pins vulnerable 4.2.0, so a lockfile bump alone cannot reach the
patched version
- bumps the existing `protobufjs` override 7.6.3 → 7.6.5
([GHSA-j3f2-48v5-ccww](https://github.com/advisories/GHSA-j3f2-48v5-ccww),
moderate)
- leaves the low `@ai-sdk/provider-utils` advisory as-is: the advertised
patched version 3.0.98 is not published on npm and `@mastra/core`
exact-pins 3.0.25
- aligns `analytics.dispatch.test.ts` mocks with the `spawnDetached`
contract from https://github.com/ComposioHQ/composio/pull/3880
(`once('spawn')`/`once('error')` with unref-after-confirmation); the
test added in https://github.com/ComposioHQ/composio/pull/3881 still
asserted the old fire-and-forget `.on('error')` shape

Verified locally: `pnpm audit --prod --audit-level=high` exits 0 (1 low
remaining), `pnpm install --frozen-lockfile` passes, and the full
`@composio/cli` suite passes (934 tests).

<!-- codesmith:footer -->
---
<a
href="https://app.blacksmith.sh/ComposioHQ/codesmith/composio/pr/3898"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img
alt="View with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a>
<a
href="https://backend.blacksmith.sh/track/enable-autofix?expires=1787212096&installation_model_id=428187&pr_number=3898&repository=ComposioHQ%2Fcomposio&return_to=https%3A%2F%2Fgithub.com%2FComposioHQ%2Fcomposio%2Fpull%2F3898&signature=9d7662095d47fa89940855b0abb67f014a807b522b81ea780b62f6326dc0e807"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img
alt="Autofix with Codesmith"
src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a>
<sup>Need help on this PR? Tag <code>/codesmith</code> with what you
need. Autofix is disabled.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->
2026-07-21 12:09:07 +04:00
Alberto Schiabel 0979159e10 refactor(cli): activate the try/catch and env ban and pin the boundary ratchet (#3885)
This PR:
- activates the final rule group: `TryStatement` and `process.env` are
now ESLint-banned in CLI source, completing the enforcement ramp started
in the first PR of this stack; the env guard covers dot access, bracket
access, and object destructuring
- migrates the residual files (auth-config create, trigger create,
toolkit search, runtime debug flags, master detection, import-time UI
setup) off try/catch and ad-hoc env reads
- every surviving violation sits at a declared runtime boundary (bin.ts
bootstrap, child-process companion runtime, import-time UI setup,
environment writes and whole-environment enumeration, spawn-time env
handshakes) with an inline `// eslint-disable-next-line <rule> --
<reason>` justification
- adds `eslint-boundaries.json` — the checked-in manifest of the 46
sanctioned disables across 22 files — and `validate:boundaries` (part of
`pnpm test`), which scans both `.ts` and `.tsx` sources and fails on
undeclared, missing, relocated, bare, or file-wide disables
- pins each registered boundary by rule, justification, and governed
next-line target, with regression coverage for TSX discovery, same-file
retargeting, and duplicate semantic sites
- documents the Effect Boundary Policy in the CLI `AGENTS.md` and the
`cli-command` skill reference, and drops the gitleaks test-file
allowlist
---

**Stack** (re-slice of https://github.com/ComposioHQ/composio/pull/3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. https://github.com/ComposioHQ/composio/pull/3877 — guidelines + inert
rule groups
2. https://github.com/ComposioHQ/composio/pull/3878 — `new Function()`
eval security fix
3. https://github.com/ComposioHQ/composio/pull/3879 — behavioral fix
pack
4. https://github.com/ComposioHQ/composio/pull/3880 — platform-imports
rule + migration
5. https://github.com/ComposioHQ/composio/pull/3881 — terminal-streams
rule + TerminalUI boundary
6. https://github.com/ComposioHQ/composio/pull/3882 — descriptor seam +
zod→Schema
7. https://github.com/ComposioHQ/composio/pull/3883 — test-tree lint +
deterministic suites
8. https://github.com/ComposioHQ/composio/pull/3884 — typed error
boundaries + v4 seams
9. https://github.com/ComposioHQ/composio/pull/3885 — try/catch+env ban
+ boundary ratchet ← **this PR**

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
2026-07-21 11:36:32 +04:00
Alberto Schiabel aaabe9922b refactor(cli): enforce typed Effect error boundaries and v4 readiness seams (#3884)
This PR:
- converts every remaining thunk-form `Effect.tryPromise` in CLI source
to a purpose-named `Data.TaggedError` preserving its cause —
`UnknownException` no longer surfaces from `src/` — and switches
best-effort recoveries from `catchAll` to `catchTag`
- isolates the `Symbol.for('effect/SpanAnnotation')` internal read
behind a shape-validating adapter, with a canary test that fails a real
effect through the runtime so an `effect` upgrade that drops the
mechanism breaks CI instead of silently losing span timelines
- decodes trigger-log payloads once at the API boundary through a
tolerant schema documenting camelCase/snake_case/meta-nested alias
precedence
- deduplicates the per-entry tolerant decoders into
`utils/collect-decoded-entries.ts`, replaces locally re-declared record
schemas with `JsonRecordSchema`, deletes the unused `EnvLangDetector`
service, and removes the temporary sync bridge from the platform-imports
slice
- the largest PR of the stack by design: generic refactors land late so
the fix and rule slices before it stay small; one commit per sub-theme
keeps `git show` reviewable
---

**Stack** (re-slice of https://github.com/ComposioHQ/composio/pull/3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. https://github.com/ComposioHQ/composio/pull/3877 — guidelines + inert
rule groups
2. https://github.com/ComposioHQ/composio/pull/3878 — `new Function()`
eval security fix
3. https://github.com/ComposioHQ/composio/pull/3879 — behavioral fix
pack
4. https://github.com/ComposioHQ/composio/pull/3880 — platform-imports
rule + migration
5. https://github.com/ComposioHQ/composio/pull/3881 — terminal-streams
rule + TerminalUI boundary
6. https://github.com/ComposioHQ/composio/pull/3882 — descriptor seam +
zod→Schema
7. https://github.com/ComposioHQ/composio/pull/3883 — test-tree lint +
deterministic suites
8. https://github.com/ComposioHQ/composio/pull/3884 — typed error
boundaries + v4 seams ← **this PR**
9. https://github.com/ComposioHQ/composio/pull/3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
2026-07-21 11:36:00 +04:00
Alberto Schiabel 6010fc712c test(cli): lint the test tree and enforce deterministic @effect/vitest suites (#3883)
This PR:
- un-ignores `ts/packages/cli/test` in the root ESLint config (fixtures
and the `test/__utils__` harness glue stay unlinted) and activates the
test rule group: no `Effect.run*` in suites (use
`it.effect`/`it.scoped`/`it.live`) and no `Date.now()`
- adds `test/__utils__/vitest.setup.ts`, pointing HOME and
`COMPOSIO_CACHE_DIR` at per-run temp directories so suites never touch
the developer's real `~/.composio` state
- migrates the remaining suites onto @effect/vitest runners with
layer-provided platforms, pins wall-clock reads via
`vi.setSystemTime`/TestClock-relative fixtures, and uses
`crypto.randomUUID()` for unique temp names
- lands the v3 Cause characterization suite pinning `reduceWithContext`
traversal over the six-constructor tree and rendered error output — the
executable equivalence oracle for the effect v4 migration
- tests arriving in later stack PRs must comply from birth
---

**Stack** (re-slice of https://github.com/ComposioHQ/composio/pull/3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. https://github.com/ComposioHQ/composio/pull/3877 — guidelines + inert
rule groups
2. https://github.com/ComposioHQ/composio/pull/3878 — `new Function()`
eval security fix
3. https://github.com/ComposioHQ/composio/pull/3879 — behavioral fix
pack
4. https://github.com/ComposioHQ/composio/pull/3880 — platform-imports
rule + migration
5. https://github.com/ComposioHQ/composio/pull/3881 — terminal-streams
rule + TerminalUI boundary
6. https://github.com/ComposioHQ/composio/pull/3882 — descriptor seam +
zod→Schema
7. https://github.com/ComposioHQ/composio/pull/3883 — test-tree lint +
deterministic suites ← **this PR**
8. https://github.com/ComposioHQ/composio/pull/3884 — typed error
boundaries + v4 seams
9. https://github.com/ComposioHQ/composio/pull/3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
2026-07-21 11:35:28 +04:00
Alberto Schiabel 6d5465acd3 refactor(cli): seal the CommandDescriptor seam and drop zod for Schema (#3882)
This PR:
- activates the descriptor-seam rule group:
`commands/command-introspection.ts` becomes the only module allowed to
import `CommandDescriptor`/`Usage` from `@effect/cli` — the redesign
seam the Effect CLI v4 migration will reimplement
- routes root command assembly, preflight parsing, help routing, and
value-option collection through the seam helpers instead of descriptor
walks
- replaces the Zod-backed tool-input adapter with an internal
`@composio/json-schema-to-effect-schema` compiler backed by the
eval-free `@cfworker/json-schema` interpreter
- normalizes the OpenAPI and Composio extensions accepted by the
previous validator, while preserving typed `ToolInputValidationError`
failures, field paths, multi-error reporting, and typo suggestions
- extends the descriptor and Zod boundaries to reject static imports,
dynamic `import()`, and `require()` bypasses
- adds compiler parity tests plus CLI regression coverage for multiple
simultaneous field and unknown-key failures
- introspection tests pin that the helpers yield identical descriptors
and usage strings

---

**Stack** (re-slice of https://github.com/ComposioHQ/composio/pull/3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. https://github.com/ComposioHQ/composio/pull/3877 — guidelines + inert
rule groups
2. https://github.com/ComposioHQ/composio/pull/3878 — `new Function()`
eval security fix
3. https://github.com/ComposioHQ/composio/pull/3879 — behavioral fix
pack
4. https://github.com/ComposioHQ/composio/pull/3880 — platform-imports
rule + migration
5. https://github.com/ComposioHQ/composio/pull/3881 — terminal-streams
rule + TerminalUI boundary
6. https://github.com/ComposioHQ/composio/pull/3882 — descriptor seam +
Zod→Schema ← **this PR**
7. https://github.com/ComposioHQ/composio/pull/3883 — test-tree lint +
deterministic suites
8. https://github.com/ComposioHQ/composio/pull/3884 — typed error
boundaries + v4 seams
9. https://github.com/ComposioHQ/composio/pull/3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
2026-07-21 11:25:58 +04:00
Alberto Schiabel 5318f60de9 refactor(cli): route terminal output through the TerminalUI boundary (#3881)
This PR:
- activates the terminal-streams rule group:
`process.stdout`/`process.stderr` are now ESLint-banned in CLI source,
with `services/terminal-ui.ts` as the sole stream boundary (that file is
itself held to the try/catch + `process.env` restrictions early)
- extends `TerminalUI` with `capabilities` (TTY/interactivity/decoration
detection) and a raw `error()` writer, replacing `utils/stdio.ts`
(deleted)
- makes analytics dispatch Effect-native and delivers detached telemetry
through `DetachedProcess`, staying non-fatal on failure
- consolidates runtime debug logging into a `RuntimeDebugLogger` service
and rebuilds `cli-main` around `showUpdateNotice`/telemetry as Effects
with errors reported via `ui.error`
- the update checker reads terminal capabilities from the service and
keeps its fire-and-forget contract
---

**Stack** (re-slice of https://github.com/ComposioHQ/composio/pull/3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. https://github.com/ComposioHQ/composio/pull/3877 — guidelines + inert
rule groups
2. https://github.com/ComposioHQ/composio/pull/3878 — `new Function()`
eval security fix
3. https://github.com/ComposioHQ/composio/pull/3879 — behavioral fix
pack
4. https://github.com/ComposioHQ/composio/pull/3880 — platform-imports
rule + migration
5. https://github.com/ComposioHQ/composio/pull/3881 — terminal-streams
rule + TerminalUI boundary ← **this PR**
6. https://github.com/ComposioHQ/composio/pull/3882 — descriptor seam +
zod→Schema
7. https://github.com/ComposioHQ/composio/pull/3883 — test-tree lint +
deterministic suites
8. https://github.com/ComposioHQ/composio/pull/3884 — typed error
boundaries + v4 seams
9. https://github.com/ComposioHQ/composio/pull/3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
2026-07-21 11:25:29 +04:00
Alberto Schiabel 4e26d36fd4 refactor(cli): migrate platform access to @effect/platform services (#3880)
This PR:
- activates the platform-imports rule group: `node:fs`, `node:path`,
`node:os`, and `child_process` are now ESLint-banned in CLI source
- migrates ~36 files to `FileSystem`/`Path`/`Command` from
`@effect/platform`, introducing the `NodeOs` and `DetachedProcess`
service boundaries and Effect-native run-companion module resolution
- keeps the sanctioned permanent boundaries (`node-os.ts`,
`detached-process.ts`, `run-subagent-output-mcp.ts`,
`cli-user-config.ts`, the child-process companion runtime) on inline `--
reason` disables
- files whose compliant rewrite belongs to a later slice carry temporary
inline disables naming the owning PR; a small sync bridge in
`run-companion-modules.ts` keeps the not-yet-migrated Promise-based
callers working until the typed-error slice removes it
- adds A-theme coverage: `NodeOs` platform info through Effect, session
artifacts via `FileSystem` with symlink and HOME-less handling,
skill-root discovery, and safe-output-path traversal guards
---

**Stack** (re-slice of https://github.com/ComposioHQ/composio/pull/3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. https://github.com/ComposioHQ/composio/pull/3877 — guidelines + inert
rule groups
2. https://github.com/ComposioHQ/composio/pull/3878 — `new Function()`
eval security fix
3. https://github.com/ComposioHQ/composio/pull/3879 — behavioral fix
pack
4. https://github.com/ComposioHQ/composio/pull/3880 — platform-imports
rule + migration ← **this PR**
5. https://github.com/ComposioHQ/composio/pull/3881 — terminal-streams
rule + TerminalUI boundary
6. https://github.com/ComposioHQ/composio/pull/3882 — descriptor seam +
zod→Schema
7. https://github.com/ComposioHQ/composio/pull/3883 — test-tree lint +
deterministic suites
8. https://github.com/ComposioHQ/composio/pull/3884 — typed error
boundaries + v4 seams
9. https://github.com/ComposioHQ/composio/pull/3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.

## Review follow-up

- `DetachedProcess` now waits for Node’s `spawn` or `error` event, so
ENOENT/EACCES become typed failures instead of false success; focused
tests cover failure and success.
- the platform-import rule now rejects bare `fs`, `fs/*`, `os`, and
`path` specifiers as well as their `node:` forms; the two exposed path
imports now use Effect `Path`.
- run-companion self-repair again honors unprefixed `GITHUB_*` values
first, with `COMPOSIO_GITHUB_*` fallback and regression coverage for
both contracts.

Verification: CLI tests (95 files, 834 passed, 1 skipped), workspace
TypeScript typecheck, scoped ESLint (0 errors), CLI build, Prettier, and
`git diff --check`.
2026-07-21 11:19:01 +04:00
Alberto Schiabel 377de97293 fix(cli): fail closed on permission gaps and tolerate skewed payloads (#3879)
This PR:
- lands ten behavioral fixes as one commit each, so `git show` reviews
standalone
- fails the tool-permission gate closed: an unknown snapshot state or a
failed policy resolve on a controls-enabled org now synthesizes an
`ask_every_call` snapshot (never persisted) instead of silently skipping
the gate, and interactive approval UI never spawns where nobody can
answer it (CI/Vitest)
- surfaces realtime subscription failures as typed errors: `composio
listen` prints one actionable line mentioning `composio login`, cleans
up temporary triggers, and exits 1 instead of dumping a captured defect
- tolerates newer connected-account payloads with a fallback decode
whose field allowlist keeps credential-bearing fields (`state`, `data`)
out of `link --list` output
- decodes persisted caches and GitHub release listings per entry, so one
corrupt or version-skewed entry drops only itself
- extracts API error details field by field, renders the deepest cause
in pretty errors, and accepts `null` optional strings in agent API
responses
---

**Stack** (re-slice of https://github.com/ComposioHQ/composio/pull/3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. https://github.com/ComposioHQ/composio/pull/3877 — guidelines + inert
rule groups
2. https://github.com/ComposioHQ/composio/pull/3878 — `new Function()`
eval security fix
3. https://github.com/ComposioHQ/composio/pull/3879 — behavioral fix
pack ← **this PR**
4. https://github.com/ComposioHQ/composio/pull/3880 — platform-imports
rule + migration
5. https://github.com/ComposioHQ/composio/pull/3881 — terminal-streams
rule + TerminalUI boundary
6. https://github.com/ComposioHQ/composio/pull/3882 — descriptor seam +
zod→Schema
7. https://github.com/ComposioHQ/composio/pull/3883 — test-tree lint +
deterministic suites
8. https://github.com/ComposioHQ/composio/pull/3884 — typed error
boundaries + v4 seams
9. https://github.com/ComposioHQ/composio/pull/3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
2026-07-21 11:15:53 +04:00
Alberto Schiabel 2dc9ef34d0 fix(cli): remove new Function() eval from JSON-ish input parsing (#3878)
This PR:
- removes an in-process arbitrary-code-execution surface:
`parse-json-ish.ts` fell back to `Function("return (…)")()` for any
`tools execute --data`, `listen --params`, or `proxy` body value that
failed JSON and comment-JSON parsing — the CLI is agent-facing and
`--data` also accepts `@file` and stdin, so crafted or prompt-injected
input reached the eval
- lands the parser in its final consolidated shape:
`src/utils/parse-json.ts` parses JSON5 (unquoted keys, single quotes,
trailing commas, comments) without evaluating anything and returns
`Either` with a typed `JsonParsingError`
- consolidates the `effect-errors` read-json duplicates onto the same
parser and swaps `comment-json` for `json5` (the stack's only lockfile
change)
- pins code-execution probes in unit tests (`new Date()`, computed
expressions, `globalThis` writes are rejected, never evaluated)
- intentional narrowing: JS expressions such as `1+1` that only worked
because of the eval now fail with a parse error; proxy bodies that are
not JSON5 records fall back to the raw string
---

**Stack** (re-slice of https://github.com/ComposioHQ/composio/pull/3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. https://github.com/ComposioHQ/composio/pull/3877 — guidelines + inert
rule groups
2. https://github.com/ComposioHQ/composio/pull/3878 — `new Function()`
eval security fix ← **this PR**
3. https://github.com/ComposioHQ/composio/pull/3879 — behavioral fix
pack
4. https://github.com/ComposioHQ/composio/pull/3880 — platform-imports
rule + migration
5. https://github.com/ComposioHQ/composio/pull/3881 — terminal-streams
rule + TerminalUI boundary
6. https://github.com/ComposioHQ/composio/pull/3882 — descriptor seam +
zod→Schema
7. https://github.com/ComposioHQ/composio/pull/3883 — test-tree lint +
deterministic suites
8. https://github.com/ComposioHQ/composio/pull/3884 — typed error
boundaries + v4 seams
9. https://github.com/ComposioHQ/composio/pull/3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
2026-07-21 11:14:55 +04:00
Alberto Schiabel 3c7a6c6c91 chore(cli): land Effect-first guidelines and inert guardrail rule groups (#3877)
This PR:
- opens the CLI Effect-guardrails stack that re-slices
https://github.com/ComposioHQ/composio/pull/3859 into reviewable PRs
- rewrites the CLI `AGENTS.md` Effect guidance: typed `Data.TaggedError`
failures, `Schema`/`Predicate` decoding at trust boundaries, and Effect
platform services over Node builtins — policy for new code effective now
- adds the matching "Effect Platform Boundaries" section to the
`cli-command` skill reference
- defines every new ESLint rule group (platform imports, terminal
streams, descriptor seam, zod ban, test rules, try/catch +
`process.env`) as live constants in the root `eslint.config.mjs`, with
the entries applying them commented out and annotated with the stack PR
that activates each group
- bumps the vendored `ts/vendor/effect` reference to the pin used across
the stack (CI never initializes submodules)

No CLI source is touched; `pnpm lint` stays green with the rules inert.
---

**Stack** (re-slice of https://github.com/ComposioHQ/composio/pull/3859;
each PR targets its parent and auto-retargets to `next` as parents
merge):

1. https://github.com/ComposioHQ/composio/pull/3877 — guidelines + inert
rule groups ← **this PR**
2. https://github.com/ComposioHQ/composio/pull/3878 — `new Function()`
eval security fix
3. https://github.com/ComposioHQ/composio/pull/3879 — behavioral fix
pack
4. https://github.com/ComposioHQ/composio/pull/3880 — platform-imports
rule + migration
5. https://github.com/ComposioHQ/composio/pull/3881 — terminal-streams
rule + TerminalUI boundary
6. https://github.com/ComposioHQ/composio/pull/3882 — descriptor seam +
zod→Schema
7. https://github.com/ComposioHQ/composio/pull/3883 — test-tree lint +
deterministic suites
8. https://github.com/ComposioHQ/composio/pull/3884 — typed error
boundaries + v4 seams
9. https://github.com/ComposioHQ/composio/pull/3885 — try/catch+env ban
+ boundary ratchet

On stacked bases CI runs lint/build and the CLI Docker e2e job; unit
tests and typecheck are verified locally per PR and re-verified by full
CI when each PR is retargeted to `next`.
2026-07-21 11:13:04 +04:00
Alberto Schiabel 9498679c26 fix(release): guard and document CLI releases (#3895)
This PR:
- removes the stale `@composio/cli` changeset that wedges
`changesets/action` and preserves its release note in the CLI changelog
- adds `validate:changesets` before the TypeScript release action and
covers ignored-package changesets in the release regression suite
- makes the guard read changeset files directly so it also works in
shallow and detached CI checkouts
- refreshes `mise.lock` after the pinned Python standalone artifacts
moved to the 20260718 build
- adds the repo-local `cli-release` skill with beta, stable-promotion,
verification, and failure-recovery procedures
- replaces the contradictory "stable via changeset" contributor guidance
with the tested-beta promotion path
- extends skill taxonomy, routing probes, and PR path filters so the
guard cannot silently drift

## Regression coverage

The validator test creates a changeset fixture outside a Git repository,
verifies that an ignored CLI package is rejected, then verifies that a
normal package changeset passes. This reproduces the shallow-checkout
failure without relying on a local `next` ref.

## Verification

- `pnpm validate:agent-skills`
- `pnpm validate:skill-routing`
- `pnpm validate:changesets`
- `pnpm test:release-workflow`
- `pnpm lint`
- `pnpm install --frozen-lockfile`
- all 24 TypeScript package test tasks
- skill-creator `quick_validate.py`
- Prettier check
- `git diff --check`
- manual beta release
[`@composio/cli@0.2.33-beta.294`](https://github.com/ComposioHQ/composio/releases/tag/%40composio/cli%400.2.33-beta.294):
33/33 release and installation jobs passed
2026-07-20 23:17:03 +04:00
xHai 8467efdfb9 fix(cli): whoami respects the org selected via orgs switch (#3832)
## Summary

`composio whoami` reports the wrong organization after `composio orgs
switch`.
It always shows the API key's home org, ignoring the switched-to org,
even
though `composio orgs list` correctly reflects the selection.

**Root cause:** `whoami` resolves session info via
`getSessionInfoByUserApiKey`,
which sent only `x-user-api-key` and omitted `x-org-id`. Without that
header the
backend falls back to the API key's home org. Consumer commands
(`search`,
`execute`, …) already forward the selected org via
`resolveCommandProject`, so
only `whoami` was affected — a display bug, not an API bug.

**Verification that the backend honors the header** (direct calls to
`GET /api/v3/auth/session/info`):

| `x-org-id` | returned project/org |
|---|---|
| org A | org A |
| org B | org B |
| (omitted) | API key's home org |

## Changes

- `getSessionInfoByUserApiKey` accepts an optional `orgId` and forwards
it as
`x-org-id` when present (login-time callers pass none, so their behavior
is
  unchanged).
- `whoami` passes the selected global org (`ctx.data.orgId`).
- Unit tests asserting the header is sent when an org is selected and
omitted
  otherwise.

## Type of change
- [x] Bug fix

## How Has This Been Tested?

- New unit tests: `test/src/services/composio-clients.test.ts` (2
cases).
- Existing `whoami.cmd.test.ts` still passes (5 tests total green).
- Manual end-to-end with a real UAK against two orgs: a binary built
from this
  branch reports the switched org correctly (`orgs switch` → `whoami`),
  whereas the released 0.2.31 binary always reported the home org.

## 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 (no user-facing docs affected)
- [x] I added tests or explain why not applicable
- [x] I added a changeset if this change affects published packages
2026-07-17 00:11:33 +04:00
Alberto Schiabel ac7ca020da fix(release): drop stale changeset for ignored @composio/cli (#3861)
This PR:

- unblocks #3820 — the `0.14.0` release train (including
`@composio/experimental@0.2.0` with the `./eve` export) is versioned on
`next` but was never published to npm
- deletes `.changeset/cli-file-upload-denylist.md`: it targets
`@composio/cli`, which is in the changesets `ignore` list (since #3676),
so `changeset version` never consumes it
- the leftover changeset makes `changesets/action` pick version-PR mode
over publish mode on every push to `next`, then fail with `No commits
between next and changeset-release/next` because the versioning no-ops —
see the [failed
run](https://github.com/ComposioHQ/composio/actions/runs/29449907252/job/87469539026);
the publish step never executes
- ports the security note from the deleted changeset into the `0.3.0`
section of `ts/packages/cli/CHANGELOG.md` — the fix commit (fc17c37bf,
#3763) is part of the `0.3.0` tree, and `@composio/cli@0.3.0` has no
tag/release yet

## Context

Once this merges, the next `TS SDK Release` run on `next` will find no
pending changesets, take the publish path (`pnpm changeset:release`),
and push the pending versions to npm.

Follow-up worth considering (not in this PR): a CI check that rejects
changesets referencing packages in the `ignore` list, since any such
changeset permanently wedges the release train.
2026-07-16 16:54:58 +04:00
sdkrelease[bot] 26f3c626e9 Release: update version (#3717)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to next, this PR will
be updated.


# Releases
## @composio/core@0.14.0

### Minor Changes

- fc17c37: Export the sensitive-file-upload denylist guard from the
package root so downstream packages share one implementation:
`assertSafeFileUploadPath`, `isBlockedSensitiveFileUploadPath`, and
`BUILTIN_FILE_UPLOAD_PATH_DENY_SEGMENTS`. The guard now routes its
filesystem access through the internal `#platform` abstraction (adding a
`realpathSync` platform method), so it is edge/workerd-safe and the
module carries no static `node:*` imports. Behavior on Node/Bun is
unchanged.
- 20a4711: `triggers.create` now resolves the connection from `user_id`
on the backend instead of client-side.

- The SDK no longer makes an extra `connectedAccounts.list()` call. When
`connectedAccountId` is omitted, the backend resolves the first active
connection for the user and the trigger's toolkit (ordered by most
recently created), matching tool execution.
- **Behavior change:** `create` no longer throws
`ComposioConnectedAccountNotFoundError` for a missing or invalid
connection. That case now surfaces as the backend error from the upsert
call. `ComposioTriggerTypeNotFoundError` (invalid slug) and
`ValidationError` (including empty `userId`) are still thrown
client-side.
- **Requires a backend that resolves the trigger connection from
`user_id` on upsert**
([ComposioHQ/platform#10932](https://github.com/ComposioHQ/platform/pull/10932)).
Self-hosted deployments must be on a version that includes it.

### Patch Changes

- 7125576: Normalize duplicate JSON Schema `required` entries before
provider tool schemas are emitted.
- 58bc93b: Refresh dependency ranges and lockfiles across the workspace.
- 4c3a321: Disable client retries on `tools.execute` and
`tools.proxyExecute`. These are non-idempotent writes, so a silent retry
after a read timeout could duplicate the side effect (e.g. send the same
email more than once). Both now route through a sibling client built
with `maxRetries: 0`; reads keep the default retry behaviour.
- b07fcad: Add an eve provider: `EveProvider` makes `session.tools()`
return eve-native `defineTool`s, `defineComposioTools` is the
replay-safe `step.started` resolver, and `(ctx, next)` hooks can
rewrite, deny, or transform Tool Router meta-tool calls.

Preserve successful local-tool results when the remote half of a mixed
`COMPOSIO_MULTI_EXECUTE_TOOL` batch fails at the transport layer, so
callers can see which side effects already completed before retrying.

- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
- 2ef40ce: Treat local file paths that begin with `http` as paths
instead of URLs, ensuring that upload allowlist and sensitive-file
denylist checks still run.
- e78ed31: Execute every parallel tool call in
`OpenAIProvider.handleToolCalls`. It previously only ran the first tool
call in each assistant message, so parallel tool calls (on by default)
dropped the rest and left their `tool_call_id`s unanswered, failing the
next request.

The calls are run sequentially, in the order the model returned them —
here "parallel" means the model issued several calls in one turn, not
that they execute concurrently — so each `tool_call_id` is answered
exactly once and the tool messages come back in a deterministic order.

Only the first choice is handled. Tool results are fed back into a
single assistant turn, so with `n > 1` iterating over every choice would
run each tool call once per choice and orphan the `tool_call_id`s from
the alternative completions.

- a0f37a7: Close two secret/SSRF exposure surfaces in the TypeScript
SDK:

- **SSRF guard on URL file inputs.** `composio.files.upload(url)` and
automatic file upload during tool execution previously did a raw
`fetch()` on user-supplied URLs with no guard. They now resolve the host
and refuse private, loopback, link-local (incl. the `169.254.169.254`
cloud-metadata endpoint), CGNAT, and reserved addresses, reject
non-`http(s)` schemes, and follow redirects manually so each hop is
re-validated (blocking a public URL that redirects into internal space).
Blocked requests throw `ComposioBlockedInternalUrlError`. Node-only;
behaviour for public URLs is unchanged.
- **Telemetry redaction.** Error telemetry previously shipped
`error.message` / `error.stack` verbatim. They are now passed through a
redactor that strips URL query strings, `Authorization` bearer/basic
credentials, and secret-like `key=value` pairs (API keys, tokens, client
secrets, passwords) before transport.

- 820abb9: Resolve toolkit version pins case-insensitively. Version maps
are keyed by normalized (lowercase) slugs, but `getToolkitVersion`
previously looked them up with the raw slug, so a pin configured under a
different casing (e.g. `{ GitHub: '20250101_00' }` or
`COMPOSIO_TOOLKIT_VERSION_GITHUB`) could silently fall back to
`'latest'`. Normalization is now centralized in a single
`normalizeToolkitSlug` helper used symmetrically on both the write
(map-building) and read (lookup) paths, so the two sides can no longer
drift. This mirrors the equivalent fix in the Python SDK.
- Updated dependencies [58bc93b]
- Updated dependencies [fa933a6]
  - @composio/json-schema-to-zod@0.2.1
## @composio/experimental@0.2.0

### Minor Changes

- b07fcad: Add an eve provider: `EveProvider` makes `session.tools()`
return eve-native `defineTool`s, `defineComposioTools` is the
replay-safe `step.started` resolver, and `(ctx, next)` hooks can
rewrite, deny, or transform Tool Router meta-tool calls.

Preserve successful local-tool results when the remote half of a mixed
`COMPOSIO_MULTI_EXECUTE_TOOL` batch fails at the transport layer, so
callers can see which side effects already completed before retrying.

### Patch Changes

- 58bc93b: Refresh dependency ranges and lockfiles across the workspace.
- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/json-schema-to-zod@0.2.1

### Patch Changes

- 58bc93b: Refresh dependency ranges and lockfiles across the workspace.
- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/anthropic@0.10.1

### Patch Changes

- 7125576: Normalize duplicate JSON Schema `required` entries before
provider tool schemas are emitted.
- 58bc93b: Refresh dependency ranges and lockfiles across the workspace.
- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/claude-agent-sdk@0.10.1

### Patch Changes

- 58bc93b: Refresh dependency ranges and lockfiles across the workspace.
- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/cloudflare@0.10.1

### Patch Changes

- 7125576: Normalize duplicate JSON Schema `required` entries before
provider tool schemas are emitted.
- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/google@0.10.1

### Patch Changes

- 7125576: Normalize duplicate JSON Schema `required` entries before
provider tool schemas are emitted.
- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/langchain@0.10.1

### Patch Changes

- 58bc93b: Refresh dependency ranges and lockfiles across the workspace.
- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/llamaindex@0.10.1

### Patch Changes

- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/mastra@0.10.2

### Patch Changes

- 58bc93b: Refresh dependency ranges and lockfiles across the workspace.
- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/openai@0.10.1

### Patch Changes

- 7125576: Normalize duplicate JSON Schema `required` entries before
provider tool schemas are emitted.
- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/openai-agents@0.10.1

### Patch Changes

- 7125576: Normalize duplicate JSON Schema `required` entries before
provider tool schemas are emitted.
- 58bc93b: Refresh dependency ranges and lockfiles across the workspace.
- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/vercel@0.11.1

### Patch Changes

- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
## @composio/slim@0.14.0

### Patch Changes

- 58bc93b: Refresh dependency ranges and lockfiles across the workspace.
- fa933a6: Fix the `homepage` links in these packages' `package.json`.
They pointed at `github.com/ComposioHQ/composio/tree/main/...`, but the
default branch is `next` and no `main` branch exists, so every link
404'd on npm and in editor tooltips. They now point at `tree/next/...`.
- Updated dependencies [58bc93b]
- Updated dependencies [fa933a6]
  - @composio/json-schema-to-zod@0.2.1
## @composio/cli-keyring@0.2.1

### Patch Changes

- 58bc93b: Refresh dependency ranges and lockfiles across the workspace.
## @composio/ts-builders@0.2.1

### Patch Changes

- 58bc93b: Refresh dependency ranges and lockfiles across the workspace.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-16 00:52:07 +04:00
Kshitij Jhunjhunwala 333d2296ed refactor(cli): defer setup telemetry 2026-07-14 20:07:30 -07:00
Kshitij Jhunjhunwala 4e68e65280 feat(cli): track plugin setup lifecycle 2026-07-14 19:35:13 -07:00
Kshitij Jhunjhunwala 20273ea66e fix(cli): honor if-present for unsupported hosts 2026-07-14 19:35:10 -07:00
Kshitij Jhunjhunwala 2b375b4855 fix(cli): handle plugin setup errors gracefully 2026-07-14 19:20:57 -07:00
Kshitij Jhunjhunwala 460f804dd1 fix(cli): continue setup with supported hosts 2026-07-14 19:06:52 -07:00
Kshitij Jhunjhunwala d004b2a309 fix(cli): handle incompatible Codex plugin commands 2026-07-14 18:59:01 -07:00
Kshitij Jhunjhunwala cb75ddd02b fix(cli): preserve unmanaged skills on uninstall 2026-07-14 16:13:39 -07:00
Kshitij Jhunjhunwala ca3b25d238 fix(cli): approve all setup mutations 2026-07-14 16:01:18 -07:00
Kshitij Jhunjhunwala c8b1ddeb64 fix(cli): confirm managed skill removal 2026-07-14 15:59:55 -07:00
Kshitij Jhunjhunwala 692dec45e7 fix(cli): preserve explicit setup PATH errors 2026-07-14 15:55:27 -07:00
Kshitij Jhunjhunwala f09141455c fix(cli): require all explicit setup targets 2026-07-14 15:54:54 -07:00
Kshitij Jhunjhunwala 82341643a4 feat(cli): add plugin uninstall flow 2026-07-14 15:54:54 -07:00
Kshitij Jhunjhunwala b148eda4e6 fix(cli): prompt only for missing plugins 2026-07-14 15:54:36 -07:00