mirror of
https://github.com/ComposioHQ/composio.git
synced 2026-09-22 11:46:35 +08:00
@composio/claude-agent-sdk@0.11.0
334 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
02196de0b5 |
Release: update version (#4104)
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.16.0 ### Minor Changes - |
||
|
|
a2f6b9699b |
fix(json-schema): integrate Google object type normalization
Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
1af694baee | Merge branch 'next' into fix/cross-sdk-json-schema-object-conversion | ||
|
|
c625edc0f7 |
fix(core): reuse fetched schemas for provider execution (#4103)
Reuse fetched raw tool schemas for provider-wrapped execution while preserving modifier isolation, runtime validation, and unknown-slug fallback behavior. Refresh the Python 3.12.13 mise lock entries required by the Audit freshness gate. Co-authored-by: breezeFur <voidexl@outlook.com> |
||
|
|
13cba53b1d |
Release: update version (#4038)
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.15.0 ### Minor Changes - |
||
|
|
3fef432dc0 | fix: fail closed on invalid dynamic schemas | ||
|
|
051c8c5357 |
fix(telemetry): redact secrets inside JSON payloads (#4043)
## Summary
Telemetry error text is redacted before it leaves the process, but the
key/value rule only matches when the separator follows the key name
directly:
```
\b(authorization|api[-_]?key|...|pwd)\b(\s*[:=]+\s*)(["']?)([^\s"',}&]+)\3
```
In JSON — and in a Python `dict` repr — the key's own closing quote sits
between the name and the colon, so `{"api_key": "..."}` never matches
and the value is sent verbatim. That is the shape error messages usually
carry: an API error envelope, a rejected request body echoed back, or an
f-string interpolating a config dict. Both SDKs share the pattern and
both are affected.
The fix moves the optional quote into the separator group
(`(["']?\s*[:=]+\s*)`). The key name, separator, and quoting are all
preserved in the output; only the value is replaced.
Measured with `redact_sensitive_text` / `redactSensitiveText` directly,
before and after:
| input | before | after |
|---|---|---|
| `api_key=sk-1` | redacted | redacted |
| `api_key: "sk-1"` | redacted | redacted |
| `x-api-key: sk-live-hdr` | redacted | redacted |
| `{"api_key": "sk-live-abc"}` | **leaks** | `{"api_key": "[REDACTED]"}`
|
| `{"api_key":"sk-live-abc"}` | **leaks** | redacted |
| `{"api_key" : "sk-live-abc"}` | **leaks** | redacted |
| `{"refresh_token":"rt-abc.def-123"}` | **leaks** | redacted |
| `{"x-api-key":"sk-hdr","user":"bob"}` | **leaks** | redacted,
`"user":"bob"` kept |
| `{'client_secret': 'cs-live-abc'}` | **leaks** | redacted |
| `the password field is required` | untouched | untouched |
| `no separator here "api_key" and nothing else` | untouched | untouched
|
The end-to-end path in Python is `composio/core/models/base.py:60-61`,
which passes `str(e)` and `traceback.format_exc()` through the redactor
into the telemetry `error` field. A tool failure whose message
interpolates the rejected request body — `Error executing tool: request
body was rejected: {"toolkit": "GMAIL", "arguments": {"api_key": "...",
...}}` — leaked the customer's key today.
## Changes
- `python/composio/utils/redaction.py`,
`ts/packages/core/src/telemetry/redact.ts`: allow a quote between the
key name and the separator.
- Tests on both sides for JSON, minified JSON, spaced-colon JSON, dict
repr, the realistic serialized-payload shape, key/quote preservation,
and no-false-positive cases.
- Changeset for `@composio/core`.
## Type of change
- [x] Bug fix
## How Has This Been Tested?
Node 24.13.0 / pnpm 11.8.0 / Python 3.10.20 (uv), Windows.
- `uv run pytest tests/test_redaction.py` — 13 passed. With
`redaction.py` reverted and the new tests kept, 9 of them fail; the 4
that still pass are the pre-existing test plus the three
no-false-positive controls.
- `npx vitest run test/telemetry/redact.test.ts` in `ts/packages/core` —
10 passed. With `redact.ts` reverted, the 3 new cases fail.
- Full Python suite: 913 passed, same 4 failures as on `next` unchanged
(3 are `ModuleNotFoundError: composio_langchain` from providers not
installed in my env; 1 is `test_empty_name_safe_fails_at_write_time`,
which expects `IsADirectoryError` but Windows raises `PermissionError`
when opening a directory). Collection 944 → 956, exactly the 12 tests
added.
- Full `@composio/core` vitest: 798 passed / 2 failed, byte-identical to
the unchanged baseline (794 passed / same 2 failed — both Windows
path-and-symlink cases). 11 test files fail to load in both runs because
I installed with `--ignore-scripts`; the repo's `preinstall` hook
doesn't run in my shell.
- `ruff format --check` and `ruff check` clean on the two Python files;
`prettier --write` applied to the two TypeScript files.
## 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 behavior change
to document
- [x] I added tests or explain why not applicable
- [x] I added a changeset if this change affects published packages
## Additional context
This is defence-in-depth, so it stays deliberately narrow: same denylist
of key names, same value character class, no new rules. Bare-prefix
secrets (`sk-...`, `ghp_...`) and PEM blocks are still not matched by
either SDK — worth a separate look, but widening the pattern is a
different risk trade-off than closing a shape the rule already intends
to cover.
|
||
|
|
8d4bb3bf7e | chore: merge next into json schema conversion fix | ||
|
|
4a89ff112c | fix(json-schema): enforce dynamic object contracts | ||
|
|
063f03a72f | docs(changesets): explain each behavior change with a before and after | ||
|
|
ac6bbabd31 |
fix(claude-agent-sdk): register complete tool schemas
The provider registered tools through jsonSchemaToZodShape, which returns only
the per-property map. Root-level constraints -- `additionalProperties`, boolean
or schema-valued, and `patternProperties` -- are structurally unrepresentable in
a raw shape, so they were dropped before the Claude Agent SDK ever saw them:
free-form object arguments lost their content, and an unknown key was silently
stripped from tool input rather than rejected.
Registering the complete object schema fixes both. A tool with no input
parameters keeps its closed root explicitly, since a bare `properties: {}` is
now an open object.
Proven at the real SDK boundary rather than against a mocked `tool()`: the new
suite drives createSdkMcpServer over an in-memory MCP transport and asserts on
the advertised inputSchema and on real tools/call results.
|
||
|
|
cb6fe9bf64 |
chore(changesets): release the schema conversion fixes as minor bumps
Python's unknown-key rejection changes behavior for the langchain, langgraph, and crewai providers rather than only fixing a defect, so the release carries a minor bump across both SDKs instead of a patch. |
||
|
|
5e57815af1 |
fix(json-schema): accept and preserve dynamic object content across converters
Property-less objects (`{ type: "object" }`, `properties: {}`) were being
collapsed to a strict empty object by the Zod converter and given an implicit
`additionalProperties: false` by the Effect converter, so valid free-form
payloads such as METABASE_POST_API_CARD.dataset_query were rejected at the CLI
boundary. `ToolSchema.parse` separately dropped root `patternProperties` and
rejected a schema-valued root `additionalProperties`.
Dynamic keys are now routed to exactly the schemas that apply to them, and the
shared acceptance contract lives in one checked-in corpus with byte-identical
TypeScript and Python copies.
|
||
|
|
ecd0861741 |
fix(core): release unread response bodies (#4078)
This PR: - follows [Undici's recommendation](https://github.com/nodejs/undici#garbage-collection) to consume or cancel unread response bodies instead of leaving connection-resource cleanup to the garbage collector, which can increase connection pressure and reduce reuse - cancels every intermediate redirect body in `ssrfSafeFetch` before validating and following the next hop - cancels non-success response bodies in both URL-upload callers before preserving their existing errors - adds regression coverage for redirect ordering, redirect-budget exhaustion, and both caller error paths - adds a patch changeset for `@composio/core` - supersedes https://github.com/ComposioHQ/composio/pull/4018 and credits @pacocartones as co-author ## Verification - `pnpm --filter @composio/core test ssrfGuard fileUtils ToolRouterSessionFilesMount` (4 files, 58 tests passed) - `pnpm --filter @composio/core test` (47 files, 1,049 tests passed) - `pnpm --filter @composio/core typecheck` - `pnpm lint:packages` (0 errors; existing warnings only) - Prettier check on all touched files - `pnpm validate:changesets` Co-authored-by: pacocartones <pacocartones@users.noreply.github.com> |
||
|
|
2a6a051bc3 |
refactor(core): remove unused isNewerVersion helper (#4029)
This PR: - closes #4028 - removes the unused `isNewerVersion` helper from the internal version utility - avoids defining malformed-version behavior for a helper with no callers or package export - retains `next`'s tested npm version-check timeout implementation - adds a patch changeset for `@composio/core` - refreshes Python 3.12.13 artifact metadata in `mise.lock` after the upstream build was republished ## Verification - version utility tests: 2 passed - core typecheck: passed - changeset validation: passed - `mise 2026.5.18 lock --platform linux-x64,linux-arm64,macos-arm64,macos-x64`: clean --------- Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
86cc3cc399 | chore: merge next into dependabot remediation | ||
|
|
27fbb464f7 | Merge branch 'next' into fix/version-check-fetch-timeout | ||
|
|
1503786358 |
refactor(core): forbid explicit any and type JSON Schema recursively (#3968)
This PR: - is refreshed directly onto the current next branch now that #3966 and #3967 have merged; the PR contains only its intended type-safety work - enforces no explicit any through the standard .oxlintrc.json, pnpm lint, and plain oxlint --fix in lint-staged; docs remain covered by their nested configuration - replaces the loose JSONSchemaProperty in @composio/core with a concrete recursive interface, removing explicit any reachable through Tool.inputParameters and Tool.outputParameters - includes a minor changeset documenting the type narrowing for properties, default, and enum consumers - adds a compile-only public-contract test for recursive schemas, known-key validation, extension keywords, and Tool reachability - removes explicit any from FileToolModifier, ts-builders, core and CLI tests, provider tests, and json-schema-to-zod tests - keeps TypeScript ESLint-parity rules enabled and closes the tool-router example lint/typecheck gap - makes no lockfile changes and no runtime behavior changes ## Context Final PR from the original #3958 split. The migration foundations are already on next via #3966 and #3967, so this branch no longer carries a stacked base commit. |
||
|
|
2ac6ad320a | fix(core): complete version check timeout guard | ||
|
|
e5c9adabc8 | chore(deps): migrate OpenAI runtime to v7 | ||
|
|
4ceaede87a | chore(deps): refresh runtime support dependencies | ||
|
|
51056129ee |
fix(core): detect filesystem case sensitivity for uploads (#4036)
This PR: - fixes #4024 - supersedes #4025 with target-filesystem detection instead of unconditional case folding - uses `is-fs-case-sensitive` on the nearest existing directory so mixed mounts and case-sensitive filesystem configurations are handled correctly - preserves exact matching on case-sensitive filesystems and falls back to conservative case-insensitive matching when detection fails - keeps Node-only detection behind `#platform` and mirrors its runtime dependency in `@composio/slim`, preserving the workerd-safe bundle and Slim import - adds focused Node, workerd, and matcher regressions plus an `@composio/core` patch changeset - verifies 1,044 core tests, five CLI upload tests, the Slim build/import smoke, typecheck, build, ATTW, publint, lint, formatting, frozen install, and changeset validation |
||
|
|
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 - |
||
|
|
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>
|
||
|
|
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`. |
||
|
|
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 --> |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 (
|
||
|
|
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 - |
||
|
|
2ef40ce6da |
docs: document TypeScript 0.14.0 release (#3849)
This PR: - documents TypeScript `@composio/core` `0.14.0`, prepared in https://github.com/ComposioHQ/composio/pull/3717 - calls out trigger behavior changes, provider compatibility, file-upload security, telemetry redaction, and tool-execution reliability - adds the missing `@composio/core` patch Changeset for the local-path fix merged in https://github.com/ComposioHQ/composio/pull/3766 - leaves the Python `0.18.0` changelog with https://github.com/ComposioHQ/composio/pull/3825 so the release and docs PRs do not add the same file - keeps the TypeScript candidate release in the canonical docs changelog before publishing After this PR merges into `next`, the Changesets release PR must regenerate before publishing. ## Validation - `pnpm test:release-workflow` - `pnpm changeset status --since=origin/next` - `bun run test` - `bun run lint:links` - `bun run types:check` - `bun run build` - `pnpm exec prettier --check docs/content/changelog/07-15-26-typescript-sdk-014.mdx .changeset/http-prefixed-local-paths.md` `bun run lint` remains red on 23 existing React and Next.js errors outside these files. |
||
|
|
fbcf08136d |
revert(core): defer v1 session alias removal (#3826)
This PR: - reverts https://github.com/ComposioHQ/composio/pull/3780 - restores the TypeScript `composio.create()` and `composio.use()` session aliases, with their runtime and type-test coverage - restores the prior deprecation state for `BaseProvider.wrapMcpServerResponse` - removes the pre-v1 breaking-change changeset so the release PR no longer advertises this removal The v1 API-freeze change should be recreated later as a draft PR and kept out of the merge queue until v1 is ready. |
||
|
|
d5c86d3d6b | fix(release): keep core pre-v1 | ||
|
|
fa933a6f9b |
fix(ts): point published package homepage links at the next branch (#3821)
```markdown The `homepage` field in every published TypeScript package points at `https://github.com/ComposioHQ/composio/tree/main/ts/packages/<pkg>#readme`. The repository's default branch is `next` and there is no `main` branch, and GitHub does not redirect `tree/<branch>` paths to the default branch when that branch does not exist. As a result each of these homepage links returns a 404 wherever it is surfaced (the package page on npmjs.com, editor/IntelliSense hovers, and `npm view <pkg> homepage`). This repoints each `homepage` to `tree/next/...` so the links resolve. The `repository.url` (plain `.git` URL) and `bugs.url` fields were already correct and are left untouched. The metadata was originally added in #3611; this only corrects the branch in the `homepage` URLs. Fixes # ## Changes - Update the `homepage` field from `tree/main/...` to `tree/next/...` in the 14 published packages that carry one: `@composio/core`, `@composio/slim`, `@composio/experimental`, `@composio/json-schema-to-zod`, and the providers `@composio/anthropic`, `@composio/claude-agent-sdk`, `@composio/cloudflare`, `@composio/google`, `@composio/langchain`, `@composio/llamaindex`, `@composio/mastra`, `@composio/openai`, `@composio/openai-agents`, `@composio/vercel`. - Add a Changeset (patch) covering those 14 published packages. ## Type of change - [x] Bug fix - [ ] New feature - [ ] Refactor/Chore - [ ] Documentation - [ ] Breaking change ## How Has This Been Tested? Metadata-only change, so there is no unit test to add. Verified with: - Confirmed the target branch: `git ls-remote --symref origin HEAD` reports `refs/heads/next`, and `git ls-remote --heads origin main` is empty (no `main`). - Confirmed the links resolve: `https://github.com/ComposioHQ/composio/tree/main/ts/packages/core` returns HTTP 404 while `.../tree/next/ts/packages/core` returns HTTP 200 (same for the provider packages). - All 14 edited `package.json` files still parse as valid JSON. - `pnpm exec prettier --check` on the 14 `package.json` files and the changeset: all pass. - `pnpm changeset status --since=origin/next` lists exactly these 14 packages bumping at `patch` (no missing or extra packages), so the changeset CI check is satisfied. Environment: node 26, pnpm 10.28.0. ## Screenshots (if applicable) ## Checklist - [x] I have read the Code of Conduct and this PR adheres to it - [x] I ran linters/tests locally and they passed - [ ] I updated documentation as needed - [x] I added tests or explain why not applicable - [x] I added a changeset if this change affects published packages ## Additional context The `homepage` URLs pin an explicit branch (`tree/<branch>`), so they will break again if the default branch is ever renamed. If you would prefer a branch-agnostic form (for example dropping the `tree/<branch>` segment so the URL tracks the default branch, or linking to the published README), I am happy to switch to that instead; I went with the smallest correct fix of pointing at the actual default branch. ``` Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com> |
||
|
|
71255765a5 |
fix(schema): normalize duplicate required entries (#3812)
## Summary - Normalize duplicate JSON Schema `required` entries at the shared `ToolSchema` ingress boundary. - Reapply normalization immediately before direct providers emit vendor schemas (Anthropic, OpenAI, OpenAI Responses, Google, Cloudflare, and OpenAI Agents). - Preserve literal `const`/`default`/`enum`/`examples` values, tolerate aliasing safely, and fail predictably on pathological nesting. - Document the provider-boundary rule in `ts/packages/providers/AGENTS.md`. Co-authored-by: @MatinGathani ## Verification - `pnpm changeset status` - `pnpm --filter @composio/core build` - Focused core and affected-provider Vitest suites (1,151 tests) - `pnpm typecheck` - `pnpm lint` (passes with 71 pre-existing warnings) --------- Co-authored-by: MatinGathani <70268627+matingathani@users.noreply.github.com> |
||
|
|
0de52639c4 |
feat(core)!: remove bare session aliases; freeze MCP SPI (#3780)
## What and why This makes the intended v1 API cleanup real rather than postponing it to v2: - Remove the TypeScript root aliases `composio.create(...)` and `composio.use(...)`. Session creation and reuse now live only at `composio.sessions.create(...)` and `composio.sessions.use(...)`. This is a deliberate breaking change, reflected by a major changeset. - Retain `BaseProvider.wrapMcpServerResponse` as the stable v1 provider SPI. Its earlier deprecation pointed to a method that was never introduced. The scope is intentionally narrow: it does **not** remove unrelated deprecated APIs, and Python keeps its supported `Composio.create/use` API. The TypeScript docs, examples, providers, runtime fixtures, generated SDK reference, and API-reference indexes now use the namespaced TypeScript API. Historical changelog examples are left as history. ## Verification - `pnpm typecheck` - `pnpm --filter @composio/core test -- --run test/core/session.test.ts` (42 files, 1,018 tests) - `pnpm exec eslint ts/packages/core/src/composio.ts` - `pnpm --filter @composio/core generate:docs` - `cd docs && bun run generate:api-index` - `cd docs && bun run types:check` - `git diff --check` and targeted scans for removed TypeScript aliases I also attempted the affected Node and Cloudflare runtime E2E suites. They cannot initialize in this checkout without `COMPOSIO_API_KEY` (and, for Cloudflare, `COMPOSIO_BASE_URL` and `OPENAI_API_KEY`); they did not report a product assertion failure. |
||
|
|
a0f37a7f77 |
fix(core): SSRF guard on URL file inputs + telemetry secret redaction (#3779)
## What & why Two v1 non-negotiable gates from the [SDK road to v1](https://github.com/ComposioHQ/composio) plan are currently red. Both are self-contained and closed here. ### ① SSRF guard on URL file inputs — `@composio/core` `composio.files.upload(url)` and automatic file upload during tool execution did a **raw `fetch()`** on user-supplied URLs (`readFileContentFromURL`, `fileUtils.node.ts`) with no guard. Because URL inputs deliberately bypass the local-path allowlist, a caller — or an LLM-produced tool argument — could point the SDK at internal infrastructure (`http://169.254.169.254/…` cloud metadata, RFC1918 hosts) or a public URL that 3xx-redirects into internal space. New `ssrfGuard.node.ts`: - resolves the host and refuses **private, loopback, link-local (incl. `169.254.169.254`), CGNAT, TEST-NET, benchmarking, multicast, and reserved** ranges (IPv4 + IPv6, incl. IPv4-mapped / NAT64); - validates the **resolved address**, not the hostname string, so decimal/octal/hex IP obfuscation doesn't slip through; - rejects non-`http(s)` schemes; - follows redirects **manually** so every hop is re-validated; - blocked requests throw the new `ComposioBlockedInternalUrlError`. Behaviour for legitimate public URLs is unchanged. Node-only path. Known residual (documented in-file): a DNS-rebinding TOCTOU window between our lookup and `fetch`'s connect — closing it fully needs connect-time IP pinning (a custom dispatcher), out of scope here. ### ② Telemetry secret redaction — `@composio/core` Error telemetry shipped `error.message` / `error.stack` **verbatim** to `telemetry.composio.dev` with zero redaction. New `redact.ts` runs at the single chokepoint (`prepareAndSendErrorTelemetry`) and strips URL query strings, `Authorization` bearer/basic credentials, and secret-like `key=value` pairs (API keys, tokens, client secrets, passwords) before transport. ## Tests - `ssrfGuard.test.ts` — IP classification (v4/v6, mapped, CGNAT, metadata), scheme rejection, resolve-to-internal, "any resolved address internal" pinning bypass, redirect-into-internal, redirect budget. - `redact.test.ts` — URL query, bearer/basic, key/value secrets, quote preservation, benign text untouched. - Updated `fileUtils.test.ts` to mock DNS (hermetic) and expect `redirect: 'manual'`. - `tsc --noEmit` clean; lint clean. ## Notes - `patch` changeset included. - Adds `ComposioBlockedInternalUrlError` to the public error exports. |
||
|
|
b07fcada3f |
feat(experimental): add Eve provider and iMessage example (#3800)
This PR: - supersedes https://github.com/ComposioHQ/composio/pull/3642 and credits @shamsharoon for the original Eve provider and iMessage work - rebases the integration onto the current `next` branch and resolves its lockfile conflicts - publishes an isolated `@composio/experimental/eve` entry with the correct Eve 0.12+ peer contract - adds recoverable per-session discovery, native Eve context propagation, and durable approval policies for direct and batched side effects - centralizes circular-safe auth-link extraction across Eve and Pi and replaces dense conditional expressions with named control flow - hardens the iMessage example against `osascript` option injection, unsafe trigger scoping, and third-party prompt injection - preserves completed local-tool results when a mixed local/remote batch loses its remote transport - consolidates the example documentation onto shared components, documents direct model-provider credentials, and clarifies that the browsable source is not yet a standalone fixture - verifies the change with 999 core tests, 32 experimental tests, package builds/typechecks, packed peer-isolation smokes, and a production docs build --------- Co-authored-by: shams haroon <144290365+shamsharoon@users.noreply.github.com> Co-authored-by: shams haroon <shamsharoon7@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: shams haroon <shams@composio.dev> |
||
|
|
fc17c37bf9 |
fix(cli): enforce sensitive-file upload denylist (GHSA-hp3h-89pf-5q58) (#3763)
This PR: - fixes #3746 / GHSA-hp3h-89pf-5q58 — the CLI's tool file-upload path bypassed the sensitive-file denylist - **root cause:** the denylist was enforced at the *caller* layer, so `@composio/cli`'s duplicate upload path (`readFileFromDisk`) read and uploaded any local path a tool argument pointed at — `~/.ssh/id_rsa`, `~/.aws/credentials`, `.env`, etc. — enabling credential exfiltration via `composio execute` / `composio run` (incl. LLM-driven agents hit by prompt injection) - **unified fix:** one canonical guard, exported from `@composio/core` and enforced at the read primitive in every SDK - `core`: the guard now routes fs/path access through the internal `#platform` abstraction (adds a `realpathSync` method), so it carries **no static `node:*` imports** and is exported from the package root: `assertSafeFileUploadPath`, `isBlockedSensitiveFileUploadPath`, `BUILTIN_FILE_UPLOAD_PATH_DENY_SEGMENTS` - `cli`: `readFileFromDisk` calls the shared `assertSafeFileUploadPath` before `fs.readFile`; URLs and `File` objects are unaffected (matches core) - `python`: already routes all uploads through the guarded `FileUploadable.from_path` — no functional change; adds regression tests for parity - supersedes #3755, which exported the then-node-only guard from core's **main** `index.ts` and would have injected `node:fs` into the shared `dist/index.mjs` (the workerd/edge-light entry), likely breaking Cloudflare Workers / Vercel Edge - changesets: `@composio/core` minor (new public exports), `@composio/cli` patch (security fix) ## Verification - core: full suite passes (997), incl. new root-export + realpath-symlink cases - **edge bundle stays clean**: built `dist/index.mjs` has no static `node:*` imports and reaches the platform only via `#platform` - cli: new regression test drives the real `uploadToolInputFiles → readFileFromDisk → guard` chain — `~/.ssh/id_rsa` and `.env` throw `ComposioSensitiveFilePathBlockedError` before any `createPresignedURL` call; a normal file still uploads - python: `test_files.py` + sensitive-path suite pass (145), incl. 3 new `from_path` guard tests - `tsgo` typecheck + eslint clean for core and cli |
||
|
|
820abb9072 |
fix(sdk): resolve toolkit versions case-insensitively in both SDKs (#3762)
## Summary
Toolkit version maps are keyed by **normalized (lowercase) slugs** on
the *write* side — env vars (`COMPOSIO_TOOLKIT_VERSION_*`) and
user-supplied dicts both get their keys lowercased when the map is
built. But the *lookup* read the map with the **raw slug**. A pin
configured under a different casing (e.g. `{ "GitHub": "20250101_00" }`
or `COMPOSIO_TOOLKIT_VERSION_GITHUB` looked up as `GitHub`) therefore
missed the map and silently fell back to `"latest"`, which
`Tools.execute` then rejects with a version-required error — discarding
the user's explicit pin.
Rather than patch only the read side, this **centralizes the
normalization rule** into a single helper per SDK and routes **both**
the map-building (write) and lookup (read) paths through it, so the two
sides can never drift apart again:
- **Python** — `normalize_toolkit_slug()` in
`python/composio/utils/toolkit_version.py`
- **TypeScript** — `normalizeToolkitSlug()` in
`ts/packages/core/src/utils/toolkitVersion.ts`, imported by
`getToolkitVersionsFromEnv` in `sdk.ts`
Behavior is now identical across the two SDKs. This **supersedes
#3759**, which patched only the Python read side and left TS diverging.
## Backend verification
Confirmed against the backend (staging, **1403 toolkits across 15
pages**): every toolkit `slug` is already lowercase (the mixed-case
`name` field is display-only). So the *primary* execute path — which
resolves via `tool.toolkit.slug` — was never broken in practice. This
change:
- **hardens** the direct-util and mixed-case-config paths (a user
passing `{ "GitHub": "…" }` now resolves as expected), and
- **removes a latent cross-SDK divergence**: the TS suite previously
asserted `getToolkitVersion` was *case-sensitive* (`versions.test.ts`),
locking in the bug for an input the production code path can't actually
produce. That test is flipped to assert case-insensitive resolution.
## Tests
- **Python** (`python/tests/test_toolkit_version.py`): case-insensitive
lookup via `get_toolkit_versions` round-trip, and against a raw
pre-normalized map. `18 passed`.
- **TypeScript** (`ts/packages/core/test/core/versions.test.ts`):
replaced the case-sensitivity assertion with case-insensitive resolution
+ a write→read round-trip of a mixed-case user pin. Full core suite `997
passed`.
- Typecheck (`tsc --noEmit`) clean; `ruff` clean.
## Notes
- Changeset added: `@composio/core` patch.
- No public API change; the fix is behavioral (case-insensitive) plus an
internal shared helper.
|
||
|
|
58bc93b2d9 |
chore(deps): consolidate dependency updates (#3760)
This PR: - consolidates https://github.com/ComposioHQ/composio/pull/3733, https://github.com/ComposioHQ/composio/pull/3735 through https://github.com/ComposioHQ/composio/pull/3744, and https://github.com/ComposioHQ/composio/pull/3752 - refreshes the pnpm workspace dependencies under the existing `minimumReleaseAge` supply-chain gate - keeps latest compatible pins for ESLint 9, AI SDK 6, and Cloudflare workers types 4 where latest majors conflict with the current workspace - keeps zod-v3 runtime fixtures on `zod@3.25.76` while bumping the workspace catalog to zod 4 - bumps Python `composio-client` to `1.42.0` and refreshes `uv.lock` - updates the SHA-pinned Claude and Codex workflow actions from the Dependabot action group - adds a patch changeset for the versioned package manifests touched by dependency updates - verifies the rollup with frozen pnpm/uv locks, peer checks, audit threshold checks, typecheck, lint, builds, TypeScript tests, and Python `nox -s chk` ## Context `pnpm audit --prod --audit-level=moderate` passes. The remaining production audit item is a low `@ai-sdk/provider-utils` advisory through Mastra transitive dependencies; forcing it higher would require leaving the compatible stable Mastra dependency path. |
||
|
|
e78ed312c6 |
fix(core): run all parallel OpenAI tool calls from the first choice (#3726)
This PR: - supersedes https://github.com/ComposioHQ/composio/pull/3712, preserving the original commits by @serhiizghama (kept as author/co-author) - fixes `OpenAIProvider.handleToolCalls` to execute **every** tool call in an assistant message — it previously only read `tool_calls[0]`, so parallel tool calls (on by default) were dropped and their `tool_call_id`s went unanswered, failing the next request with the "must be followed by tool messages responding to each `tool_call_id`" error - limits execution to the **first choice** — with `n > 1` the previous loop ran each tool call once per choice and orphaned the `tool_call_id`s belonging to the alternative completions - aligns the Python SDK (`OpenAIProvider.handle_tool_calls`) with the same first-choice behavior so both SDKs stay in parity - fixes the stale `@composio/openai` test that modeled "multiple tool calls" as separate `n` choices rather than one message's `tool_calls` array - clarifies in the changeset that the calls run **sequentially**, in the order the model returned them (here "parallel" means the model issued several calls in one turn, not concurrent execution), so each `tool_call_id` is answered exactly once and deterministically - adds regression tests (TS + Python): both calls run for parallel tool calls in one message, and only the first choice runs when `n > 1` Full `@composio/core` (996) and `@composio/openai` suites green; Python `handle_tool_calls` tests, Ruff, and mypy clean; `tsc` clean. --------- Co-authored-by: serhiizghama <zmrser@gmail.com> |
||
|
|
a69c3edea3 |
fix(triggers): align create() user_id resolution across SDKs (#3723)
This PR: - builds on top of https://github.com/ComposioHQ/composio/pull/3714 — addresses its code-review findings - restores TS/Python parity on slug validation: Python `create()` now validates the slug up-front and raises a new `TriggerTypeNotFound`, mirroring the TS `ComposioTriggerTypeNotFoundError` - switches both SDKs to the native `user_id` upsert field — TS drops the `& { user_id }` intersection and Python drops the `extra_body` shim (both pinned clients already expose the field) - treats a blank/whitespace `userId` (TS) or `user_id`/`connected_account_id` (Python) as missing instead of forwarding it to the backend - documents the error-contract change (`create()` no longer throws `ComposioConnectedAccountNotFoundError` — that case now surfaces from the backend) in the changeset and `ts/docs/api/triggers.md`, and bumps the changeset from `patch` to `minor` - adds Python tests for the 2FA (both-provided) path, unknown-slug, and blank `userId`; tightens the TS auto-resolve assertion and adds an empty-`userId` case ## ⚠️ Release gating Since #3714, `triggers.create()` (both SDKs) relies on the backend resolving the trigger connection from `user_id` on upsert ([ComposioHQ/platform#10932](https://github.com/ComposioHQ/platform/pull/10932)). There is **no client-side fallback** — reintroducing one would undo #3714's intent, so this is a release-sequencing requirement, not a code change here. - Do not release `@composio/core` or the Python SDK until #10932 is live in all regions. - Self-hosted / on-prem deployments must be on a backend version that includes #10932. ## Review findings addressed | # | Finding | Resolution | |---|---------|------------| | P1-1 | Coupled to unreleased backend, no fallback | Documented dependency in changeset; flagged release-gating above (no fallback by design) | | P2-2 | Cross-SDK parity on slug validation | Kept `getType` in both; added Python `TriggerTypeNotFound` mirroring TS | | P2-3 | `user_id` bridge redundant in both SDKs | TS uses native field directly; Python uses `user_id=` kwarg | | P2-4 | Error-contract change shipped silently | Documented in changeset + docs `Throws` section; `minor` bump | | P2-5 | Python missing 2FA (both-provided) test | Added `test_create_with_user_id_and_connected_account_id` | | P3 6–13 | Cleanups | undefined-assertion, `none_to_omit`, docstring, `parsedBody.data`, redundant `else`, stale comments, blank-string guard | ## Verification - TS: `vitest` 59 passed, `typecheck` clean, `eslint`/`prettier` clean - Python: `pytest tests/test_triggers.py` 69 passed, `nox -s chk` (ruff + mypy) clean |
||
|
|
20a4711ee0 |
feat(triggers): resolve connection from user_id server-side in create() (#3714)
## What `triggers.create()` used to make an extra **`connectedAccounts.list()`** call to turn a `user_id` into a `connected_account_id` before calling upsert. The backend now resolves the connection from `user_id` directly (picks the first active connection for the user + the trigger's toolkit, parity with tool execution), so the SDK passes `user_id` straight through and drops the round-trip. - **TS** (`ts/packages/core`): removed the `connectedAccounts.list()` block in `Triggers.create()`. `user_id` was already forwarded to upsert (for 2FA); this just removes the now-redundant lookup. `getType` is kept for an early, clear "trigger type not found" error. `connected_account_id` is passed when the caller pins one. - **Python** (`python/composio`): dropped `_get_connected_account_for_user`; `user_id` is sent via `extra_body` and `connected_account_id` is omitted when not provided. ## Why The SDK only ever holds a `user_id`, so every `triggers.create()` paid an extra API call to find a connection. The backend change (ComposioHQ/platform#10932) makes trigger upsert accept `user_id` and auto-resolve — this PR consumes that. ## Behavior notes - When `connected_account_id` is omitted, the backend picks the first active connection ordered by `created_at desc`. The TS SDK's old `list()[0]` ordering was unspecified — selection is now deterministic and consistent with tool execution. - "No connected account found" / "connected account not found" now surface from the **backend** instead of a client-side pre-check. - When 2FA is enabled and `connected_account_id` is pinned, the backend validates that `user_id` owns it (unchanged; `user_id` was already forwarded). - The `extra_body` / `& { user_id }` bridges are still needed only until `@composio/client` / `composio_client` regenerate with `user_id` on the upsert type (the field is already in the OpenAPI contract). ## Tests - TS: `vitest run test/models/triggers.test.ts` → **58 passed** (create tests rewritten to assert no `connectedAccounts.list()` call + `user_id` forwarded). - Python: `pytest tests/test_triggers.py` → **66 passed** (same). ## Depends on Backend: ComposioHQ/platform#10932 (`feat(apollo): resolve trigger connection from user_id on upsert (PLEN-2580)`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
4c3a321550 |
fix(core): disable retries on non-idempotent tools.execute/proxyExecute (#3716)
This PR: - Closes https://github.com/ComposioHQ/composio/issues/3586 - disables client retries on `tools.execute` and `tools.proxyExecute` — the two non-idempotent writes — so a request that succeeds server-side but times out client-side is no longer silently replayed (the cause of duplicate `GMAIL_SEND_EMAIL` sends) - routes both through a sibling client built with `withOptions({ maxRetries: 0 })`; reads keep the client's default retries (`maxRetries: 2`) - caches that sibling per `Tools` instance (`clientWithoutRetries`) since `withOptions` constructs a fresh client and execute/proxy is the hottest path; mirrors the Python SDK's `client.without_retries` - adds regression tests asserting both paths route through `maxRetries: 0` and that the sibling is built once across multiple executes ## Context The original hang on attachments larger than ~25 KB was a backend defect and has already been fixed server-side. The remaining gap was a cross-SDK parity one: the Python SDK already disabled retries on these non-idempotent writes, but `@composio/core` still inherited the client default of 2 retries (3 attempts total), with no public knob to turn them off. This is the TypeScript parity port — scoped to exactly `tools.execute` / `tools.proxy`, leaving `toolRouter.session.execute` and all reads on their default retry behaviour, matching Python. |
||
|
|
156a1eb7b2 |
Release: update version (#3686)
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/vercel@0.11.0 ### Minor Changes - |
||
|
|
cddd66c3ce |
chore(ts): bump Hono, Mastra, and Chrome deps (#3691)
This PR: - bumps `hono`, `@mastra/core`, and `chrome-devtools-mcp` to their current latest published versions - routes the remaining Cloudflare Tool Router Hono consumer through the workspace catalog - updates the `@composio/mastra` `@mastra/core` peer range and adds a patch changeset - fixes `check-peer-deps` to resolve `semver` through the normal package dependency |
||
|
|
9ffc696a99 |
feat(vercel): support AI SDK v7 (#3683)
This PR:
- widens the `ai` peer dependency of `@composio/vercel` to `^6.0.0 ||
^7.0.0`, adding AI SDK v7 support and dropping the previously-untested
v5 arm
- adds Docker-based Node e2e compatibility suites for AI SDK v6 and v7
under `ts/e2e-tests/runtimes/node/vercel-ai-sdk-v{6,7}`, auto-discovered
by the existing e2e matrix (so PR CI exercises them)
- each installs the packed `@composio/vercel` tarball, typechecks the
provider against the target `ai` major, and asserts runtime tool
wrapping/execution (object inputs, JSON-string normalization via
`normalizeToolArguments`, `ToolSet` shape)
- the v7 suite additionally exercises v7's `ToolExecutionOptions` passed
to `tool.execute`
- ships a `minor` changeset (the peer-range narrowing removes a
previously-supported major)
- breaking:
- `ai@5` is no longer an accepted peer of `@composio/vercel`; v5
consumers should stay on the prior minor
|
||
|
|
605a7263ab |
Add tool router session deletion (#3679)
## Summary Explain the motivation and context for this change. Link to any related issues. Fixes # ## Changes - - ## Type of change - [ ] Bug fix - [ ] New feature - [ ] Refactor/Chore - [ ] Documentation - [ ] Breaking change ## How Has This Been Tested? Describe the tests you ran and instructions so reviewers can reproduce. Include any relevant config/versions. ## Screenshots (if applicable) ## Checklist - [ ] I have read the Code of Conduct and this PR adheres to it - [ ] I ran linters/tests locally and they passed - [ ] I updated documentation as needed - [ ] I added tests or explain why not applicable - [ ] I added a changeset if this change affects published packages ## Additional context --------- Co-authored-by: jkomyno <alberto@composio.dev> |
||
|
|
d102ab03ca |
Release: update version (#3676)
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.13.0 ### Minor Changes - |