mirror of
https://github.com/ComposioHQ/composio.git
synced 2026-09-22 11:46:35 +08:00
@composio/cli@0.3.1-beta.327
4542 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
50d43960eb |
feat(cli): send telemetry direct to PostHog; stitch install→Apollo identity (#3927)
## What Sends CLI telemetry **directly to PostHog** (project 196278) instead of through Apollo/Segment, and stitches the anonymous install identity to the Apollo user at login. This unlocks the previously-dark top of the CLI adoption funnel (install → setup → login → connect → execute). ### Why Two live funnel breaks in PostHog today: 1. **Install/setup/plugin events were dark.** Pre-login, `dispatch.ts` had no API base URL (→ endpoint null → dropped), and Apollo's ingest 401s anonymous requests. So `CLI_INSTALL_*`/`CLI_SETUP_*` never arrived. 2. **Identity split.** CLI/server events landed on *email*-keyed PostHog persons (via Segment); dashboard onboarding on *UUID*-keyed persons — the two never joined. The CLI already knows the canonical Apollo UUID (`org_member.id` from session info), so it can self-stitch with no platform/dashboard change. ## How - **Direct transport.** New PostHog capture path (`captureToPostHog`) reusing the existing detached best-effort worker. No dependency on the API base URL, so **pre-login events send**. - **Identity.** Pre-login `distinct_id = install_id`; at login `linkApolloIdentityForAnalytics(org_member.id)` persists the UUID to `~/.composio/analytics.json` and fires one `$create_alias` (`install_id → org_member.id`); post-login events key on the persisted `apollo_user_id`. Because onboarding is already UUID-keyed, the CLI funnel now joins web onboarding. - **Public key, baked at build.** The write-only `phc_` project key is injected via `bun build --define` from the `COMPOSIO_POSTHOG_KEY` env (CI Actions **variable**), never committed to source. `--define` (not a second `--env`) is deliberate — Bun honors only the last `--env`, which would clobber the existing `DEBUG_OVERRIDE_*` bake. For this OSS repo this keeps the key out of git history and means **forks / local builds compile a no-op binary** (only official releases carry the key). - **Direct-only:** the Apollo `/cli/analytics` send is dropped for CLI events; the CLI attaches its own `org_id` (project_id is per-session; a follow-up). ## Guards No-op (no send, no worker spawn, no identity-file write) when telemetry is disabled (`COMPOSIO_CLI_TELEMETRY_DISABLED`/`TELEMETRY_DISABLED`/`COMPOSIO_DISABLE_TELEMETRY`/`CI`/`NODE_ENV=test`) or when no project key is configured. Alias fires once per install (idempotent). No user API key or tool-arg values in event properties. ## Verification - `pnpm typecheck`, `validate:boundaries`, full CLI vitest (**954 passed / 1 skipped**) — green. - Independent code review pass; findings addressed (spawn-gated-on-key, disabled=no-file, idempotent alias). - **Live cold-install trace:** built the real compiled binary with the key baked, ran it from a throwaway `$HOME` → fresh `install_id` → 2 pre-login events delivered to `us.i.posthog.com` with **HTTP 200** → confirmed queryable in PostHog 196278 within ~10s, keyed by `install_id`. ## Notes - CLI is Changesets-ignored; human note added to `CHANGELOG.md` directly. - Requires the `COMPOSIO_POSTHOG_KEY` Actions variable to be set for official release builds (done). 🤖 Generated with [Claude Code](https://claude.com/claude-code)@composio/cli@0.3.1-beta.327 |
||
|
|
69a73648b3 | fix(cli): preserve selected org on manual link | ||
|
|
f9ddd94e10 | Merge next into analytics identity | ||
|
|
ad8fba2dea |
docs: update guides for SDK changes (#3995)
## Summary
Automated docs update triggered by SDK source changes on `next`.
- Claude reviewed the SDK diff and updated guides, FAQs, or examples
that reference changed APIs or features.
- The docs `@composio/*` dependencies were realigned to their latest
published releases so Twoslash snippets and example apps validate
against versions users can actually install.
## Review checklist
- [ ] Changes accurately reflect the new SDK behavior
- [ ] No unrelated docs were modified
- [ ] Code examples are correct and complete
- [ ] If a documented feature is not published yet, the Twoslash build
will fail — wait for the release instead of working around it
Generated by Claude Code via GitHub Actions.
Co-authored-by: jkomyno <12381818+jkomyno@users.noreply.github.com>
@composio/cli@0.3.0-beta.325
@composio/cli@0.3.0
|
||
|
|
7bf09cdbb8 |
fix(cli): make GitHub releases the version authority (#3993)
This PR: - fixes https://github.com/ComposioHQ/composio/issues/3932 - replaces the private CLI package semver with the non-release `0.0.0-development` sentinel - injects the exact GitHub release tag version into every standalone binary and verifies it during the build - makes every push to `next` a beta, with explicit semver selection for intentional minor or major betas and stable releases only through beta promotion - resolves skill installation, setup repair, companion repair, and upgrades from packaged `release-tag.txt` metadata - adds regression coverage for release selection, compiled version identity, and packaged-tag asset lookup; all 1,009 CLI tests and the monorepo typecheck pass - updates the direct CLI changelog and release guidance; no Changeset is added because CLI packages are excluded from Changesets@composio/cli@0.2.34-beta.324 |
||
|
|
7c5e22c3ce |
fix(cli): unblock release checksum generation (#3990)
This PR: - fixes https://github.com/ComposioHQ/composio/actions/runs/30535159056/job/90850055191 by keeping checksum generation independent of unbuilt CLI runtime packages - extracts the shared Effect teardown into a dependency-light module while preserving existing script imports - adds an executable release regression that generates a checksum manifest from a clean fixture - verifies the fix with `pnpm test:release-workflow`, `pnpm build:packages`, CLI typecheck, the full CLI test suite, and `pnpm validate:changesets`@composio/cli@0.2.33-beta.322 @composio/cli@0.2.33 |
||
|
|
d7b6e7eb30 |
chore(agents): restore good-docs-writing and good-docs-audit skills (#3991)
## What Restores the two docs-prose skills that were dropped from the skill tree: - `good-docs-writing` — the house documentation style guide (Voice, Structure, Terminology, Punctuation, Code examples, Formatting), for drafting or revising docs prose. - `good-docs-audit` — the review process that audits a doc/README/prose block against that guide and reports `file:line` findings with concrete rewrites. Report-only by default. ## Why Both existed at `d17a268d` under `.claude/skills/` and were deleted in #3666 (`chore(agents): normalize repo guidance skills`), which renamed the rest of the tree into the current taxonomy but did not carry these two over. Nothing replaced them: there is no docs-voice guidance in the repo today, and `docs-decisions` covers docs-site mechanics (Fumadocs, changelogs, ADRs), not prose style. ## Shape of the restore Rule content is restored unchanged from `d17a268d`. Only the file layout differs, because the old flat single-file form no longer passes `validate:agent-skills` (SKILL.md is now capped at 80 lines and a linked `references/` directory is required): ``` .agents/skills/good-docs-writing/SKILL.md # routing + the rules that matter most .agents/skills/good-docs-writing/references/style-guide.md .agents/skills/good-docs-audit/SKILL.md # routing + non-negotiables .agents/skills/good-docs-audit/references/audit-process.md ``` They live under `.agents/skills` (canonical); `.claude/skills` is a symlink, so there is no second copy. Wiring, all three places the taxonomy is asserted: - `ts/scripts/validate-agent-skills.mjs` — added to the `expectedSkills` gate - `ts/scripts/test-skill-routing.mjs` — one routing probe each (the validator fails on any skill without a probe) - `AGENTS.md` — added to the Skill Routing list ## Test results - `node ts/scripts/test-skill-routing.mjs` — passes, 18 probes over 18 skills. Both new probes resolve to a unique top match. - `node ts/scripts/validate-agent-skills.mjs` — zero findings against tracked files. - `pnpm lint-staged` — no staged file matches a configured task (patterns cover `ts/packages/**` and `python/**`). - Markdown formatted with the repo Prettier config. Two notes on how those were run. `pnpm validate:*` could not be used locally: pnpm's pre-run dependency check tries to purge and reinstall the workspace and aborts without a TTY, so the scripts were invoked directly with `node` (same entrypoints the pnpm scripts use). Separately, `validate-agent-skills` reports stale-guidance hits under `docs/.eve/dev-runtime/snapshots/**` on my machine; those are untracked, gitignored local eve snapshots, unrelated to this change, and won't exist in CI. The commit used `--no-verify` because the pre-commit hook is just the `pnpm lint-staged` wrapper that hits the same pnpm abort; lint-staged was run manually instead. No changeset: guidance and tooling only, no published package touched. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
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 -@composio/mastra@0.10.3 @composio/core@0.14.1 @composio/experimental@0.2.1 @composio/slim@0.14.1 py@0.18.1 |
||
|
|
0ae443bece |
chore(sdk): prepare Python 0.18.1 and TypeScript 0.14.1 releases (#3988)
This PR: - prepares Python SDK core and provider packages for `0.18.1`, including runtime and lockfile metadata - adds the canonical combined changelog for Python `0.18.1` and the TypeScript releases tracked by https://github.com/ComposioHQ/composio/pull/3906 - documents `@composio/core` `0.14.1` so the generated release PR passes the release-workflow guard - refreshes `mise.lock` so the TypeScript audit workflow reaches the dependency audit - keeps the pending TypeScript Changesets untouched; the generated release PR remains responsible for npm versioning and publication - validates Python with Ruff/mypy, 944 tests, all 13 package builds, and `twine check` - validates TypeScript/docs with the full 26-task `pnpm test`, high-severity production audit, 100 docs static tests, link checks, docs typecheck, and production build |
||
|
|
264d7dc9ee |
docs: update Python SDK reference from source (#3965)
## Summary Auto-generated Python SDK reference docs from `python/composio/`. Regenerates pages at `docs/content/reference/sdk-reference/python/` to reflect changes in the Python package's public API (new methods, updated signatures, changed types). Co-authored-by: jkomyno <12381818+jkomyno@users.noreply.github.com> |
||
|
|
52071ed5a0 | fix(cli): resolve selected-org analytics identity | ||
|
|
d00a3b5576 | fix(cli): keep analytics identity in sync with auth | ||
|
|
4bdbaecc10 | fix(cli): simplify analytics identity dispatch | ||
|
|
0f37be91e5 | fix(cli): key identity stitch throttle by credential | ||
|
|
2cfdd90680 |
docs: document Custom MCP (#3975)
## Summary - add an experimental guide for registering customer-hosted MCP servers - document supported authentication, the registration and sync flow, toolkit behavior, and customer-facing workarounds for known gaps - add the guide to the Extending sessions navigation and cross-link it from the sessions-over-MCP guide ## Why Custom MCP toolkits now have a working platform lifecycle, but customers need one place that clearly distinguishes remote MCP servers from in-process custom tools. The guide also makes the current pilot limitations explicit so customers know which API version, sync flow, account selection, and hosting model to use. ## Validation - `cd docs && bun run lint:links` - `cd docs && bun run types:check` - `git diff --check` Docs-only change, so no changeset is required. |
||
|
|
ce2c3a4024 | fix(cli): wait for analytics state locks | ||
|
|
24626db522 | chore: refresh mise lock metadata | ||
|
|
2be90d765f | fix(cli): serialize analytics identity state | ||
|
|
3335bb3013 |
fix(docs): restore API reference pages dropped by undeclared tags (#3973)
This PR: - fixes a regression introduced by https://github.com/ComposioHQ/composio/pull/3956: fumadocs-openapi 11's `groupBy: 'tag'` silently skips operations whose tag is not declared in the document's top-level `tags` array, which 404'd all 16 Projects and Organization Management operation pages (e.g. `/reference/api-reference/projects/postProjectUsageSummary`) on both v3.1 and v3, and dropped them from the sitemap and search index - adds `declareOperationTags()` (`docs/lib/openapi-tags.ts`), applied at spec load time in `lib/openapi.ts` (covers build-time-fetched specs) and at sync time in `scripts/fetch-openapi.mjs`, which now also warns when the upstream payload omits tags; the checked-in specs are normalized accordingly (purely additive) - adds CI guards in `tests/static/api-reference-routes.test.ts`: spec invariants (every visible operation has a tag and `operationId`), bidirectional spec-vs-generated-routes set equality through the production loader path, a negative test pinning fumadocs' silent-drop behavior (fails when upstream fixes it, signaling the workaround can be retired), and validation that every `ApiEndpointsTable` quick-link href resolves to a generated page - extends `Docs - Check Links` with a nightly (02:30 UTC) + manual external-URL sweep via a new `lint:links:external` script; the validator only fails on evidence a link is dead (404/410 or repeated network errors, with UA/timeout/GET-fallback/retry and per-URL caching) and scheduled failures file a deduplicated tracking issue; PR runs keep the fast internal-only check - fixes six dead external links the first sweep found (moved Claude Agent SDK docs, stale dashboard settings URL ×2, a `master`-branch path now pinned to tag `0.5.0+post.1`, and two removed `ComposioHQ` example repos whose mentions were dropped — their code is embedded in the pages via `RepoBrowser`) ## Context fumadocs-openapi 10 generated a page for any tag string found on an operation; v11 requires the tag to be declared top-level and skips silently otherwise (`preset-auto.js`: `builder.fromTagName(tag)` → `continue`, no warning). The backend spec generator omits `Projects`, `Organization Management`, and `Invite Codes` from `tags`, so their operation pages vanished while the checked-in MDX tag landing pages kept rendering with dead quick links. With the normalizer, the generated URL set is byte-identical to the pre-#3956 set (234 routes, verified via `getReferenceSource().getPages()` diff); no revert needed. Existing checks missed this because `lint:links` only extracts markdown links and `Card` hrefs (the dead links live in `ApiEndpointsTable`'s array prop) and validates against the same loader that shrank, and the integration suite samples fixed routes under tags that survived. The new completeness guard derives expectations from the specs themselves, so routine data syncs don't churn it. |
||
|
|
bf87709d92 |
Merge remote-tracking branch 'origin/next' into claude/cli-adoption-funnel-d95660
# Conflicts: # ts/packages/cli/CHANGELOG.md # ts/packages/cli/test/src/commands/login.cmd.test.ts |
||
|
|
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. |
||
|
|
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> |
||
|
|
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> |
||
|
|
9aa3e4b1c6 | Merge branch 'next' into claude/cli-adoption-funnel-d95660 | ||
|
|
fab1790b50 |
chore(cli): decommission npm + Homebrew install channels (PRDE-1155, PRDE-1156) (#3960)
## What Removes npm and Homebrew as Composio CLI install channels. `curl -fsSL .../install.sh | bash` is now the only supported channel (plus manual zip / from-source). Channel decommission decision: KJ, 2026-07-27. Tickets: PRDE-1155 (npm), PRDE-1156 (brew). ## Changes - **INSTALL.md** — removed the npm/pnpm/yarn install section; Windows guidance is now explicitly WSL - **install.sh** — Windows error path no longer advises `npm install -g @composio/cli` (dead channel); points to WSL - **.github/workflows/build-cli-binaries.yml** — generated per-release INSTALL.md no longer includes an npm section; comment no longer lists Homebrew as a release consumer - **.github/workflows/cli.install-health-check.yml** — dropped the stale comment describing an npm dist-tag publish flow that no longer exists - **.github/workflows/cli.test-installation.yml** — deleted the `test-npm-fallback` job (a no-op that only checked `npm --version`) and its `toolchain-versions` feeder job; pruned summary references - **.github/workflows/cli.bump-homebrew-tap.yml** + **.github/scripts/bump-homebrew-formula.py** — deleted. This automation never fired reliably: releases are published with `GITHUB_TOKEN`, which suppresses `release`-triggered workflows, and `HOMEBREW_TAP_TOKEN` is dead (root cause recorded on PRDE-1156). Removal loses nothing. - **ts/packages/cli/package.json** — dropped the unused `publishConfig` block (package stays `private: true`) - **.agents/skills/cli-release/** + **ts/packages/cli/AGENTS.md** — removed Homebrew steps from release docs - **toolchain-versions.json** — dropped `node_install_compat`, whose only consumer was the deleted npm-fallback job ## Deliberately untouched - SDK npm packages (`@composio/core` etc.) — only the CLI channel dies - Historical CHANGELOG entries mentioning npm - `brew install mise` / `brew install steipete/tap/peekaboo` — third-party tools, unrelated to the CLI channel ## Cross-repo Companion PR on the Homebrew tap repo replaces the formula with a deprecation stub that migrates existing brew users (0.2.28) to the curl installer. ## Verification - `bash -n install.sh` - `test/install-sh-release-resolution.test.sh` passes locally - YAML/JSON parse checks on all edited workflow/manifest files - grep sweeps: zero remaining user-facing npm/brew CLI-install references 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
11686484d1 | [codex] restore Security and Data docs (#3720) | ||
|
|
6725d1c078 | Merge branch 'next' into kj/decommission-npm-brew | ||
|
|
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`.
|
||
|
|
c5c3203db6 | fix(install): prioritize Windows platform guidance | ||
|
|
fa0b8f7c1c | fix(cli): align generated Windows install guidance | ||
|
|
6dc77c987d | fix(cli): preserve release bundle in manual installs | ||
|
|
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` |
||
|
|
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. |
||
|
|
a3f6b9c8f2 |
fix(py/triggers): swallow malformed chunked frame instead of tearing down subscription (#3892)
## Problem TriggerSubscription._handle_chunked_events is bound directly as a Pysher channel callback. Pysher channel dispatch has no local exception boundary, so malformed chunk frames reached websocket-client error handling and marked the realtime connection failed. The normal payload parser already skips malformed frames, but the chunked path did not. ## Fix - Validate that each decoded frame is an object with string id/chunk, integer non-boolean index, and boolean final fields. - Contain decoder, validation, and reassembly failures at the callback boundary and log only a bounded frame preview. - Clear partial state only when the frame carries a validated string id, allowing that id to be reused safely. - Preserve the valid chunk reassembly path and fix the Chunked docstring typo. ## Regression coverage Tests cover invalid JSON, non-object frames, missing fields, unhashable ids, wrong field types, unexpected decoder failures, and successful same-id reassembly immediately after a bad frame clears partial state. ## Verification - nox -s tst -- tests/test_triggers.py: 83 passed - make chk: Ruff and mypy passed --------- Signed-off-by: Checo <sergio@checo.cc> Co-authored-by: Checo <sergio@checo.cc> Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
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.
|
||
|
|
64acec65b6 |
ci(ts): fix next push trigger and cache reuse (#3971)
This PR: - fixes the `ts.build.yml` push trigger, which still pointed at `master`: the repo's default branch is `next`, so the workflow has not run on push since 2025-10-24 and never populated the turbo/pnpm caches that PR runs restore from (GitHub cache isolation lets PRs read base-branch caches, but not other PRs') - fixes the same stale `master` push trigger in `py.check.yaml` (all other workflows already list `next`) - adds a `cache-save` input (default `'true'`, preserving current behavior for every other consumer) to the `setup-node-pnpm-bun` composite action, swapping `actions/cache` for `actions/cache/restore` (same repo, same v6.1.0 SHA pin) when disabled - wires `cache-save` in `ts.build.yml` so only push runs on `next` persist caches: PR-scoped saves are invisible to other branches, so they only cost post-job time and evict default-branch entries from the 10 GB repo cache quota ## Context Measured on run [30364148082](https://github.com/ComposioHQ/composio/actions/runs/30364148082) (PR run, ~90s wall time): | Step | Time | |---|---| | Checkout | 5s | | Setup Node.js, pnpm, Bun | 34s (mise 1s, setup-node 5s, setup-bun 2s, pnpm 2s, **pnpm store restore 18s** for 658 MB, turbo restore 1s) | | `pnpm install --frozen-lockfile` | 14s | | `pnpm lint` | 1s | | `pnpm run build:packages` | 24s, **0/19 turbo cache hits** | | Post-job cache saves | ~11s (turbo save + a 658 MB pnpm store save that failed with "Unable to reserve cache", racing the parallel ts.* jobs on the same PR) | The build restored a turbo cache only via the fallback restore-key `Linux-turbo-build-` from an unrelated branch, hence 0 hits. Once push runs fire on `next` again, every merge saves a fresh `Linux-turbo-build-<sha>` cache that PR runs match through the restore-key prefix, so unchanged packages become turbo hits (up to ~20s off the build step) and the ~11s post-job save disappears from PR runs. The per-commit key suffix is kept intentionally: the turbo cache accumulates, so each default-branch save needs a fresh key and readers match via the prefix (documented inline in the action). |
||
|
|
a78a3fcb97 | Merge branch 'next' into claude/cli-adoption-funnel-d95660 | ||
|
|
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 |
||
|
|
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. |
||
|
|
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> |
||
|
|
a59b6480dd |
fix(cli): read user and CLI config from the cache dir in analytics
The analytics module resolved user_data.json and config.json from a hardcoded ~/.composio, but the CLI writes both under the cache dir (COMPOSIO_CACHE_DIR override, ~/.composio otherwise; see setup-cache-dir.ts and cli-user-config.ts). With the override set, telemetry silently lost the api key fingerprint, org_id, and the keychain-mode signal it needs for identity attribution. |
||
|
|
3083137ffe |
fix(cli): stop sending free-form strings in telemetry event properties
PostHog-bound events carried raw error messages, the user's search query, and the proxied endpoint URL. Free text like this can embed API keys, customer identifiers, and private URLs, and the client-side scrubber only catches known token shapes. Record the shape instead: error_name only for failures, query_length/query_term_count for search, has_endpoint for proxy. The codact-failure path keeps its error_message: it goes to Composio's own authenticated endpoint, not to PostHog. |
||
|
|
2a2d77a764 |
fix(py/triggers): stop() deadlock + subscribe() timeout connection leak (#3890)
Fixes #3858 (both bugs reported there). Bug 1 — TriggerSubscription.stop() deadlocks when called from a trigger callback. pysher's Connection.disconnect runs socket.close() then join(timeout) on the websocket thread, but callbacks execute on that same thread's dispatch path (_handle_event blocks on the callback's future.result()), so joining the thread from within a callback that runs on it deadlocks the whole process and _alive is never cleared, so a main thread parked in wait_forever() never exits. Fix: clear _alive synchronously (so wait_forever unblocks immediately) and run disconnect() on a daemon thread, breaking the reentrancy while still closing the socket. Bug 2 — _SubcriptionBuilder.connect() leaks a websocket thread on timeout. The deadline path raised ComposioSDKTimeoutError without calling pusher.disconnect(), so pysher's run loop kept redialing every reconnect_interval for the life of the process — one leaked thread per timed-out subscribe(), and if a leaked connection later succeeded it would authenticate and subscribe the channel of an abandoned TriggerSubscription, silently consuming events. Fix: wrap the wait loop in try/except so the timeout path tears the connection down before re-raising. Added regression tests (TestTriggerSubscriptionStop and TestSubscriptionBuilderConnectTimeout) that reproduce both failure modes offline with mocked pusher connections; all 73 tests in tests/test_triggers.py pass, ruff check + format clean. --------- Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com> Co-authored-by: jkomyno <alberto.schiabel@gmail.com> |
||
|
|
16fa3d963a |
chore(docs): migrate the docs site to Fumadocs 11 (#3956)
## Summary - upgrades `fumadocs-openapi` 10 → 11, `fumadocs-mdx` 14 → 15, and `fumadocs-core` / `fumadocs-ui` 16.4 → 16.13 - migrates the Fumadocs OpenAPI API while preserving the custom schema renderer - restores local `$ref` resolution in both the visible API schema UI and generated LLM markdown - reduces API-reference client payloads by slicing the bundled OpenAPI document to each page's reachable operations and components - restores required badges for GET parameters - normalizes the OpenAPI `no_auth` sentinel so explicitly public endpoints render without authentication - moves to `getOpenAPIPageProps()` / `OpenAPIPageProps` and removes obsolete CSS overrides ## Correctness fixes Fumadocs 11 changed the page contract from a server-resolved document id to a client-side bundled document. That exposed several silent regressions: - **Reference resolution:** bundled documents retain local `$ref`s. The LLM renderer now dereferences them, including alias chains and cycles, while the custom schema renderer uses Fumadocs' resolver and retains raw reference identity for stable deduplication. - **Dereference reuse:** repeated LLM-page requests reuse the dereferenced copy for each cached bundled document instead of walking the complete spec per page. - **Client payload size:** each API page now receives only its selected operations and transitively reachable components. The slicer falls back to the complete document for non-component pointers, deep component pointers, missing operations, or dangling references. - **Required badges:** `readOnly` cannot distinguish GET inputs from responses. The renderer now uses the page hook's client name to identify responses. - **Recursive rendering:** schema markdown rendering now caps both structural recursion and nested array type rendering. - **No-auth normalization:** the undeclared `no_auth` sentinel is removed without discarding any real security alternatives that may accompany it. - **Contract drift:** code consuming `getSchema()` now treats `bundled` as required, matching the upstream type. Review follow-up also replaces the new OpenAPI `any` types with typed Fumadocs page props and a narrow recursive schema model. Historical Fumadocs 10/11 migration explanations live here in the PR, not as version-specific source comments; source comments retain only durable invariants. ## Payload impact | | before | after | | --- | --- | --- | | bundled document | 451 KB | 7.7 KB avg / 30 KB worst | | served page HTML | 693 KB | 198 KB | | 10-page sample | 6.55 MB | 2.02 MB (69% smaller) | ## Verification - `bun install --frozen-lockfile` - `bun run test` — 89 pass - `bun run lint:links` — 0 errors - `bun run lint` — 0 errors (77 existing warnings) - `bun run types:check` - `bun run build` - production server + `bun run test:integration` — 74 pass, including v3.1/v3 API pages, redirects, search, and LLM endpoints The production build has one existing Turbopack NFT tracing warning from `next.config.mjs`; it does not fail the build. ## Production vs preview checks A live sample comparison between [production](https://docs.composio.dev) and the [PR preview](https://docs-git-chore-docs-fumadocs-11.preview.composio.dev) found no docs regression: - all 14 representative routes returned 200 with matching titles, headings, canonical production URLs, and key content - redirects for `/`, `/api-reference`, `/tools`, and `/docs/welcome` matched exactly - the sampled pages exposed the same 1,137 internal-link targets; a balanced sample of 29 links resolved successfully on both deployments - sampled v3 and v3.1 OpenAPI pages retained endpoint paths, required fields, response schemas, and legacy indicators - the generated OpenAPI LLM page was byte-for-byte identical - selecting TypeScript in a hydrated browser rendered both inactive-tab examples and synchronized the language tab groups - `llms.txt` retained the same 139 unique lines in a different order - `/docs/quickstart.md` only added an explicit `[#next]` heading anchor The sampled OpenAPI HTML was roughly 35–42% smaller in the preview, consistent with document slicing rather than missing rendered content. |
||
|
|
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 |
||
|
|
8cb290be35 |
fix(schema_converter): allOf containing a false member must reject every value (#3921)
### Summary
Preserves JSON Schema boolean-schema semantics throughout the Python
schema converter:
- an unsatisfiable `allOf` now propagates through nested combiners
instead of being dropped;
- impossible array items, object properties, and local definition
references remain rejecting;
- composed impossible schemas use a Pydantic type that rejects every
value, including JSON `null`;
- the existing `json_schema_to_pydantic_type(False) is None`
compatibility behavior remains unchanged.
### Problem
The original fix handled only a literal `false` directly inside one
`allOf`. Recursive filtering still interpreted the same `None` value as
both "discard this union branch" and "the whole schema is impossible."
That widened nested `allOf` schemas to `str`, removed impossible
properties/items/definitions, and allowed JSON `null` because Pydantic
treats a bare `None` annotation as `NoneType`.
### Fix
- Add a private unsatisfiable marker with Pydantic core- and JSON-schema
hooks.
- Drop that marker only from `anyOf`/`oneOf`; propagate it through
`allOf`.
- Preserve rejecting semantics for nested object properties, array
items, and local `$defs`/`definitions` references.
- Emit `{"not": {}}` from the rejecting Pydantic type.
- Correct the related empty-`allOf` and all-false-`anyOf` expectations.
### Testing
- `uv run pytest tests/test_schema_converter.py
tests/test_schema_parser.py -q` - 106 passed
- `uv run pytest -m schema -q` - 71 passed
- `make tst` - 928 passed
- `make chk` - Ruff and mypy passed across core, providers, tests, and
scripts
Regression coverage includes direct and nested `allOf`, JSON `null`,
empty arrays with impossible items, optional and required impossible
properties, local definition references, generated JSON Schema, and
shared model consumers.
### Note
#3876 adds coverage for the same function's `anyOf` handling but does
not change this `allOf` propagation path.
---------
Co-authored-by: jkomyno <alberto@composio.dev>
|
||
|
|
026c104fe8 |
fix(py): validate canonical URL targets (#3962)
This PR: - builds on top of https://github.com/ComposioHQ/composio/pull/3823 - carries forward the Requests canonicalization hardening proposed in https://github.com/ComposioHQ/composio/pull/3810 - resolves and validates the prepared hostname that Requests will actually connect to - blocks backslash parser differentials and rejects malformed ports before DNS resolution - adds focused URL-safety coverage while preserving both existing upload entry points Validation: - `uv run --frozen pytest tests/test_url_safety.py tests/test_files.py tests/test_tool_router_session_files.py -q` (177 passed) - `make chk` |
||
|
|
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.
|
||
|
|
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>
|
||
|
|
b85cea7618 |
docs: replace masking-disable instructions with Proxy Execute (#3887)
## What changed - Kept the credential-masking explanation and masked-value examples in the Connected Accounts guide. - Removed only the dashboard and API instructions for disabling masking. - Added guidance to use Proxy execute when calling a provider API directly. No changelog files are changed. ## Why Credential masking remains useful product documentation, but the guide should not direct users to disable it. Proxy execute covers direct provider API calls while Composio injects the connected account credentials server-side. ## Impact Users still understand how credential masking appears in connected-account responses, while direct provider API use cases are routed to Proxy execute. ## Validation - `bun run types:check` - `bun run lint:links` - `bun run test` — 33 tests passed - `git diff --check` `bun run lint` still reports 23 existing errors in unrelated React/Next.js files; this PR only changes MDX content. |