Commit Graph

268 Commits

Author SHA1 Message Date
Abir Taheer f6df489c0b Release: update version (#3429)
> [!WARNING]
> ## ⚠️ Breaking change — `@composio/core@0.11.0` (minor bump, pre-1.0)
>
> The legacy in-memory custom-tools registry
**`composio.tools.createCustomTool(...)` has been removed** (changeset
`44e5458`). Because `@composio/core` is pre-1.0, this ships as a
**minor** bump per semver convention — but it **is breaking**: any code
calling `composio.tools.createCustomTool(...)` will fail to compile/run
after upgrading. Consumers pinned to `^0.10.0` will **not** auto-upgrade
to `0.11.0` and must opt in explicitly.

### Migration: use Tool Router custom tools

Custom tools are now standalone factories bound to a **Tool Router
session**, instead of being registered into an in-memory registry on the
`composio.tools` namespace.

**Before (removed):**

```ts
// ❌ No longer available in 0.11.0
const tool = composio.tools.createCustomTool({ /* ... */ });
```

**After — a standalone tool (no auth):**

```ts
import { Composio, experimental_createTool } from '@composio/core';
import { z } from 'zod';

const composio = new Composio();

const grep = experimental_createTool('GREP', {
  name: 'Grep Search',
  description: 'Search for patterns in files',
  inputParams: z.object({ pattern: z.string(), path: z.string() }),
  execute: async (input) => ({ matches: [] }),
});

// Bind the custom tool(s) to a Tool Router session
const session = await composio.create(userId, {
  experimental: { customTools: [grep] },
});
```

**After — a tool that extends a Composio toolkit (inherits that
toolkit's auth):**

```ts
const getImportantEmails = experimental_createTool('GET_IMPORTANT_EMAILS', {
  name: 'Get Important Emails',
  description: 'Fetch high-priority emails',
  extendsToolkit: 'gmail',
  inputParams: z.object({ limit: z.number().default(10) }),
  execute: async (input, ctx) => {
    // ctx.execute() returns the standard { data, error, logId } shape
    const result = await ctx.execute('GMAIL_SEARCH', { query: 'is:important' });
    return { emails: result.data };
  },
});

const session = await composio.create(userId, {
  experimental: { customTools: [getImportantEmails] },
});
```

**After — grouping related tools into a custom toolkit:**

```ts
import { experimental_createToolkit } from '@composio/core';

const devTools = experimental_createToolkit('DEV_TOOLS', {
  name: 'Dev Tools',
  description: 'Local developer utilities',
  tools: [grep], // tools here must NOT set `extendsToolkit`
});

const session = await composio.create(userId, {
  experimental: { customToolkits: [devTools] },
});
```

> Notes:
> - `experimental_createTool` / `experimental_createToolkit` are also
available from the dedicated `@composio/core/experimental` entry point.
> - Tool slugs are auto-prefixed with `LOCAL_` when exposed to the agent
(e.g. `GREP` → `LOCAL_GREP`; inside a toolkit, `DEV_TOOLS` + `GREP` →
`LOCAL_DEV_TOOLS_GREP`).
> - These APIs are intentionally namespaced `experimental_*` — they are
not yet stable and may change.

---

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.11.0

### Minor Changes

- a94715f: Forward `userId` when creating trigger instances so trigger
2FA flows can verify connected account ownership.
- 44e5458: Remove the legacy `composio.tools.createCustomTool(...)`
in-memory registry API. Use Tool Router custom tools via
`experimental_createTool`, `experimental_createToolkit`, and
`composio.create(..., { experimental: { customTools, customToolkits }
})` instead.

### Patch Changes

- 22a9171: Defer telemetry batch and error sends so instrumentation does
not wait for telemetry network requests before returning SDK results or
rethrowing SDK errors.
- 93b67e8: Fix automatic file upload/download substitution for file
schemas that accept either a single file or a list of files.

The file modifier now selects composed-schema branches by runtime value
shape, so `anyOf(file, array<file>)` uploads or downloads each file when
the tool receives a list while preserving existing single-file behavior.

- b69cef1: Tolerate dangling `$ref` pointers in tool schemas the
Composio API ships without a matching `$defs` entry. Some toolkits (e.g.
`GMAIL_FETCH_EMAILS`) emit `outputParameters` with `"$ref":
"#/$defs/FetchEmailsResponse"` while never declaring a top-level `$defs`
block. After the strict resolver shipped with the previous Mastra fix,
this caused `composio.tools.get(...)` to throw
`JsonSchemaRefResolutionError` upfront, making every Gmail / Slack /
Google-Calendar tool unusable through `MastraProvider`. The SDK now
degrades the unresolvable branch to a permissive object schema and
surfaces a single observability warning per `(toolSlug, ref)` pair
instead of crashing.
- `dereferenceJsonSchema` accepts a new optional second argument `{
onUnresolved?: 'throw' | 'sentinel'; onReplace?: (ref, reason) => void
}`. Default behavior is unchanged (`'throw'`) — first-party /
custom-tool schemas with a typo'd `$ref` still surface as a hard error.
Pass `'sentinel'` to replace unresolved branches with the cycle-break
sentinel (`{ type: 'object', additionalProperties: true }`) that the
resolver already uses for `$ref` cycles. The replaced sentinel carries a
default `description` hint so LLMs consuming the wrapped tool's schema
get an in-band signal that the branch is opaque; a caller-provided
`description` sibling overrides the default (Draft 2020-12
sibling-keyword merge). Safety caps (`MAX_REF_CHAIN_DEPTH`,
`MAX_NODE_DEPTH`) keep throwing in both modes. New
`UnresolvedRefStrategy`, `UnresolvedRefReason`, and
`DereferenceJsonSchemaOptions` type exports.
- `MastraProvider.wrapTool` opts both `inputParameters` and
`outputParameters` into `'sentinel'` mode and emits one `logger.warn`
per `(toolSlug, ref)` pair via the provider-scoped dedup `Set`.
User-controlled segments in the warning (`tool.slug`, `toolkit.slug`,
`ref`) are `JSON.stringify`d to neutralize embedded newlines / ANSI
escapes / control bytes that could otherwise forge log lines (CWE-117).
A matching one-shot telemetry event
(`composio.mastra.wrapTool.danglingRef`) fires next to the warn so the
Composio team has aggregate visibility into which toolkits are affected;
the event respects `COMPOSIO_DISABLE_TELEMETRY=true`.
- The `telemetry` instance from `@composio/core` is now publicly
re-exported alongside `logger`, so providers can emit aggregate signals
without reaching into the package's internals.
- Resolvable `$defs` / `definitions` continue to be inlined exactly as
before — no regression in the type-info preservation contract introduced
by the previous Mastra fix.

- 1ba66ca: Fix `tools.execute` (and `tools.get` / `tools.list`) failing
with a `ZodError` for every tool from an MCP-backed toolkit (e.g.
`granola_mcp`, `apify_mcp`, `tavily_mcp`).

MCP toolkits don't declare an output schema, and the Composio API
serializes that as `output_parameters: {}`. The SDK's `ParametersSchema`
required `{ type: 'object', properties: {...} }`, so
`transformToolCases` rejected the response and
`getRawComposioToolBySlug` — called by `execute` and the list path —
threw.

The SDK now normalizes empty (`{}`), `null`, and missing
`input_parameters` / `output_parameters` payloads to `undefined` before
validation. `outputParameters` was already declared `.optional()` in the
public `Tool` type, so this preserves the contract: "undefined means no
declared schema." Tools that _do_ declare a schema continue to be
validated strictly.

  No public API change; no toolkit allow-list.

- ce4b213: fix(providers): normalize string tool-call arguments across
all providers

Models occasionally emit tool-call arguments as a JSON string instead of
an
object (most visibly with `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI
SDK),
  which broke downstream validation with errors like
  `tool_use.input: Input should be a valid dictionary`.

`@composio/core` now exposes a single `normalizeToolArguments` helper,
and every
provider routes model-supplied arguments through it. Object payloads
pass
through unchanged, JSON strings are parsed, empty/`null` payloads become
`{}`,
  and anything that cannot resolve to an object throws a typed
`ComposioInvalidToolArgumentsError` instead of a raw `SyntaxError` or a
silently
forwarded malformed string. This replaces the inconsistent per-provider
guards
  that previously existed only in vercel, cloudflare and openai-agents.
## @composio/cli-local-tools@0.0.5

### Patch Changes

- Updated dependencies [22a9171]
- Updated dependencies [93b67e8]
- Updated dependencies [b69cef1]
- Updated dependencies [1ba66ca]
- Updated dependencies [a94715f]
- Updated dependencies [ce4b213]
- Updated dependencies [44e5458]
  - @composio/core@0.11.0
## @composio/anthropic@0.9.3

### Patch Changes

- ce4b213: fix(providers): normalize string tool-call arguments across
all providers

Models occasionally emit tool-call arguments as a JSON string instead of
an
object (most visibly with `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI
SDK),
  which broke downstream validation with errors like
  `tool_use.input: Input should be a valid dictionary`.

`@composio/core` now exposes a single `normalizeToolArguments` helper,
and every
provider routes model-supplied arguments through it. Object payloads
pass
through unchanged, JSON strings are parsed, empty/`null` payloads become
`{}`,
  and anything that cannot resolve to an object throws a typed
`ComposioInvalidToolArgumentsError` instead of a raw `SyntaxError` or a
silently
forwarded malformed string. This replaces the inconsistent per-provider
guards
  that previously existed only in vercel, cloudflare and openai-agents.
## @composio/claude-agent-sdk@0.9.3

### Patch Changes

- ce4b213: fix(providers): normalize string tool-call arguments across
all providers

Models occasionally emit tool-call arguments as a JSON string instead of
an
object (most visibly with `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI
SDK),
  which broke downstream validation with errors like
  `tool_use.input: Input should be a valid dictionary`.

`@composio/core` now exposes a single `normalizeToolArguments` helper,
and every
provider routes model-supplied arguments through it. Object payloads
pass
through unchanged, JSON strings are parsed, empty/`null` payloads become
`{}`,
  and anything that cannot resolve to an object throws a typed
`ComposioInvalidToolArgumentsError` instead of a raw `SyntaxError` or a
silently
forwarded malformed string. This replaces the inconsistent per-provider
guards
  that previously existed only in vercel, cloudflare and openai-agents.
## @composio/cloudflare@0.9.3

### Patch Changes

- ce4b213: fix(providers): normalize string tool-call arguments across
all providers

Models occasionally emit tool-call arguments as a JSON string instead of
an
object (most visibly with `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI
SDK),
  which broke downstream validation with errors like
  `tool_use.input: Input should be a valid dictionary`.

`@composio/core` now exposes a single `normalizeToolArguments` helper,
and every
provider routes model-supplied arguments through it. Object payloads
pass
through unchanged, JSON strings are parsed, empty/`null` payloads become
`{}`,
  and anything that cannot resolve to an object throws a typed
`ComposioInvalidToolArgumentsError` instead of a raw `SyntaxError` or a
silently
forwarded malformed string. This replaces the inconsistent per-provider
guards
  that previously existed only in vercel, cloudflare and openai-agents.
## @composio/google@0.9.3

### Patch Changes

- ce4b213: fix(providers): normalize string tool-call arguments across
all providers

Models occasionally emit tool-call arguments as a JSON string instead of
an
object (most visibly with `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI
SDK),
  which broke downstream validation with errors like
  `tool_use.input: Input should be a valid dictionary`.

`@composio/core` now exposes a single `normalizeToolArguments` helper,
and every
provider routes model-supplied arguments through it. Object payloads
pass
through unchanged, JSON strings are parsed, empty/`null` payloads become
`{}`,
  and anything that cannot resolve to an object throws a typed
`ComposioInvalidToolArgumentsError` instead of a raw `SyntaxError` or a
silently
forwarded malformed string. This replaces the inconsistent per-provider
guards
  that previously existed only in vercel, cloudflare and openai-agents.
## @composio/langchain@0.9.3

### Patch Changes

- ce4b213: fix(providers): normalize string tool-call arguments across
all providers

Models occasionally emit tool-call arguments as a JSON string instead of
an
object (most visibly with `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI
SDK),
  which broke downstream validation with errors like
  `tool_use.input: Input should be a valid dictionary`.

`@composio/core` now exposes a single `normalizeToolArguments` helper,
and every
provider routes model-supplied arguments through it. Object payloads
pass
through unchanged, JSON strings are parsed, empty/`null` payloads become
`{}`,
  and anything that cannot resolve to an object throws a typed
`ComposioInvalidToolArgumentsError` instead of a raw `SyntaxError` or a
silently
forwarded malformed string. This replaces the inconsistent per-provider
guards
  that previously existed only in vercel, cloudflare and openai-agents.
## @composio/llamaindex@0.9.3

### Patch Changes

- ce4b213: fix(providers): normalize string tool-call arguments across
all providers

Models occasionally emit tool-call arguments as a JSON string instead of
an
object (most visibly with `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI
SDK),
  which broke downstream validation with errors like
  `tool_use.input: Input should be a valid dictionary`.

`@composio/core` now exposes a single `normalizeToolArguments` helper,
and every
provider routes model-supplied arguments through it. Object payloads
pass
through unchanged, JSON strings are parsed, empty/`null` payloads become
`{}`,
  and anything that cannot resolve to an object throws a typed
`ComposioInvalidToolArgumentsError` instead of a raw `SyntaxError` or a
silently
forwarded malformed string. This replaces the inconsistent per-provider
guards
  that previously existed only in vercel, cloudflare and openai-agents.
## @composio/mastra@0.9.3

### Patch Changes

- b69cef1: Tolerate dangling `$ref` pointers in tool schemas the
Composio API ships without a matching `$defs` entry. Some toolkits (e.g.
`GMAIL_FETCH_EMAILS`) emit `outputParameters` with `"$ref":
"#/$defs/FetchEmailsResponse"` while never declaring a top-level `$defs`
block. After the strict resolver shipped with the previous Mastra fix,
this caused `composio.tools.get(...)` to throw
`JsonSchemaRefResolutionError` upfront, making every Gmail / Slack /
Google-Calendar tool unusable through `MastraProvider`. The SDK now
degrades the unresolvable branch to a permissive object schema and
surfaces a single observability warning per `(toolSlug, ref)` pair
instead of crashing.
- `dereferenceJsonSchema` accepts a new optional second argument `{
onUnresolved?: 'throw' | 'sentinel'; onReplace?: (ref, reason) => void
}`. Default behavior is unchanged (`'throw'`) — first-party /
custom-tool schemas with a typo'd `$ref` still surface as a hard error.
Pass `'sentinel'` to replace unresolved branches with the cycle-break
sentinel (`{ type: 'object', additionalProperties: true }`) that the
resolver already uses for `$ref` cycles. The replaced sentinel carries a
default `description` hint so LLMs consuming the wrapped tool's schema
get an in-band signal that the branch is opaque; a caller-provided
`description` sibling overrides the default (Draft 2020-12
sibling-keyword merge). Safety caps (`MAX_REF_CHAIN_DEPTH`,
`MAX_NODE_DEPTH`) keep throwing in both modes. New
`UnresolvedRefStrategy`, `UnresolvedRefReason`, and
`DereferenceJsonSchemaOptions` type exports.
- `MastraProvider.wrapTool` opts both `inputParameters` and
`outputParameters` into `'sentinel'` mode and emits one `logger.warn`
per `(toolSlug, ref)` pair via the provider-scoped dedup `Set`.
User-controlled segments in the warning (`tool.slug`, `toolkit.slug`,
`ref`) are `JSON.stringify`d to neutralize embedded newlines / ANSI
escapes / control bytes that could otherwise forge log lines (CWE-117).
A matching one-shot telemetry event
(`composio.mastra.wrapTool.danglingRef`) fires next to the warn so the
Composio team has aggregate visibility into which toolkits are affected;
the event respects `COMPOSIO_DISABLE_TELEMETRY=true`.
- The `telemetry` instance from `@composio/core` is now publicly
re-exported alongside `logger`, so providers can emit aggregate signals
without reaching into the package's internals.
- Resolvable `$defs` / `definitions` continue to be inlined exactly as
before — no regression in the type-info preservation contract introduced
by the previous Mastra fix.

- ce4b213: fix(providers): normalize string tool-call arguments across
all providers

Models occasionally emit tool-call arguments as a JSON string instead of
an
object (most visibly with `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI
SDK),
  which broke downstream validation with errors like
  `tool_use.input: Input should be a valid dictionary`.

`@composio/core` now exposes a single `normalizeToolArguments` helper,
and every
provider routes model-supplied arguments through it. Object payloads
pass
through unchanged, JSON strings are parsed, empty/`null` payloads become
`{}`,
  and anything that cannot resolve to an object throws a typed
`ComposioInvalidToolArgumentsError` instead of a raw `SyntaxError` or a
silently
forwarded malformed string. This replaces the inconsistent per-provider
guards
  that previously existed only in vercel, cloudflare and openai-agents.
## @composio/openai@0.9.3

### Patch Changes

- ce4b213: fix(providers): normalize string tool-call arguments across
all providers

Models occasionally emit tool-call arguments as a JSON string instead of
an
object (most visibly with `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI
SDK),
  which broke downstream validation with errors like
  `tool_use.input: Input should be a valid dictionary`.

`@composio/core` now exposes a single `normalizeToolArguments` helper,
and every
provider routes model-supplied arguments through it. Object payloads
pass
through unchanged, JSON strings are parsed, empty/`null` payloads become
`{}`,
  and anything that cannot resolve to an object throws a typed
`ComposioInvalidToolArgumentsError` instead of a raw `SyntaxError` or a
silently
forwarded malformed string. This replaces the inconsistent per-provider
guards
  that previously existed only in vercel, cloudflare and openai-agents.
## @composio/openai-agents@0.9.3

### Patch Changes

- ce4b213: fix(providers): normalize string tool-call arguments across
all providers

Models occasionally emit tool-call arguments as a JSON string instead of
an
object (most visibly with `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI
SDK),
  which broke downstream validation with errors like
  `tool_use.input: Input should be a valid dictionary`.

`@composio/core` now exposes a single `normalizeToolArguments` helper,
and every
provider routes model-supplied arguments through it. Object payloads
pass
through unchanged, JSON strings are parsed, empty/`null` payloads become
`{}`,
  and anything that cannot resolve to an object throws a typed
`ComposioInvalidToolArgumentsError` instead of a raw `SyntaxError` or a
silently
forwarded malformed string. This replaces the inconsistent per-provider
guards
  that previously existed only in vercel, cloudflare and openai-agents.
## @composio/vercel@0.9.3

### Patch Changes

- ce4b213: fix(providers): normalize string tool-call arguments across
all providers

Models occasionally emit tool-call arguments as a JSON string instead of
an
object (most visibly with `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI
SDK),
  which broke downstream validation with errors like
  `tool_use.input: Input should be a valid dictionary`.

`@composio/core` now exposes a single `normalizeToolArguments` helper,
and every
provider routes model-supplied arguments through it. Object payloads
pass
through unchanged, JSON strings are parsed, empty/`null` payloads become
`{}`,
  and anything that cannot resolve to an object throws a typed
`ComposioInvalidToolArgumentsError` instead of a raw `SyntaxError` or a
silently
forwarded malformed string. This replaces the inconsistent per-provider
guards
  that previously existed only in vercel, cloudflare and openai-agents.
## @composio/cli@0.2.32

### Patch Changes

- Updated dependencies [22a9171]
- Updated dependencies [93b67e8]
- Updated dependencies [b69cef1]
- Updated dependencies [1ba66ca]
- Updated dependencies [a94715f]
- Updated dependencies [ce4b213]
- Updated dependencies [44e5458]
  - @composio/core@0.11.0
  - @composio/cli-local-tools@0.0.5

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-19 17:12:38 +04:00
Alberto Schiabel eba6c6d7ef fix(release): keep provider peer bumps range-aware (#3582)
This PR:
- prevents the open Changesets release PR from promoting provider
packages to `1.0.0` when `@composio/core` gets a minor bump that still
satisfies their peer range
- points Changesets at `next`, matching the active TypeScript release
branch and workflow trigger
- enables `onlyUpdatePeerDependentsWhenOutOfRange` so peer dependents
only major-bump when the new dependency version leaves their declared
range
- adds a release workflow guard that runs Changesets against a fixture
and verifies `@composio/core@0.10.0` -> `0.11.0` keeps
`@composio/openai` on `0.9.3`
- leaves https://github.com/ComposioHQ/composio/pull/3429 to regenerate
after this lands on `next`
2026-06-16 16:04:25 +04:00
Anshu Garg a94715f577 feat(sdk): forward user_id on triggers.create for trigger 2FA (#3576)
## What

`triggers.create(userId/user_id, ...)` already accepts a user id and
uses it to resolve the connected account, but **dropped it** when
building the `trigger_instances.upsert` body. With [2FA for
triggers](https://linear.app/composio/issue/PLEN-2580) on the backend,
2FA-enabled projects need `user_id` on the upsert to verify the pinned
connected account belongs to the caller's user.

This forwards `user_id` to upsert in **both SDKs**:

- **TS** (`ts/packages/core/src/models/Triggers.ts`): adds `user_id` to
the upsert params via a `& { user_id?: string }` bridge.
- **Python** (`python/composio/core/models/triggers.py`): forwards
`user_id` via the client's supported `extra_body` escape hatch.

Both bridges exist only until `@composio/client` / `composio-client`
regenerate from the updated OpenAPI spec with a native `user_id` field —
drop them then.

## Safety

Backends without trigger 2FA ignore the extra field, so this is safe to
land ahead of / alongside the backend rollout (hermes PR #10635).

## Tests

- TS: upsert-body assertion added.
- Python: `tests/test_triggers.py::test_create_with_user_id` asserts
`extra_body == {"user_id": ...}`. 49 tests pass.

Closes part of PLEN-2580 (SDK follow-up to the agreed approach).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-06-16 13:10:37 +04:00
Alberto Schiabel ce4b213361 fix(providers): normalize string tool-call arguments across all providers (TS + Python) (#3514)
## Summary

Models — and some MCP transports — occasionally emit tool-call arguments
as a **JSON string instead of an object/dict**. The most visible trigger
is `COMPOSIO_MULTI_EXECUTE_TOOL` on the Vercel AI SDK, where streaming
fails with:

> `messages.3.content.1.tool_use.input: Input should be a valid
dictionary`

Until now only a handful of providers guarded against this, each with
its own slightly different inline check, leaving most providers
vulnerable and behaviour inconsistent across the SDK.

This PR centralizes the coercion into **one helper per language** and
routes **every** provider through it, in both the TypeScript and Python
SDKs.

Closes https://github.com/ComposioHQ/composio/issues/2406

## What changed

**TypeScript** — new `normalizeToolArguments` in `@composio/core`
(exported), used by every provider:
`vercel`, `cloudflare`, `openai-agents`, `openai` (ChatCompletions +
Responses), `anthropic`, `google`, `langchain`, `llamaindex`,
`claude-agent-sdk`, `mastra`.

**Python** — new `normalize_tool_arguments` in `composio.utils.shared`,
used by every provider:
`openai`, `openai-responses`, `anthropic`, `google`, `langchain`,
`langgraph`, `crewai`, `autogen`, `llamaindex`, `gemini`, `google-adk`,
`openai-agents`, `claude-agent-sdk`.

Shared semantics (identical in both languages):

| Input | Result |
| --- | --- |
| object / dict | returned unchanged |
| JSON string | parsed to object |
| empty / whitespace string | `{}` |
| `null` / `undefined` / `None` | `{}` |
| array, primitive, unparseable string, JSON that isn't an object |
**typed error** (`ComposioInvalidToolArgumentsError` / `InvalidParams`)
with the original parse error as cause |

The typed error replaces the previous grab-bag of behaviours: a raw
`SyntaxError`/`JSONDecodeError`, or — worse — silently forwarding a
malformed string downstream.

## Why this supersedes the open PRs

This consolidates and extends three open PRs that each addressed a slice
of the problem inconsistently. Their authors are credited as co-authors
on the relevant commits:

- **#3489** (LlamaIndex + Claude Agent SDK, TS) — @srijanarya
- **#3438** (Anthropic, Google, LangChain, TS) — @aptsalt
- **#3437** (Google ADK empty schemas + name fix, Python) —
@pragnyanramtha — its empty-`input_parameters` / missing-description
handling and the `gemini` → `google_adk` provider-name fix are folded in
here.

Compared to the three combined, this PR additionally: covers **every**
provider in **both** SDKs (not a subset of one), defines a single source
of truth instead of per-provider snippets, normalizes empty/`null`
payloads to `{}`, and raises an actionable typed error instead of
leaking `SyntaxError` or forwarding a bad string.

## Tests

- Exhaustive unit tests for both helpers (object passthrough,
JSON-string parse, empty/null → `{}`, malformed/non-object → typed
error).
- Per-provider regression tests across the touched TypeScript providers
(string path, malformed-string path, empty-payload path).
- Full `@composio/core` + touched-provider TS suites pass; `typecheck`
and `lint` clean. Python `ruff` clean and new test green.

## Changeset

Patch bump for all affected TypeScript packages (`@composio/core` + the
providers). Python follows its own versioning, so no changeset there.

---------

Co-authored-by: srijanarya <74669415+srijanarya@users.noreply.github.com>
Co-authored-by: Deepak Singh Kandari <deepaksinghkandari07@gmail.com>
Co-authored-by: Pragnyan Ramtha <pragnyanramtha@gmail.com>
2026-06-16 12:49:37 +04:00
Rahul Tarak 56534e2158 chore(cli): release 0.2.31 (#3538)
## What

Manual version bump for **`@composio/cli` → 0.2.31**, replacing the
broken changeset-bot flow.

- Bumps `ts/packages/cli/package.json` `0.2.30 → 0.2.31`
- Adds the `0.2.31` entry to `ts/packages/cli/CHANGELOG.md`
- Consumes (deletes) the 5 CLI changesets it covers:
  - `composio upgrade <version>` (#3428)
  - `x-cli-session-id` header (#3430)
  - refreshed approval fallback page (#3431)
  - LLM-friendly output (#3450)
  - fish completion paths (#3162) + 1-hour permission expiry (#3449)
- Leaves the `@composio/core` changesets untouched for the bot.

## Why manual

The changeset release bot (`ts.release.yml` → `changesets/action`) has
been failing on **`HttpError: Bad credentials`** on every run since
2026-06-04 — its `CI_BOT_TOKEN` secret is expired/invalid — so it can no
longer regenerate the "Release: update version" PR (#3429, stale since
2026-05-20).

`build-cli-binaries.yml` is healthy and independent of that token: on
merge to `next` it compares `ts/packages/cli/package.json` against the
previous commit, detects the version change, and builds + publishes the
stable **`@composio/cli@0.2.31`** GitHub release (cross-platform
binaries). The CLI is `"private": true`, so it ships only as the GitHub
binary release, not npm — this path fully covers it.

## Validated

The pinned-actions binary pipeline was already validated green
end-to-end (all 4 platform binaries built + full install-test matrix
passed) on a `build-beta` run. Local binary (built from this same
source) passed version/whoami/help, LLM-friendly stdout, `upgrade
<version>`, and the full Slack + `experimental_subAgent` integration
test.

## Follow-up (separate)

`CI_BOT_TOKEN` needs refreshing to restore the changeset bot (recommend
a GitHub App installation token over a personal PAT to avoid future
expiry). Tracked outside this PR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 02:18:21 -07:00
Rahul Tarak df104ec1fa chore: CLI 0.2.31 patch release (#3536)
## Changes since `@composio/cli@0.2.30`

### Features
- **feat(cli): `composio upgrade <version>`** (#3428) — install a
specific stable or beta release (`0.13.1`, `0.13.1-beta.42`, or full
tag). No-arg behavior unchanged; `--beta` still controls the
latest-prerelease case.
- **feat(cli): send `x-cli-session-id` header** (#3430) — attach the
per-cwd CLI session id to every client request so tool execution logs
can be grouped by CLI session.
- **feat(cli): refresh approval fallback page** (#3431) — browser
fallback tool-approval prompt restyled in the CLI landing-page visual
language and served directly from the loopback callback server; native
macOS sidecar remains preferred.

### Fixes
- **fix(cli): make output more LLM-friendly** (#3450) — route logger
output to stderr, gate human-only notices/prompts on interactive TTYs,
keep `composio run` helper/subagent logs off stdout.
- **fix(cli): permission allows expire after 1 hour** (#3449) — prompt
action relabeled "Allow for 1 hr"; cached allow decisions expire after
1h with stale-entry pruning.
- **fix: fish completion install paths** (#3162) — write completions to
`~/.config/fish/completions/composio.fish` and sanitize lines that could
break parsing.

## Changeset

A single patch bump (`@composio/cli` → 0.2.31) aggregated from the
pending changesets:
- `.changeset/cli-upgrade-version-arg.md`
- `.changeset/cli-session-id-header.md`
- `.changeset/cli-permission-fallback-design.md`
- `.changeset/quiet-llm-friendly-cli.md`
- `.changeset/cli-fish-completions-and-permission-expiry.md` (new —
covers #3162 and #3449)

## Testing

### Phase 1: CI Types/Lint
- N/A for this branch — `ts.typecheck` / `ts.test` / `ts.build` are
gated on `paths: ts/**`, and this branch only adds a `.changeset/*.md`.
Each underlying CLI PR (#3428/#3430/#3431/#3449/#3450/#3162) already
passed CI when merged to `next`.

### Phase 2: Local Binary Test ✅
Built from source (`pnpm turbo build` → `build:binary`) and tested:
- `composio version` → `0.2.30` ✅ · `composio whoami` → JSON ✅ ·
`composio --help` ✅
- `composio run 'console.log(...)'` → clean stdout (LLM-friendly stderr
routing confirmed, #3450) ✅
- `composio upgrade` `<version>` arg wired (`normalizeReleaseTag`,
#3428) ✅
- `SLACK_SEND_MESSAGE` input validation correctly rejects unknown keys
(LLM-friendly errors) ✅
- **Slack integration test** ✅ — `SLACK_FIND_CHANNELS` →
`SLACK_SEND_MESSAGE` → `SLACK_FETCH_CONVERSATION_HISTORY` →
`experimental_subAgent` (structured output via `z` schema +
`result.prompt()`) → summary posted to `#buzz-skill-based-cli-testing`.

### Phase 3: Bundled Binary (CI) ✅ pipeline unblocked
The CI binary pipeline was briefly blocked by a new org policy requiring
SHA-pinned actions — **already resolved on `next` by #3531**
(`chore(ci): pin GitHub Actions to SHAs`). A validation `build-beta` on
an identically-pinned branch confirmed the fix: "Resolve Release
Metadata" passes and binaries build/test green across platforms.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 02:10:07 -07:00
Alberto Schiabel 44e5458c35 feat(core): remove legacy TypeScript custom tools (#3509)
This PR is **part 2 of 3** splitting
https://github.com/ComposioHQ/composio/pull/3505 to make the removal of
the old 2025 custom tools easier to review. It carries the
**TypeScript** slice.

- builds on top of https://github.com/ComposioHQ/composio/pull/3508
(stacked — review/merge the Python slice first; this PR's base is
`remove-py-custom-tools`)
- removes the legacy `composio.tools.createCustomTool(...)` in-memory
registry: the plural `CustomTools` model, registry wiring, legacy-only
tests/examples/`ts/docs` pages, and stale error/type exports
- preserves the 2026 tool-router APIs: `experimental_createTool`,
`experimental_createToolkit`, `ToolRouter`, `ToolRouterSession`, and
inline custom-tool execution
- regenerates the TypeScript SDK reference so raw tool listing no longer
describes the removed local registry and points to session-scoped custom
tools
- adds a `@composio/core` minor changeset

## Notes

- The committed SDK reference is **verified self-consistent**: `pnpm
--filter @composio/core generate:docs` reproduces it with zero diff.
- This slice is a byte-identical subset of #3505 — the three split
branches recombine to that PR's exact tree. See #3505 for the original
verification logs (`typecheck`, `test`, `lint`, `cf-workers` e2e); CI
re-runs per PR.
2026-06-04 23:53:58 -07:00
Alberto Schiabel b69cef123f fix(mastra): tolerate dangling $ref in tool schemas [PLEN-2451] (#3400)
## Summary

Closes
[PLEN-2451](https://linear.app/composio/issue/PLEN-2451/mastraprovider-dollardefsdollarref-schema-is-dangling-at-the-api).
Fixes https://github.com/ComposioHQ/composio/issues/3307.

The Composio API ships some tools (e.g. `GMAIL_FETCH_EMAILS`) with
`outputParameters` like:

```jsonc
{
  "type": "object",
  "properties": {
    "data": { "$ref": "#/$defs/FetchEmailsResponse" },  // ← points into $defs
    "error": { "type": "string" },
    "successful": { "type": "boolean" }
  },
  "required": ["data", "successful"]
  // ← no `$defs` block anywhere
}
```

Since [PLEN-2244](https://linear.app/composio/issue/PLEN-2244)
introduced strict `dereferenceJsonSchema`, this caused
`composio.tools.get(...)` to throw `JsonSchemaRefResolutionError: Cannot
resolve $ref #/$defs/FetchEmailsResponse` upfront via
`MastraProvider.wrapTool`, making every Gmail / Slack / Google Calendar
tool unusable through the Mastra integration.

### Approach

Two-tier fix that keeps the architectural boundary from PLEN-2244 (only
the provider that owns the validation contract pre-processes the
schema):

1. **`@composio/core`** — add an additive opt-in to
`dereferenceJsonSchema`:
   ```ts
   dereferenceJsonSchema(schema, {
onUnresolved: 'sentinel', // default 'throw' unchanged
     onReplace: (ref, reason) => { /* observe */ },
   })
   ```
In `'sentinel'` mode the unresolvable branch is replaced with the same
`{ type: 'object', additionalProperties: true }` cycle-break sentinel
the resolver already uses for `$ref` cycles. Safety caps
(`MAX_REF_CHAIN_DEPTH`, `MAX_NODE_DEPTH`) keep throwing in both modes.
Default behavior is byte-for-byte unchanged — first-party / custom-tool
authors who typo a `$ref` still see a hard error.

2. **`@composio/mastra`** — opt `wrapTool` into sentinel mode for both
`inputParameters` and `outputParameters` and emit one `logger.warn` per
`(toolSlug, ref)` pair via a provider-scoped dedup `Set`. The warning
names the tool / toolkit / pointer and links to
https://github.com/ComposioHQ/composio/issues/3307.

### Why not centralize the dereferencing in `Tools.ts`?

- Mastra is the only provider that wraps `outputParameters`. Vercel /
OpenAI / Anthropic / Google bind tool input schemas to function-calling
APIs that ignore output shapes.
- Centralized dereferencing would silently mutate `tools.get()`'s public
output shape for callers iterating raw schemas.
- PLEN-2244 set the precedent — the provider that owns the validation
contract pre-processes its own schemas. This PR mirrors that boundary.

### API-side follow-up

The proper long-term fix is for the Composio API to emit a top-level
`$defs` block alongside any `$ref` it produces. That belongs in a
separate backend ticket — the SDK fix is intentionally independent so we
don't block on a backend rollout. The SDK now degrades gracefully
regardless of which API version it talks to.

## Test plan

Red-green TDD — every new test was confirmed red against the pre-fix
`dereferenceJsonSchema` / `MastraProvider`, then green after the fix.

- [x] **Core** — 9 new cases in
`ts/packages/core/test/utils/jsonSchema.test.ts` under `describe("with
onUnresolved: 'sentinel' lenient mode")` covering: missing-target,
missing-key, malformed-pointer (non-`#/` ref), mixed schema (resolvable
+ dangling siblings), `onReplace` reasons, safety-cap-still-throws
(chain & node depth), explicit `'throw'` matches default, `onReplace`
not called in strict mode.
- [x] **Mastra** — new file
`ts/packages/providers/mastra/test/mastra-dangling-defs.test.ts` (6
cases, un-mocked `@mastra/schema-compat`): replicates the exact
`GMAIL_FETCH_EMAILS.outputParameters` shape; verifies no throw, no
surviving `$ref` in the produced schema, exactly one warn per `(slug,
ref)` pair across repeat wraps, a separate warn per slug, symmetric
tolerance on `inputParameters`, and a regression guard for resolvable
`$defs` (PLEN-2244 contract).
- [x] **Regression** — all 908 core tests, all 39 existing
`mastra.test.ts` cases, and all 3 `mastra-ref.test.ts` cases continue to
pass. Full TS workspace test run: 14 packages green, 1322 tests total.
- [x] `pnpm typecheck` passes in both `@composio/core` and
`@composio/mastra`.
- [x] `pnpm lint` on touched files produces no new warnings.
- [ ] **Manual** (post-merge, against live API):
`composio.tools.get(userId, { tools: ['GMAIL_FETCH_EMAILS'] })` returns
a `MastraTool` whose `outputSchema` is defined; SDK logs exactly one
warning per process per `(slug, ref)` pair.

## Files

```
ts/packages/core/src/index.ts                                      # new type exports
ts/packages/core/src/utils/jsonSchema.ts                           # opts + lenient branch
ts/packages/core/test/utils/jsonSchema.test.ts                     # 9 new cases
ts/packages/providers/mastra/src/index.ts                          # opt-in + dedup warn
ts/packages/providers/mastra/test/mastra-dangling-defs.test.ts     # 6 new cases
.changeset/fix-mastra-dangling-defs-ref.md                         # core/mastra patch
plans/fix-mastra-dangling-defs-ref.md                              # design + acceptance log
```

---------

Co-authored-by: jkomyno <12381818+jkomyno@users.noreply.github.com>
2026-06-04 23:53:13 -07:00
Rahul Tarak 22a9171981 fix(core): defer telemetry sends (#3447)
## Summary
- Defer telemetry batch processing via microtask scheduling so reaching
the batch size no longer starts telemetry network work inline with SDK
calls.
- Defer SDK init/error telemetry sends and make `flush()` wait for both
deferred sends and pending batches.
- Remove stale blocking TODOs and add regression coverage for deferred
batch/error telemetry behavior.

Fixes #3442

## Tests
- `pnpm --filter @composio/json-schema-to-zod build`
- `pnpm --filter @composio/core typecheck`
- `pnpm --dir ts/packages/core exec vitest run
test/telemetry/TelemetryService.test.ts`
- `pnpm --filter @composio/core test`
- `pnpm exec eslint ts/packages/core/src/telemetry/BatchProcessor.ts
ts/packages/core/src/telemetry/Telemetry.ts
ts/packages/core/src/services/telemetry/TelemetryService.ts`

## CI
- GitHub checks are pending on PR creation; initial `gh pr checks 3447`
showed required jobs queued/in progress.
2026-05-26 14:29:05 +04:00
Rahul Tarak 1692bbee7a fix(cli): make output more LLM friendly (#3450)
## Summary
- Route Effect pretty logger output to stderr and share a TTY-based
interactive detector for human-only CLI UI.
- Gate upgrade notices and default browser login waiting on interactive
stdio; non-interactive `composio login` now emits session JSON and
exits.
- Stream `composio run` helper/subagent logs to stderr (or only to the
run log with `--logs-off`) without polluting stdout.
- Add a CLI patch changeset.

## Tests
- `pnpm -C ts/packages/cli exec vitest run
test/src/services/update-check.test.ts
test/src/commands/login.cmd.test.ts test/src/commands/run.cmd.test.ts`
- `pnpm --filter @composio/cli typecheck`
- `pnpm typecheck`
- `pnpm exec eslint ts/packages/cli/src/utils/stdio.ts
ts/packages/cli/src/services/terminal-ui.ts
ts/packages/cli/src/services/update-check.ts
ts/packages/cli/src/cli-main.ts
ts/packages/cli/src/commands/login.cmd.ts
ts/packages/cli/src/commands/run.cmd.ts
ts/packages/cli/src/services/run-helpers-runtime.ts --no-warn-ignored`
- `pnpm exec prettier --check ts/packages/cli/src/utils/stdio.ts
ts/packages/cli/src/services/terminal-ui.ts
ts/packages/cli/src/services/update-check.ts
ts/packages/cli/src/cli-main.ts
ts/packages/cli/src/commands/login.cmd.ts
ts/packages/cli/src/commands/run.cmd.ts
ts/packages/cli/src/services/run-helpers-runtime.ts
ts/packages/cli/test/src/services/update-check.test.ts
ts/packages/cli/test/src/commands/login.cmd.test.ts`

## Notes
- Initial targeted `pnpm --filter @composio/cli test -- ...` ran the
full CLI suite and failed before building `@composio/cli-keyring`; after
building/installing workspace deps, the targeted Vitest command above
passed.
2026-05-19 01:45:56 -07:00
Rahul Tarak eb51b923bb feat(cli): send x-cli-session-id header on every request (#3430)
## Summary

- Pulls the per-cwd CLI session id (already generated by
`getCurrentCwdSessionId()` and used as `cli_session_id` on analytics
events) and attaches it as the `x-cli-session-id` header on every
request from the composio client.
- Backend reads the header off `requestInfo`, threads it through
`enrichSessionInfo`, and writes `session_info.cli_session_id` on every
tool execution log row.

Companion hermes PR: ComposioHQ/hermes#9990

## Why

The CLI already tracks a stable per-cwd session id for analytics, but
tool execution logs are blind to it — there's no way to filter the logs
UI / retool back to a single CLI session. Wiring the header through lets
us group all tool calls from one CLI workflow together.

## Test plan

- [x] `pnpm --filter @composio/cli typecheck` clean
- [ ] Run `composio tools execute <slug>` against a build with the
hermes companion deployed, confirm ClickHouse tool log has
`session_info.cli_session_id`
- [ ] Confirm header is absent (and log field absent) when no cwd
session is cached

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 00:47:21 +04:00
Alberto Schiabel 1ba66ca045 fix(core): normalize empty output_parameters so MCP tools can execute [PLEN-2450] (#3397)
Closes
[PLEN-2450](https://linear.app/composio/issue/PLEN-2450/toolsexecute-fails-for-all-mcp-toolkits-strict-zod-validation-rejects)
· refs https://github.com/ComposioHQ/composio/issues/3354

## Summary

`composio.tools.execute('<MCP_TOOL>', ...)` currently throws a
`ZodError` for **every** tool from an MCP-backed toolkit (`granola_mcp`,
`apify_mcp`, `tavily_mcp`, …) on `@composio/core` 0.6.x–0.9.x. The
reporter on #3354 confirmed all three versions hit the identical
failure; users are working around it by dropping to `@composio/client`
and losing modifiers, custom-tool routing, version checks, and provider
wrapping.

**Root cause** (`ts/packages/core/src/models/Tools.ts` +
`src/types/tool.types.ts`):
- `tools.execute` and `tools.get` both run the API response through
`transformToolCases → ToolSchema.parse`.
- `ParametersSchema` requires `{ type: 'object', properties: {...} }`.
- The API returns `output_parameters: {}` for MCP tools that have no
declared output schema, so the validator throws.

**Fix:** a tiny module-private `normalizeRawToolParameters` helper
applied to both `input_parameters` and `output_parameters` inside
`transformToolCases`. `null` / `undefined` / `{}` all map to
`undefined`; everything else passes through to strict Zod validation.
This:

- Preserves the existing `outputParameters: undefined` ⇒ "no declared
schema" contract — `outputParameters` was already optional in the public
`Tool` type, so no public API change.
- Fixes both `tools.execute` and `tools.list` (single funnel covers both
paths).
- Doesn't relax `ParametersSchema` — tools that actually declare a
schema remain strictly validated.
- Forward-compatible for every present / future `*_mcp` toolkit with no
toolkit allow-list.

## Testing

Red → green confirmed locally. New `describe('MCP-shaped tool metadata
(PLEN-2450)')` block in `tools.test.ts` covers:

1. `getRawComposioToolBySlug` does not throw `ZodError` for
`output_parameters: {}`.
2. Empty `output_parameters` is normalized to `undefined` on the
transformed `Tool`.
3. Both-empty edge case (`input_parameters: {}` + `output_parameters:
{}`).
4. `tools.list` path also tolerates empty `output_parameters`.
5. End-to-end `tools.execute` succeeds against an MCP-shaped fixture.

All five tests **fail** on `next` without the helper (reproducing the
exact `ZodError` from the report) and **pass** with it. Full
`@composio/core` suite: **904 / 904 green**.

```bash
cd ts/packages/core
pnpm vitest run tools.test -t "MCP-shaped tool metadata"   # → PASS (5) FAIL (0)
pnpm vitest run                                            # → PASS (904) FAIL (0)
```

## Test plan

- [x] Red phase confirmed: 5 new tests fail with the exact `ZodError`
from the report.
- [x] Green phase confirmed: 5 new tests pass after the helper lands.
- [x] Full `@composio/core` test suite stays green (904 tests).
- [x] `pnpm typecheck` clean.
- [ ] Optional live sanity: build a tarball and run the reporter's
`repro.mjs` against a sandbox user (`pg-test-9f4d0df7-…`) — not
blocking; the mocked end-to-end test pins the same path.

## Follow-ups (out of scope)

- **Python SDK** — verify whether `composio` Python has the same
over-strict Pydantic check and apply the equivalent normalization if so.

> Note: not proposing an API-side change. `output_parameters: {}` is the
correct JSON Schema for "no declared schema" (the empty schema validates
anything); the bug was the SDK's Zod validator being over-strict, not
the API. The fix belongs at the SDK layer, where it now lives.

Co-authored-by: jkomyno <12381818+jkomyno@users.noreply.github.com>
2026-05-17 00:44:51 +04:00
Rahul Tarak 5555dee25c feat(cli): refresh approval fallback page (#3431)
## Summary
- Refresh the browser fallback tool-approval page with the Composio CLI
landing page visual language: light dithered background, mono terminal
card, and brand-blue actions.
- Serve the approval HTML directly from the temporary `127.0.0.1`
callback server instead of writing/opening a `file://` page, so approval
buttons call back to the active CLI process with relative local routes.
- Keep the native macOS sidecar as the preferred prompt when available;
the browser prompt remains the fallback when the sidecar is missing or
fails before returning a decision.
- Add a CLI patch changeset.

## Tests
- `pnpm exec eslint ts/packages/cli/src/services/tool-permissions.ts`
- `pnpm --filter @composio/cli typecheck:src`
- `git diff --check`

## Notes
- Initially `typecheck:src` failed before workspace package build
artifacts existed in the fresh worktree; after building required
workspace dependencies it passed.

---------

Co-authored-by: Malay Vasa <malayvasa@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 17:41:04 -07:00
Shrey Singla 93b67e8b2f fix(ts-sdk): handle file upload list unions (#3374)
## Summary

This updates the TypeScript SDK's automatic file upload/download
modifier to handle tool schemas that accept either a single file or a
list of files.

Tools can expose file inputs as a composed schema such as:

```json
{
  "anyOf": [
    { "type": "object", "file_uploadable": true },
    { "type": "array", "items": { "type": "object", "file_uploadable": true } }
  ]
}
```

When auto-upload is enabled, the SDK presents those file-uploadable
objects to models as file path strings. If the model supplies a list of
local paths, the old upload resolver selected the first file-bearing
branch, usually the single-file branch, and left the list unchanged.
That means the backend could receive local paths instead of staged `{
name, mimetype, s3key }` descriptors.

## Changes

- Adds runtime-shape-aware composed-schema selection for file
upload/download modifiers.
- Chooses the array branch for list values while preserving single-file
behavior for string/File values.
- Preserves the existing fallback behavior: if no runtime shape matches,
use the first file-bearing variant.
- Keeps `anyOf`, `oneOf`, and `allOf` on the same composed-schema path
used by the SDK today.
- Adds regression coverage for single-file vs multi-file unions, array
item unions, and download parity.
- Adds a patch changeset for `@composio/core`.

## Why

Some file-capable tool schemas are backward compatible at the API level
by accepting both a single file and multiple files. The TypeScript SDK
should transform a list of local paths into a list of staged file
descriptors, just as it already does for direct array schemas.

## Verification

- `pnpm --filter @composio/core exec prettier --check
src/utils/modifiers/FileToolModifier.node.ts
test/tools/fileModifiers.test.ts`

Blocked locally:

- `pnpm --filter @composio/core test -- fileModifiers.test.ts` fails
before collecting tests because this workspace cannot resolve the
unbuilt `@composio/json-schema-to-zod` package entry.
- `pnpm --filter @composio/core typecheck:tsc` is also blocked by
existing local generated-client/workspace dependency errors unrelated to
this patch.

---------

Co-authored-by: Zen <zen@composio.dev>
2026-05-14 13:38:27 +05:30
Rahul Tarak 2d52e7f7c0 feat(cli): support composio upgrade <version> for specific releases (#3428)
## Summary

- `composio upgrade` now accepts an optional `<version>` argument so
users can install any published CLI release — stable or beta — without
having to wait for the channel pointer to move.
- Accepts `0.13.1`, `v0.13.1`, `0.13.1-beta.42`, or the full
`@composio/cli@0.13.1` tag (normalized internally).
- When a version is provided, the "already on latest" guard is skipped
so the command also supports downgrades and reinstalls. `--beta`
continues to control the no-arg case.

## Examples

```bash
composio upgrade                       # latest stable (unchanged)
composio upgrade --beta                # latest beta (unchanged)
composio upgrade 0.13.1                # pin to a specific stable
composio upgrade 0.13.1-beta.42        # install a specific beta
composio upgrade @composio/cli@0.13.1  # full release tag also accepted
```

## Implementation

- `upgrade.cmd.ts`: added optional `version` Args, normalizes input to a
`@composio/cli@<semver>` release tag, passes it through to the service.
- `upgrade-binary.ts`: `upgrade`/`fetchLatestRelease` now accept a `tag`
option that fetches the GitHub release directly via
`/releases/tags/<tag>`, bypassing the channel resolver. When a tag is
explicit, the update-availability check is skipped.
- Falls back to the explicit tag for `installSkillSafe` when the upgrade
completes without returning one.

## Test plan

- [x] `pnpm typecheck` (CLI package) — clean
- [x] `pnpm test` in `ts/packages/cli` — 703 passed, 1 skipped, no
regressions in `upgrade-binary.test.ts`
- [ ] Manual: `composio upgrade 0.13.0` from a newer beta to confirm
downgrade path works against real GitHub releases
- [ ] Manual: `composio upgrade --beta` still resolves the latest beta
when no version is given

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:11:27 -07:00
Abir Taheer 878ac4d34b Release: update version (#3425)
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.10.0

### Minor Changes

- 42ebff3: feat(connected-accounts): namespace SHARED-connection surface
under `experimental`

Aligns the TypeScript SDK with the experimental wire shape used by
Shared Connections. The flat `accountType` / `aclConfigForShared`
options on `connectedAccounts.link()` and `session.authorize()` have
moved under a single `experimental` block, and
`connectedAccounts.updateAcl()` has moved off the class onto a top-level
`experimental_updateAcl(composio, id, opts)` export — same precedent as
`experimental_createTool` / `experimental_createToolkit`.

The `experimental` namespace is the signal that the shape may change in
future releases. Pinning a SHARED connection in a session config
(`connectedAccounts: { gmail: [...] }`) and direct execute by
`connectedAccountId` are unchanged — only the connection-create / patch
/ authorize surfaces are namespaced.

Also surfaces the `accountType` filter on `connectedAccounts.list()` so
SHARED connections can be listed without dropping to the raw client. The
wire keeps this as a flat query param, so the SDK keeps it flat too.

`@composio/client` bumped from `0.1.0-alpha.71` → `0.1.0-alpha.72` so
the generated typed client carries the `Experimental` namespaces for
`link.create`, `toolRouter.session.link`, and `connectedAccounts.patch`.

    Caller migration:

    ```typescript
    // before
    await composio.connectedAccounts.link('user_id', 'auth_config_id', {
      accountType: 'SHARED',
      aclConfigForShared: { allowAllUsers: true },
    });
await composio.connectedAccounts.updateAcl('ca_abc', { allowAllUsers:
true });
    await session.authorize('github', {
      accountType: 'SHARED',
      aclConfigForShared: { allowAllUsers: true },
    });

    // after
    await composio.connectedAccounts.link('user_id', 'auth_config_id', {
      experimental: {
        accountType: 'SHARED',
        aclConfigForShared: { allowAllUsers: true },
      },
    });
await experimental_updateAcl(composio, 'ca_abc', { allowAllUsers: true
});
    await session.authorize('github', {
      experimental: {
        accountType: 'SHARED',
        aclConfigForShared: { allowAllUsers: true },
      },
    });

    // new — list SHARED connections
    const shared = await composio.connectedAccounts.list({
      accountType: 'SHARED',
      userIds: ['user_creator'],
    });
    ```

## @composio/cli-local-tools@0.0.4

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0

## @composio/anthropic@1.0.0

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0

## @composio/claude-agent-sdk@1.0.0

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0

## @composio/cloudflare@1.0.0

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0

## @composio/google@1.0.0

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0

## @composio/langchain@1.0.0

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0

## @composio/llamaindex@1.0.0

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0

## @composio/mastra@1.0.0

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0

## @composio/openai@1.0.0

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0

## @composio/openai-agents@1.0.0

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0

## @composio/vercel@1.0.0

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0

## @composio/cli@0.2.30

### Patch Changes

-   Updated dependencies [42ebff3]
    -   @composio/core@0.10.0
    -   @composio/cli-local-tools@0.0.4

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: jkomyno <12381818+jkomyno@users.noreply.github.com>
2026-05-13 19:23:33 +02:00
venkat82 42ebff3f6b feat(connected-accounts): namespace SHARED-connection surface under experimental
Aligns the TypeScript SDK with the experimental wire shape used by
Shared Connections. The flat accountType / aclConfigForShared options
on connectedAccounts.link() and session.authorize() have moved under a
single experimental block, and connectedAccounts.updateAcl() has moved
off the class onto a top-level experimental_updateAcl(composio, id, opts)
export — same precedent as experimental_createTool /
experimental_createToolkit.

The experimental namespace is the signal that the shape may change in
future releases. Pinning a SHARED connection in a session config and
direct execute by connectedAccountId are unchanged — only the
connection-create / patch / authorize surfaces are namespaced.

Also surfaces the accountType filter on connectedAccounts.list() so
SHARED connections can be listed without dropping to the raw client.
The wire keeps this as a flat query param, so the SDK keeps it flat too.

@composio/client bumped from 0.1.0-alpha.71 -> 0.1.0-alpha.72 so the
generated typed client carries the Experimental namespaces for
link.create, toolRouter.session.link, and connectedAccounts.patch.

Caller migration:

  // before
  await composio.connectedAccounts.link('user_id', 'auth_config_id', {
    accountType: 'SHARED',
    aclConfigForShared: { allowAllUsers: true },
  });
  await composio.connectedAccounts.updateAcl('ca_abc', { allowAllUsers: true });
  await session.authorize('github', {
    accountType: 'SHARED',
    aclConfigForShared: { allowAllUsers: true },
  });

  // after
  await composio.connectedAccounts.link('user_id', 'auth_config_id', {
    experimental: {
      accountType: 'SHARED',
      aclConfigForShared: { allowAllUsers: true },
    },
  });
  await experimental_updateAcl(composio, 'ca_abc', { allowAllUsers: true });
  await session.authorize('github', {
    experimental: {
      accountType: 'SHARED',
      aclConfigForShared: { allowAllUsers: true },
    },
  });

  // new — list SHARED connections
  const shared = await composio.connectedAccounts.list({
    accountType: 'SHARED',
    userIds: ['user_creator'],
  });

Tests cover: experimental block forwarding (link + authorize), no-op
when omitted, empty-array preservation, typed error mapping for the
PRIVATE-with-ACL case, experimental_updateAcl body construction +
deny-list handling + ValidationError when no fields are provided, and
list(accountType: 'SHARED') flat-filter delegation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:16:56 +05:30
Abir Taheer 02723698b0 Release: update version (#3402)
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/cli-local-tools@0.0.3

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1

## @composio/core@0.9.1

### Patch Changes

- 84a3a07: Add `accountType` and per-user ACL support for SHARED
connected accounts.

- **`accountType` on create**: `composio.connectedAccounts.link(userId,
authConfigId, { accountType: 'SHARED' })` creates a SHARED connection.
Default remains `PRIVATE`. A SHARED connection can be used by other
`userId`s, but only when the connection is explicitly pinned in a
tool-router session's config and only when the requesting `userId`
passes the connection's ACL.
- **`accountType` on retrieve**: `get()` and `list()` responses now
include `accountType` (`'PRIVATE' | 'SHARED'`).
- **`aclConfigForShared` on create + retrieve**: per-user ACL block — `{
allowAllUsers, allowedUserIds, notAllowedUserIds }`. On responses the
field is `undefined` when the caller isn't authorised to see the ACL, so
callers can distinguish _"I can't see the ACL"_ from _"ACL is the
default deny-by-default state"_.
- **`updateAcl()` method** (new):
`composio.connectedAccounts.updateAcl(nanoid, { allowAllUsers,
allowedUserIds, notAllowedUserIds })` writes the ACL via `PATCH`. PATCH
semantics — omit a field to leave it unchanged; pass an empty array to
clear an allow/deny list. At least one field required. Calling on a
PRIVATE connection raises `ComposioAclOnlyForSharedError` (400).
- **`ToolRouterSession.authorize()` options gain `accountType` +
`aclConfigForShared`**, so a SHARED connection with an ACL can be
created in one call from inside a tool-router session.

    ACL resolution rule (deny wins):

    1.  requesting `userId` ∈ `notAllowedUserIds` → DENY
    2.  `allowAllUsers === true` → ALLOW
    3.  requesting `userId` ∈ `allowedUserIds` → ALLOW
    4.  otherwise → DENY (deny-by-default)

Limits: each ACL list accepts up to 1000 entries; each `userId` is
1..256 characters. The SDK enforces these caps at the input boundary.

    New error classes:

- `ComposioSharedAccessDeniedError` (403) — surfaces from direct
`connectedAccountId` execution paths when the requesting user fails the
ACL.
- `ComposioAclOnlyForSharedError` (400) — ACL fields sent on a PRIVATE
connection.
- `ComposioSharedConnectionNotAccessibleError` (400) — tool-router
session create / PATCH with a pinned SHARED connection the session user
cannot use.

No breaking changes. Existing `link()` callers without the new options
get a `PRIVATE` connection exactly as today; existing `get()` / `list()`
callers see new optional fields.

    The Python SDK mirror ships in a separate PR.

- c358ffa: Fix false-positive `initiate()` deprecation warning for
custom auth configs (SEC-339 follow-up).

`composio.connectedAccounts.initiate()` previously emitted a one-time
`console.warn` on every redirectable-OAuth response, regardless of
whether the auth config was Composio-managed (subject to the 2026-07-03
cutover) or custom (unaffected). The wording was conditional ("If this
auth config is Composio-managed…") so callers using their own OAuth apps
could ignore it, but the warning still printed and caused noise in logs.

Apollo already emits the SEC-339 `Deprecation` / `Sunset` / `Link
rel="deprecation"` headers (RFC 9745 / RFC 8594) **only** on the
retiring branch — managed + redirectable OAuth. The SDK now reads the
`Deprecation` header from the response (via `APIPromise.withResponse()`)
and gates the warning on its presence. Custom auth configs and non-OAuth
schemes get a clean response from the server and now stay silent in the
SDK as well.

- **Behavior change:** No warning is emitted for `initiate()` calls
against custom OAuth auth configs or non-OAuth schemes (API key, bearer,
basic). Managed-OAuth callers continue to get exactly one warning per
process, now with revised wording that points at the response's `Sunset`
header for the precise cutover date.
- **No public API change:** `initiate()` returns the same
`ConnectionRequest` shape and respects the same `allowMultiple` guard.
`ComposioLegacyConnectedAccountsEndpointRetiredError` continues to
surface from the 400 retired-path response.
- **Test scaffolding:** new mock helper `mockApiPromiseWithHeaders()` in
`connectedAccounts.test.ts` wraps a value as an `APIPromise`-shaped
thenable so the new tests can simulate apollo's header behavior.
Pre-existing initiate tests using `mockResolvedValueOnce` continue to
pass via the SDK's defensive fallback when `withResponse` is absent on
the mock.

    Python SDK gets the matching change in the same release train.

## @composio/anthropic@0.9.1

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1

## @composio/claude-agent-sdk@0.9.1

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1

## @composio/cloudflare@0.9.1

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1

## @composio/google@0.9.1

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1

## @composio/langchain@0.9.1

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1

## @composio/llamaindex@0.9.1

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1

## @composio/mastra@0.9.1

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1

## @composio/openai@0.9.1

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1

## @composio/openai-agents@0.9.1

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1

## @composio/vercel@0.9.1

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1

## @composio/cli@0.2.29

### Patch Changes

-   Updated dependencies [84a3a07]
-   Updated dependencies [c358ffa]
    -   @composio/core@0.9.1
    -   @composio/cli-local-tools@0.0.3

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-11 16:11:04 -07:00
Zen Agent 3b98ee0fc2 chore: change @composio/core version bump from minor to patch
Demote the connected-accounts-acl changeset from minor to patch, matching
PR #3403 by @abir-taheer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Co-authored-by: abir <abir@composio.dev>
2026-05-11 21:48:25 +00:00
venkat82 eba561f771 docs(connected-accounts): sanitize public docstrings — remove internal references
Addresses 4 review comments from @abir-taheer on #3392:

  - transformers/connectedAccounts.ts:21 — remove Hermes PR references
  - types/connectedAccounts.types.ts:79  — users don't belong to projects
  - types/connectedAccounts.types.ts:55  — drop backend storage detail
  - models/ToolRouterSession.ts:330      — remove internal information

Swept the full PR diff for similar leaks rather than just the four
flagged lines, since the same patterns repeat across the new files:

- Removed Hermes PR numbers (#9860 / #9882 / #9902) from inline
  comments and JSDoc in transformers, types, model, test describe block,
  and changeset.
- Removed "JSONB column on `connected_accounts`" storage detail.
- Reworded "any user_id in the project" / "users in the project" /
  "project member" / "project/org API key" — users don't belong to a
  project entity in Composio's data model, so the wording was
  misleading. Replaced with phrasing focused on the mechanism (pinning
  in a tool-router session, authorisation via creator or API key) and
  on the `userId` identifier.
- Removed "Backend response: 400 with code `ConnectedAccount_...`"
  blocks from error class JSDoc — surfacing the internal error code
  name leaked implementation details.
- Cleaned up the augmented `RawConnectedAccountResponseWithLabels`
  intersection — `account_type` and `acl_config_for_shared` are now on
  the regenerated `@composio/client@0.1.0-alpha.71` types, so the
  manual augmentation lines (which carried the Hermes refs) can be
  deleted entirely. Field access still works through the existing
  intersection used for `word_id` / `alias`.

Also tightened a pre-existing leak I introduced earlier in the PR's
own JSDoc on `update()` — dropped the "historical JSDoc mentioning
alias/credentials" callout since that's internal noise.

No behaviour changes — pure docstring / comment / changeset cleanup.

Verified: pnpm typecheck clean, 915 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 01:08:37 +05:30
venkat82 483af36070 feat(connected-accounts): bump @composio/client to alpha.71, drop casts, add tests + error mapping
Now that #77 in composio-base-ts merged and @composio/client@0.1.0-alpha.71 is
published with account_type + acl_config_for_shared in the typed surface,
all the forward-compat shims come out.

Removed:
- AclConfigWireShape, LinkCreateBodyWithAcl, ConnectedAccountPatchBodyWithAcl
  local types
- 3x `as unknown as Parameters<typeof X>[N]` casts in ConnectedAccounts.ts +
  ToolRouterSession.ts

Now uses the generated types directly:
- ConnectedAccountPatchParams, ConnectedAccountPatchResponse from
  @composio/client/resources/connected-accounts
- LinkCreateParams + LinkCreateParams.ACLConfigForShared from
  @composio/client/resources/link
- SessionLinkParams + SessionLinkParams.ACLConfigForShared from
  @composio/client/resources/tool-router/session/session

Review fixes from #3392 multi-agent review:

- P1-1: moved orphaned JSDoc back onto ComposioLegacyConnectedAccountsEndpoint-
  RetiredError (was stranded above ComposioSharedAccessDeniedError)
- P1-2: added empty-array footgun warning to updateAcl() JSDoc — passing
  `notAllowedUserIds: []` silently clears the deny list
- P1-3: changeset reconciled with PR body (Python ships as separate PR)
- P2-9: updateAcl() returns ConnectedAccountPatchResponse instead of void,
  matching sibling update()/refresh() ergonomics
- P2-11: clarifying JSDoc on update() pointing at updateAcl() and noting the
  pre-existing "alias/credentials" doc is inaccurate (this PR doesn't fix it)
- P3-12: callbackUrl/alias guards normalized to `!== undefined` (was a mix
  of `&&` and `!= null`) — empty strings now propagate correctly
- P3-17: refine-guard comment on UpdateConnectedAccountAclParamsSchema noting
  ZodEffects doesn't expose .shape

P2-6/P2-7: typed error mapping for ComposioAclOnlyForSharedError (400)
wired at three call sites:
- ConnectedAccounts.link()    — wraps existing
  ComposioFailedToCreateConnectedAccountLink catch
- ConnectedAccounts.updateAcl() — new catch with rethrow fallback
- ToolRouterSession.authorize() — new catch with rethrow fallback

Substring match on `'acl_config_for_shared is only valid on SHARED'` —
verified against apps/apollo/src/lib/connected_accounts/createConnectedAccount.ts
in hermes#9902. ComposioSharedAccessDeniedError and
ComposioSharedConnectionNotAccessibleError remain exported but not yet
thrown — they'll be wrapped when Tools.execute() and ToolRouterSession.create()
error-mapping lands in a follow-up.

Tests: 12 new behaviour tests in test/connectedAccounts/connectedAccounts.test.ts
covering link() ACL forwarding (5), updateAcl() body construction + empty-object
refine + AclOnlyForShared mapping (6), and link() error-mapping fallback (1).
911 tests pass (up from 899).

Verified: pnpm typecheck clean, pnpm build clean (publint + attw clean).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 23:55:13 +05:30
venkat82 752efd8863 refactor(connected-accounts): nest ACL under aclConfigForShared, match merged 9902 contract
The original draft built against 9902's PR description shape — three flat
top-level ACL fields (`allow_all_users`, `allowed_user_ids`,
`not_allowed_user_ids`). The merged design (deployed to staging now)
collapsed them into a single JSONB column and exposes them on the wire
as a nested `acl_config_for_shared` object. This commit reshapes the SDK
to match.

Wire contract changes verified against staging-backend.composio.dev:

  POST /connected_accounts/link        body: account_type, acl_config_for_shared
  POST /connected_accounts             body.connection.{account_type, acl_config_for_shared}
  PATCH /connected_accounts/{id}       body: alias, connection, acl_config_for_shared
  GET  /connected_accounts             item.{account_type, acl_config_for_shared?}
  GET  /connected_accounts/{id}        response.{account_type, acl_config_for_shared?}
  POST /tool_router/session/{id}/link  body: account_type, acl_config_for_shared

SDK changes:

- types: `ConnectedAccountAclSchema` → `ConnectedAccountAclConfigSchema`
  (input — all fields optional) + `ConnectedAccountAclConfigResponseSchema`
  (output — all fields populated when block is visible). Added per-user_id
  length cap (1..256) matching aclConfig.ts. `link()` options exposes
  `aclConfigForShared?` instead of three flat fields. Retrieve response
  exposes `aclConfigForShared?` (undefined when caller can't see ACL).

- model: `link()` and `updateAcl()` serialize the SDK's flat camelCase
  ACL params into the nested snake_case `acl_config_for_shared` wire
  block via a new `serializeAclConfigForWire` helper. Local body types
  updated to match.

- transformer: maps `acl_config_for_shared` (nested) → `aclConfigForShared`
  (nested camelCase). Forwards `undefined` when the block is absent.

- ToolRouterSession.authorize() options gain `accountType` +
  `aclConfigForShared` — the `/tool_router/session/{id}/link` endpoint
  accepts the same fields.

- changeset rewritten to document the nested shape.

Verified: pnpm typecheck clean, build clean, 899 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 18:52:55 +05:30
venkat82 84a3a074b3 feat(connected-accounts): surface accountType + per-user ACL on SHARED connections
Adds SDK wrappers for Hermes #9860, #9882, #9902 — three Apollo-side
features that previously had no high-level surface. Existing call sites
are unchanged; new fields/methods are opt-in.

- `link()` accepts `accountType` ('PRIVATE' | 'SHARED') and ACL fields
  (`allowAllUsers`, `allowedUserIds`, `notAllowedUserIds`) to create a
  SHARED connection in one call.
- `get()` / `list()` responses now expose `accountType`. ACL fields are
  forwarded when present (backend hides them for non-creator/non-API-key
  callers — left as undefined in the SDK shape).
- New `updateAcl(nanoid, params)` method wires the existing
  `client.connectedAccounts.patch` (PATCH /connected_accounts/{id}) for
  ACL writes. PATCH semantics: omit a field to leave unchanged; pass
  empty array to clear an allow/deny list. At least one field required.
- New error classes: `ComposioSharedAccessDeniedError` (403),
  `ComposioAclOnlyForSharedError` (400),
  `ComposioSharedConnectionNotAccessibleError` (400).

The wire-level fields (account_type, allow_all_users, etc.) are not yet
in @composio/client@0.1.0-alpha.70's typed surface — they'll land when
Stainless regenerates against the post-merge backend spec. Two narrow
local types (`LinkCreateBodyWithAcl`, `ConnectedAccountPatchBodyWithAcl`)
+ boundary casts paper over the gap until then; remove once the client
is bumped.

Verified: pnpm typecheck clean, build clean, 899 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 11:53:28 +05:30
Composio c358ffaa3b fix(connected-accounts): SEC-339 gate initiate() warning on response Deprecation header
initiate() previously emitted a one-time deprecation warning whenever the
response carried a redirectable OAuth scheme (OAUTH1, OAUTH2, DCR_OAUTH),
regardless of whether the auth config was Composio-managed (subject to the
2026-07-03 cutover) or custom (unaffected). The wording was conditional —
'If this auth config is Composio-managed…' — so callers using their own
OAuth apps could ignore it, but the warning still printed and added noise
to logs.

Apollo already emits the SEC-339 'Deprecation' / 'Sunset' / 'Link
rel="deprecation"' headers (RFC 9745 / RFC 8594) only on the retiring
branch — managed + redirectable OAuth + LD didn't block. The SDKs now
read the 'Deprecation' header from the response and gate the warning on
its presence. Custom auth configs and non-OAuth schemes get a clean
response from the server and now stay silent in the SDK as well.

TS:
- Chain '.withResponse()' on the underlying client.connectedAccounts.create
  call to expose the raw Response. Defensive fallback for tests that mock
  create() as a plain Promise.
- Drop the LEGACY_RETIRING_OAUTH_SCHEMES set; the apollo header is now the
  sole signal.
- New tests in describe('initiate deprecation header gate', ...) pin the
  contract: warns when Deprecation is present, silent when absent, and
  one-time-per-process even across multiple managed-OAuth calls. A
  mockApiPromiseWithHeaders helper wraps mocked values as APIPromise-shape
  thenables so the new tests can simulate apollo's header behavior.

Python:
- Switch to client.connected_accounts.with_raw_response.create so we can
  read raw.headers.get('Deprecation'). Defensive fallback to the parsed-
  only path for test mocks that don't stub with_raw_response.
- Drop _LEGACY_RETIRING_OAUTH_SCHEMES; same simplification as TS.
- New TestInitiateDeprecationHeaderGate class with the same three pinning
  cases. A _set_initiate_response helper routes mock responses through
  the with_raw_response surface that initiate() now consumes. Pre-existing
  initiate tests migrated to the helper; one assertion that inspected
  create.call_args.kwargs now reads with_raw_response.create.call_args.kwargs.

No public API change for either SDK — initiate() returns the same
ConnectionRequest shape and respects the same allowMultiple guard.
ComposioLegacyConnectedAccountsEndpointRetiredError continues to surface
from the 400 retired-path response.

Changeset bumps @composio/core as a patch.
2026-05-08 15:18:03 +05:30
github-actions[bot] 49629a7b9d Release: update version 2026-05-07 13:59:55 +00:00
Dhawal Upadhyay 28c209225a Fix invalid changeset package 2026-05-07 19:26:14 +05:30
Dhawal Upadhyay 711a703ae3 Add core minor changeset 2026-05-07 17:23:35 +05:30
Dhawal Upadhyay fdf4e85350 Refine ToolRouter preload and generated attach support (#3364)
## Summary
- Add public direct-tools preset constants (`SessionPreset.DIRECT_TOOLS`
in TS, `SESSION_PRESET_DIRECT_TOOLS` in Python) and internal
`preload.tools = "all"` constants.
- Reuse the create-time inline custom tool preload preparation path for
`use(..., customTools/customToolkits)` attach flows, including
existing-session `preload.tools = "all"` handling for attached customs.
- Use generated Stainless attach types where available; keep a narrow
Python additive type for the custom-definition `preload` hint until
Stainless exposes that field.
- Clean up user-facing preload docs/comments, direct custom tool
description prefix, and direct/preload examples with normalized
`LOCAL_*` assertions.
- Add separate changesets for preload behavior and the direct-tools
preset.

## Validation
- `pnpm --dir ts/packages/core typecheck`
- `pnpm --dir ts/packages/core test test/models/toolRouter.test.ts
test/models/customTool.test.ts`
- `(cd python && .nox/chk/bin/mypy --config-file config/mypy.ini
composio/)`
- `COMPOSIO_CACHE_DIR=/private/tmp/composio-cache
python/.nox/chk/bin/python -m pytest python/tests/test_tool_router.py
python/tests/test_custom_tools.py`
- `python/.nox/chk/bin/python -m ruff check
python/composio/core/models/tool_router.py
python/composio/core/models/custom_tool_types.py
python/composio/core/models/inline_custom_tools_payload.py
python/composio/core/models/custom_tool.py
python/composio/core/models/tool_router_constants.py
python/tests/test_tool_router.py`
- `python/.nox/chk/bin/python -m ruff check
python/examples/tool_router/direct_tools_preset.py`
- `python/.nox/chk/bin/python -m py_compile
python/examples/tool_router/direct_tools_preset.py`
- `git diff --check`
2026-05-07 16:49:22 +05:30
Alberto Schiabel 04545a72d3 fix(core): expose string | string[] in connectedAccounts public type (#3365)
## Summary

Follow-up to #3358. The `.transform()` added on the `connectedAccounts`
Zod schema made the inferred TypeScript type resolve to the
**post-transform output** (`Record<string, string[]>`), so TS users
still hit `error TS2322: Type 'string' is not assignable to type
'string[]'` even though the runtime accepted strings. The runtime fix
landed; the public TS surface didn't follow.

This PR moves the coercion out of the schema and into
`ToolRouter.create`, mirroring the Python implementation. The public
type is now `Record<string, string | string[]>` and runtime behavior is
unchanged.

## Why CI didn't catch this in #3358

The PR's own test file (`toolRouter.test.ts`) already assigns a `string`
to `ToolRouterCreateSessionConfig`, but `ts/packages/core/tsconfig.json`
has `"include": ["src/**/*.ts"]` — `test/` is not typechecked. So `tsc
--noEmit clean` was technically true while masking the issue. Verified
empirically by dropping a typecheck file into `src/` on `next`:

```
src/__type_check.ts(8,7): error TS2322: Type 'string' is not assignable to type 'string[]'.
```

After this PR, the same file compiles cleanly.

## Changes

- **`ts/packages/core/src/types/toolRouter.types.ts`**: drop
`.transform()` from the `connectedAccounts` schema; `z.infer` now
exposes `Record<string, string | string[]>`.
- **`ts/packages/core/src/models/ToolRouter.ts`**: coerce `string →
[string]` inside `create` before sending to the backend (which expects
arrays). Preserves the original `connected_accounts: undefined` behavior
when the field is omitted (the parent schema's `.partial()` strips the
`default({})`).
- **`ts/packages/core/src/types/toolRouter.types.ts`**: fix stale
`@param {Record<string, string>}` JSDoc on the same field.
- **`.changeset/v31-connected-accounts-array.md`**: drop the inaccurate
*"Bumps `@composio/client` to `0.1.0-alpha.70`"* line — that bump landed
in a separate commit on `next` (`7c1443838`), not in #3358's diff (`git
diff 2bbd791de^1 2bbd791de` shows zero `pnpm-lock.yaml`/`package.json`
changes).
- **`.changeset/fix-connected-accounts-string-type.md`**: new `patch`
changeset for `@composio/core`.

## Test plan

- [x] `pnpm exec tsc --noEmit -p tsconfig.json` clean on
`@composio/core`
- [x] `pnpm exec vitest run` — 883/883 tests pass (incl. 110 in
`toolRouter.test.ts`)
- [x] `pnpm prettier --check` clean on touched files
- [x] Spot-checked: assigning `{ connectedAccounts: { gmail: 'ca_xxx' }
}` to `ToolRouterCreateSessionConfig` now typechecks; assigning the
array form still typechecks.
2026-05-07 13:07:58 +02:00
Dhawal Upadhyay bccd32beae Refine ToolRouter preload docs and constants 2026-05-07 16:10:38 +05:30
Abir Taheer c96f094b15 feat(core): add session.update() for partial config updates (#3362)
## Summary
- Adds `session.update()` method to both TypeScript and Python SDKs
- Calls the existing `PATCH /api/v3/tool_router/session/{id}` endpoint
- Accepts the same config shape as `create()` (toolkits, tools, tags,
auth_configs, connected_accounts, manage_connections, workbench,
multi_account, preload)
- Mutates the session object in-place (preload, configVersion, warnings)

## Usage

### TypeScript
```typescript
const session = await composio.create('user_123', {
  toolkits: ['gmail'],
  manageConnections: true,
});

await session.update({
  toolkits: { enable: ['gmail', 'github'] },
  workbench: { enable: false },
});
```

### Python
```python
session = composio.create(user_id='user_123', toolkits=['gmail'])

session.update(
    toolkits={'enable': ['gmail', 'github']},
    workbench={'enable': False},
)
```

## Test plan
- [x] TypeScript: `tsc --noEmit` passes
- [x] TypeScript: all 874 existing tests pass
- [x] Python: mypy passes
- [x] Python: ruff lint + format clean
- [x] Python: all 82 tool_router tests pass
- [ ] Manual test: create session, call `session.update()`, verify
config changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-07 03:39:14 -07:00
jkomyno c9b6525bc9 fix(core): expose string | string[] in connectedAccounts public type
The Zod `.transform()` added in #3358 made the inferred type of
`ToolRouterCreateSessionConfig['connectedAccounts']` resolve to
`Record<string, string[]>` (the post-transform output), so TypeScript
users still hit `Type 'string' is not assignable to type 'string[]'`
even though the runtime accepted strings. The runtime fix landed; the
public TS surface didn't follow.

Drop the schema transform and coerce inside `ToolRouter.create` instead,
mirroring the Python implementation. The public type is now
`Record<string, string | string[]>` and behavior is unchanged.

While here:
- fix the JSDoc on `connectedAccounts` (was `Record<string, string>`)
- drop the inaccurate `@composio/client 0.1.0-alpha.70` bump line from
  the v31-connected-accounts-array changeset (this PR didn't perform
  that bump; it landed in a separate commit on `next`)
2026-05-07 12:35:03 +02:00
Abir Taheer a0242d7b3a docs: clarify session use wording 2026-05-07 02:24:42 -07:00
Abir Taheer 2d07d26f52 docs: update session use attach changeset 2026-05-07 02:12:57 -07:00
Abir Taheer ba435b77a0 chore: update changeset to include Python SDK
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 02:08:01 -07:00
Abir Taheer 81f8027cf6 chore: add changeset for session.update() (minor)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 02:01:42 -07:00
Abir Taheer 55951f18b0 docs: clarify changeset wording per review
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 22:58:19 -07:00
Abir Taheer bd36c57aaf fix: accept string | string[] for connectedAccounts, coerce to array
Non-breaking: existing callers passing a single string per toolkit
continue to work. The string is silently coerced to a single-element
array to match the v3.1 wire format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 22:57:40 -07:00
Abir Taheer 07c9baba9d chore: separate changeset for connectedAccounts array type (minor)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 22:48:06 -07:00
Abir Taheer 3ece424708 chore: add changeset for v3.1 customs use/attach (minor)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 22:46:57 -07:00
Alberto Schiabel f5c5b84bd3 fix(mastra): resolve $ref in tool schemas before applyCompatLayer [PLEN-2244] (#3315)
## Summary

Composio tools whose JSON Schema uses internal `$ref` pointers
(`#/$defs/...` or the legacy `#/definitions/...`) used to lose all type
information when wrapped by `MastraProvider.wrapTool`. The schema would
silently round-trip `raw → applyCompatLayer → convertSchemaToZod (drops
$ref → permissive anyOf) → zodToJsonSchema (no recovery)`. Customers
reported the user-visible surface as *"schema validation error: can't
resolve reference"* — AJV (bundled inside `@mastra/schema-compat`)
refusing to compile the schema downstream when validating tool input.

This PR pre-processes the schema with a new `dereferenceJsonSchema`
utility from `@composio/core` *before* it reaches
`@mastra/schema-compat`, preserving the type information from
`$defs`/`definitions` and avoiding the AJV failure entirely.

Closes
[PLEN-2244](https://linear.app/composio/issue/PLEN-2244/mastra-provider-schema-validation-error-cant-resolve-reference).

## What changed

- **`@composio/core`** — new `dereferenceJsonSchema(schema)` helper (and
`JsonSchemaRefResolutionError`):
- walks the schema reflectively (no JSON-Schema-keyword allow-list, so
future applicators are covered automatically),
- inlines internal `$ref` pointers transitively, with a 100-hop depth
cap,
- shallow-merges sibling keywords next to `$ref` per Draft 2020-12
semantics (siblings win on collision — preserves caller's `description`
/ `default` / `title`),
- breaks recursive cycles with `{ type: 'object', additionalProperties:
true }` (matches upstream guidance in
[mastra-ai/mastra#15341](https://github.com/mastra-ai/mastra/issues/15341)),
- strips `$defs` / `definitions` from the returned root once everything
reachable is inlined,
- leaves external (`http://`/`https://`) `$ref` pointers untouched so
callers can still forward them,
- throws `JsonSchemaRefResolutionError` on malformed pointers, missing
targets, and over-cap chains.
- **`@composio/mastra`** — `wrapTool` now runs `dereferenceJsonSchema`
over both `inputParameters` and `outputParameters` before calling
`applyCompatLayer`.
- **`@mastra/schema-compat`** floor bumped to `^1.2.9` so consumers
automatically pick up upstream [PR
#15400](https://github.com/mastra-ai/mastra/pull/15400)'s
recursive-`$ref` handling and [PR
#14530](https://github.com/mastra-ai/mastra/pull/14530)'s Draft-2020-12
AJV switch.
- Changeset: `.changeset/fix-mastra-ref-dereference.md` (patch bump for
`@composio/core` and `@composio/mastra`).

## Test plan

Red → green TDD across two commits — the `test:` commit reproduces
PLEN-2244 against the real `@mastra/schema-compat` (no `vi.mock`), the
`fix:` commit makes the regression tests pass.

- [x] `ts/packages/core/test/utils/jsonSchema.test.ts` — 14 unit tests
for `dereferenceJsonSchema` covering: basic resolution, chained refs,
container coverage (items / oneOf / anyOf / allOf / not /
additionalProperties / patternProperties / prefixItems), legacy
`definitions` (Draft 7), `$defs`/`definitions` mixed transitively,
sibling-keyword merge per Draft 2020-12, cycle break,
`$defs`/`definitions` strip, missing-target error, depth-cap error,
external `$ref` preserved, no input mutation, JSON Pointer escape
decoding (`~0`/`~1`), array-index pointers.
- [x] `ts/packages/providers/mastra/test/mastra-ref.test.ts` — un-mocked
integration tests against real `@mastra/schema-compat`: wrapping does
not throw, type info from `$defs` survives the round-trip (the `id`
field stays `type: 'string'` instead of degrading to permissive
`anyOf`), same for Draft-7 `definitions` on output schema, no remaining
`$ref` in the produced schemas.
- [x] `pnpm --filter @composio/core test` — 852/852 green.
- [x] `pnpm --filter @composio/mastra test` — 43/43 green (39 legacy + 4
new).
- [x] `pnpm --filter @composio/core --filter @composio/mastra typecheck`
— clean.
- [x] Existing `mastra.test.ts` (which mocks `@mastra/schema-compat`) is
**untouched** and still green.
- [x] Secret-hygiene scan: `git diff next..HEAD | grep -E
'sk-[A-Za-z0-9_-]{20,}|OPENAI_API_KEY=[A-Za-z]'` returns empty.

## Why it's a Mastra-only fix

The Mastra path is unique in routing JSON Schema through
`@mastra/schema-compat`, which embeds AJV. Sibling providers (`openai`,
`anthropic`, `vercel`, `langchain`, `google`, `llamaindex`) forward
Composio's JSON Schema through `@composio/json-schema-to-zod`, which
silently no-ops on unknown nodes. They are not affected today; expanding
pre-processing across providers is a separate PR if it ever becomes
needed.

## Caveats

- The cycle-break sentinel weakens type information for genuinely
recursive tool schemas. This is the same trade-off Mastra itself made in
[#15341](https://github.com/mastra-ai/mastra/issues/15341); without it
the agent crashes entirely.
- External `$ref` (`http://`/`https://`) is intentionally left
untouched. If a real customer schema ever needs them resolved, that's an
incremental follow-up that calls into
`@apidevtools/json-schema-ref-parser` only when needed.
- The cycle-break currently uses a single sentinel for any cycle; if
review prefers the higher-fidelity "keep first expansion, only break on
the recursion edge" pattern (mentioned in the research as a refinement),
that's a small follow-up.
2026-05-06 11:17:57 +02:00
lingalarahul7 2e1c25eb7f feat(sdk,docs): SEC-339 link-auth migration support across SDK + docs (#3289)
## Summary

End-to-end work to help SDK and direct API users move off the legacy
`POST /api/v3/connected_accounts` path for **Composio-managed OAuth**
ahead of the [2026-04-24 changelog
announcement](/docs/changelog/2026/04/24) — cutover dates **2026-05-08**
(new orgs) and **2026-07-03** (all orgs). Pairs with the backend
deprecation signals in
[ComposioHQ/hermes#9622](https://github.com/ComposioHQ/hermes/pull/9622).

Custom auth configs (your own OAuth app) and non-OAuth schemes (API key,
bearer, basic) are unaffected throughout.

## SDK changes

### TypeScript (`@composio/core`)

- **New error class**
`ComposioLegacyConnectedAccountsEndpointRetiredError` in
`errors/ConnectedAccountsErrors.ts` — extends `ComposioError`, status
400, with `possibleFixes` pre-populated to point at `link()` and the
migration guide.
- **`initiate()` wraps the create call** in try/catch and re-throws the
new typed error when the response message indicates the retiring path.
The match is conservative — substring on stable backend message text,
with everything else flowing through unchanged.
- **One-time-per-process deprecation warning** via `logger.warn` when
the response indicates `OAUTH1` / `OAUTH2` / `DCR_OAUTH`. Wording is
conditional ("If this auth config is Composio-managed...") so
custom-OAuth users can ignore it without ambiguity.
- **JSDoc** on `initiate()` flags the deprecation explicitly with both
cutover dates and a link to the migration guide.

### Python (`composio` core)

- Mirror exception class
`ComposioLegacyConnectedAccountsEndpointRetiredError` in
`composio/exceptions.py`.
- `initiate()` catches `BadRequestError` from `composio_client`,
inspects the message text, and re-raises as the typed exception
(preserves `__cause__`).
- One-time-per-process `warnings.warn(..., DeprecationWarning,
stacklevel=2)` on the redirectable-OAuth response path.
- Docstring carries a `.. deprecated::` block with the dates.

## Doc changes

| File | Change |
|---|---|
| `docs/content/docs/tools-direct/authenticating-tools.mdx` | Add a
`<Callout type="warn">` to the `OAuth Connections` subsection of `Direct
SDK Setup`. The Hosted Authentication section already shows `link()`
first; this surfaces the retirement on the `initiate()` subsection where
readers most need it. |
| `docs/content/docs/auth-configuration/connected-accounts.mdx` |
Promote the `Multiple accounts` callout from `info` to `warn`, pin the
cutover dates, and route readers to the migration guide. |
| `docs/content/docs/auth-configuration/custom-auth-configs.mdx` | Add
an `info` callout clarifying that the `initiate()` example shown is
*intentionally* fine here (custom auth configs are exempt). Avoids
copy-paste confusion. |
| `docs/content/reference/errors.mdx` | New section `## Specific error
codes` → `### Error 600` with anchor `#error-600`. The `suggested_fix`
URL emitted by the backend in the 400 response
(`/reference/errors#error-600`) now resolves to real content instead of
404'ing. |
| `docs/content/docs/auth-configuration/migrating-initiate-to-link.mdx`
| **New page.** Dedicated migration guide. Side-by-side before/after for
both languages, the `allow_multiple` replacement pattern, an
`is_composio_managed` routing recipe for mixed integrations, notes on
detecting the deprecation via SDK warning vs RFC 8594 response headers,
and a transition-friendly try/except example. |
| `docs/content/docs/auth-configuration/meta.json` | Wire the new
migration guide into the Authentication sidebar. |

## Why this scope

In the audit before this PR the SDK was the biggest gap —
`composio.connectedAccounts.initiate()` (TS) and
`composio.connected_accounts.initiate()` (Python) both call the legacy
endpoint, so every doc that teaches `initiate()` for OAuth was teaching
a path that breaks at cutover. The SDK changes give existing users a
typed deprecation path (warning before cutover, typed error after)
without forcing a behavior change on the custom-auth-config and
non-OAuth users who are unaffected. The doc changes target the two
highest-traffic teaching pages for OAuth flows, plug the broken
`error-600` URL the backend already references, and add a focused
migration guide that consolidates everything callers need.

## How did I test this PR

- **TS**: `pnpm tsc --noEmit` in `ts/packages/core` — error count
unchanged vs `next` (baseline 53 → 53; all pre-existing, none touch the
modified files). The new error class follows the existing
`ComposioFailedToCreateConnectedAccountLink` template; the typed-error
wrap reuses the `BadRequestError` import and pattern from
`errors/ComposioError.ts:13`.
- **Python**: confirmed `BadRequestError` is exported from
`composio_client` and `connection_data.auth_scheme` is a valid attribute
on the response model
(`composio_client.types.connected_account_create_response.ConnectionDataUnionMember0`
declares `auth_scheme: Literal["OAUTH1"] =
FieldInfo(alias="authScheme")` — same shape across all union members).
- **Docs**: all internal links use the relative-path convention
(`/docs/...`, `/reference/...`) and follow the format used by existing
changelog cross-links (e.g., `/docs/changelog/2026/04/24`). The new
`#error-600` anchor matches the URL the backend's `suggested_fix`
already emits.
- **Manual trace**: walked the four decision branches against the
modified `initiate()` code paths in both languages — custom auth configs
and non-OAuth schemes never trigger either the warning or the
typed-error path; managed OAuth triggers the warning on success and the
typed error on the eventual 400.

Pre-cutover the SDK warning is the loudest signal; post-cutover the
typed error is the cleanest catch point. Both are scoped strictly to the
affected combination.
2026-05-05 16:15:43 +05:30
Rahul Tarak 79ac22053f feat(cli): add local tools foundation (#3340)
## Summary
- Add experimental CLI local tools behind `local_tools` (beta-on,
stable-off unless `composio config experimental local_tools on`).
- Add reusable `@composio/cli-local-tools` package: declarations,
metadata, readiness, command/MCP/native/FFI runtimes, bundled-binary
resolution, and Tool Router search/execute/schema integration.
- Add first-class local toolkits for:
- `BEEPER_IMESSAGE` backed by `imessage-cli` from the ComposioHQ
`platform-imessage` fork.
- `CHROME_DEVTOOLS` backed by the official `chrome-devtools-mcp@0.24.0`
npm/npx package.
  - `PEEKABOO` backed by upstream Peekaboo CLI.
- Remove generated native executables from git: Beeper and Peekaboo
binaries are now ignored, pinned via git submodules, and generated
during CLI binary release jobs.
- Update CLI binary workflow so Linux artifacts skip native sidecars,
while macOS artifacts run on macOS runners and build only the matching
`darwin-x64` or `darwin-arm64` Beeper/Peekaboo sidecars before
packaging.

## Native binary policy
- No generated `imessage-cli` or `peekaboo` binary blobs are committed
in this PR history.
- Beeper source is pinned as
`ts/packages/cli-local-tools/vendor/platform-imessage` at
`364445a1b3089ad9fe293d5951efe160c5677c42` from
`https://github.com/ComposioHQ/platform-imessage`.
- Peekaboo source is pinned as
`ts/packages/cli-local-tools/vendor/peekaboo` at
`31e66e8d02656141d18f60bf3b46b24c2b9bc785` from
`https://github.com/steipete/Peekaboo`.
- Build commands:
- `pnpm --filter @composio/cli-local-tools build:local-tool-binaries --
--target darwin-arm64`
- `pnpm --filter @composio/cli-local-tools build:local-tool-binaries --
--target darwin-x64`
- Chrome DevTools is an npm/npx integration and is pinned as a dev
dependency (`chrome-devtools-mcp@0.24.0`) instead of using native
sidecars.

## Validation
- `pnpm --filter @composio/cli-local-tools build:local-tool-binaries --
--target darwin-arm64`
- `pnpm --filter @composio/cli-local-tools build:peekaboo -- --target
darwin-x64`
- `pnpm --filter @composio/cli-local-tools build:beeper-imessage --
--target darwin-x64`
- `pnpm --filter @composio/cli-local-tools typecheck:tsc`
- `pnpm --filter @composio/cli-local-tools test`
- `COMPOSIO_REAL_LOCAL_TOOLS_TESTS=1 pnpm --filter
@composio/cli-local-tools test -- src/toolkits/beeper-imessage.test.ts
src/toolkits/peekaboo.test.ts`
- `pnpm --filter @composio/cli typecheck:tsc`
- `pnpm --filter @composio/cli build`
- `COMPOSIO_CACHE_DIR=<tmp-config-with-local_tools-enabled> bun
ts/packages/cli/dist/bin.mjs local-tools doctor --json`
- `pnpm turbo build`
- Verified rewritten branch has no >1MB objects relative to
`origin/next` and no native executable paths in the PR diff.

## Notes / risks
- Beeper iMessage and Peekaboo are macOS-only and depend on local
permissions.
- Peekaboo requires macOS 15+; GUI tools require Screen Recording,
Accessibility, and sometimes Automation.
- Beeper send/reaction behavior can still depend on upstream Messages
automation reliability.
- Chrome DevTools can inspect/modify browser contents; avoid sensitive
browser sessions.
2026-05-05 00:10:38 -07:00
Composio 9f14971716 chore(changeset): @composio/core minor bump for link-auth migration tooling
Required by the TS release flow — without a changeset entry, merging
to next won't open or update the Release: update version PR and the
new exported error class won't reach npm.

`minor` because the new exported error class is additive but
user-facing. Python is tag-driven and doesn't need a changeset.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 09:58:59 +05:30
jkomyno 1d4c03efe6 docs(changeset): drop internal tracker reference
Changesets ship to the public CHANGELOG; internal Linear tracker IDs and
URLs should not appear there.
2026-04-30 02:00:24 +02:00
jkomyno cc673b654c fix(mastra): resolve $ref in tool schemas before applyCompatLayer [PLEN-2244]
Composio tools whose JSON Schema uses internal `$ref` pointers (`#/$defs/...`
or the legacy `#/definitions/...`) used to lose all type information when
wrapped by `MastraProvider.wrapTool`. The schema went through:

  raw JSON Schema (with $ref) → applyCompatLayer
    → convertSchemaToZod  (silently drops $ref, becomes a permissive anyOf)
    → zodToJsonSchema     (no way to recover the lost types)

Customers reported this surface as "schema validation error: can't resolve
reference" — AJV (bundled inside @mastra/schema-compat) refusing to compile
the schema downstream when validating tool input.

Fix: pre-process the schema with a new `dereferenceJsonSchema` helper before
handing it to `applyCompatLayer`. The helper:

- walks the schema reflectively (no JSON-Schema-keyword allow-list, so future
  applicators are covered automatically),
- inlines internal `$ref` pointers transitively, with a 100-hop depth cap,
- shallow-merges sibling keywords next to `$ref` per Draft 2020-12 semantics
  (siblings win on collision — preserves caller's `description`/`default`),
- breaks cycles with `{ type: 'object', additionalProperties: true }`,
  matching upstream guidance in mastra-ai/mastra#15341,
- strips `$defs`/`definitions` from the returned root once everything
  reachable is inlined,
- leaves external (`http://`/`https://`) `$ref` pointers untouched,
- throws `JsonSchemaRefResolutionError` on malformed pointers, missing
  targets, or chains over the cap.

Bumps the `@mastra/schema-compat` dependency floor to `^1.2.9` so users also
pick up upstream PR #15400's recursive-`$ref` handling.

The new red regression tests (committed in 5300bf8ba) now pass.
2026-04-30 01:33:30 +02:00
github-actions[bot] 757d096dac Release: update version 2026-04-28 12:12:28 +00:00
Zen Agent 6b986cd8f9 feat(sdk): add allow_multiple guard to connectedAccounts.link()
Bring `link()` to parity with `initiate()` so the duplicate-connection
guard moves with customers as Composio-managed redirectable-OAuth
callers migrate from `connected_accounts/create` to
`connected_accounts/link` (SEC-339).

- TS: add `allowMultiple?: boolean` to `CreateConnectedAccountLinkOptions`.
  `link()` now pre-flights `connectedAccounts.list({ statuses: ['ACTIVE'] })`
  and throws `ComposioMultipleConnectedAccountsError` when an active
  connection exists and `allowMultiple` is not `true`.
- Python: add `allow_multiple: bool = False` to `connected_accounts.link()`
  with the same guard and the same `ComposioMultipleConnectedAccountsError`.
- Tests: existing `link()` tests now mock `list` returning empty (default
  no-existing-connection path); new tests cover the raise path and the
  opt-in `allowMultiple=True` skip path. Also add multi_account parity
  tests on the Python tool_router create() to mirror the TS suite.

Behavior change is intentional and patched separately with
`.changeset/sdk-link-allow-multiple.md`. The user-facing changelog entry
is split into a separate docs PR alongside the sandbox-size docs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 11:48:04 +00:00
Zen Agent 1c3276ba8d feat(sdk): add workbench sandbox compute tier (sandboxSize / sandbox_size)
Expose `workbench.sandboxSize` (TypeScript) / `workbench.sandbox_size`
(Python) on `ToolRouter.create()` so callers can pick the workbench
sandbox compute tier (`standard`, `medium`, `large`, `xlarge`).
Optional; the server defaults to `standard` (1 vCPU / 1 GB) when
omitted, so existing callers keep current behavior.

- Bump stainless clients to pick up the field on the wire:
  - `@composio/client` 0.1.0-alpha.66 -> 0.1.0-alpha.67
  - `composio-client` 1.33.0 -> 1.34.0
- TS: extend `ToolRouterCreateSessionConfigSchema.workbench` with
  `sandboxSize` and forward it as snake_case `sandbox_size`. Export
  `SandboxSize` type and `SandboxSizeSchema` zod enum.
- Python: extend `ToolRouterWorkbenchConfig` TypedDict with
  `sandbox_size` and forward it on the create payload. Export
  `SandboxSize` literal alias.
- Changeset: `@composio/core` patch.
- Tests: cover the snake_case forwarding and zod enum rejection.

Docs (Configuring Sessions / Workbench / changelog) are split into a
separate PR so they can land alongside the SDK release.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 11:47:42 +00:00
Musthaq Ahamad 60b69aeaf2 Release: bump @composio/* to 0.8.0 (synced minor) (#3299)
## Summary

`pnpm changeset version` consumed `.changeset/fluffy-kids-lose.md` (the
auto file upload/download opt-in changeset for `@composio/core`). The
mechanical run bumped every dependent provider package to **`1.0.0`**,
which would surface as a spurious major bump on packages whose public
API didn't change. This PR corrects the provider bumps to **`0.8.0`** so
the version line stays aligned with `@composio/core` across the release
train.

> **Note:** Earlier revisions of this PR targeted `0.7.0`. After release
planning, the train was bumped to `0.8.0` (see commit `efc63ca6f`) —
`0.7.0` was published briefly and has since been **deprecated on npm**
in favor of `0.8.0`.

## Changes

| Package | Before | After |
| ------- | ------ | ----- |
| `@composio/core` | 0.6.11 | **0.8.0** (minor — see PR #3260) |
| `@composio/anthropic` | 0.6.11 | **0.8.0** |
| `@composio/claude-agent-sdk` | 0.6.11 | **0.8.0** |
| `@composio/cloudflare` | 0.6.11 | **0.8.0** |
| `@composio/google` | 0.6.11 | **0.8.0** |
| `@composio/langchain` | 0.6.11 | **0.8.0** |
| `@composio/llamaindex` | 0.6.11 | **0.8.0** |
| `@composio/mastra` | 0.6.11 | **0.8.0** |
| `@composio/openai` | 0.6.11 | **0.8.0** |
| `@composio/openai-agents` | 0.6.11 | **0.8.0** |
| `@composio/vercel` | 0.6.11 | **0.8.0** |
| `@composio/cli` | 0.2.25 | 0.2.26 (unchanged from `changeset version`;
CLI keeps its own version line) |

`.changeset/fluffy-kids-lose.md` is consumed.

## What changed in each provider's CHANGELOG.md

- `## 1.0.0` → `## 0.8.0`
- `### Patch Changes` → `### Minor Changes` with a one-line entry:
`Bumped to align with @composio/core@0.8.0 for the file-upload allowlist
release train. No public-API change in this package.`
- The duplicate `- Updated dependencies` line was deduped to a single `-
Updated dependencies [ebc9778]\n - @composio/core@0.8.0`.

## What changed in `@composio/core`'s CHANGELOG.md

- Removed the stray `- [BREAKING] Disable auto file upload / download`
bullet (an artifact of an earlier changeset that doesn't apply on
`next`); the long-form entry from `fluffy-kids-lose.md` already covers
the breaking nature in detail.
- The closing paragraph now says providers are bumped to `0.8.0`
alongside core (matching reality) instead of "automatic patch bumps via
`updateInternalDependencies: \"patch\"`".

## Notes

- `pnpm-lock.yaml` doesn't need updating — internal deps use
`workspace:*`.
- `pnpm typecheck` on `@composio/core` is clean (now reports
`@composio/core@0.8.0`).
- `pnpm build:packages` failed locally on a tsdown config-loader bug
under Node 24; the repo is pinned to Node 20.19.0 in `.nvmrc`, and the
build works fine there. Verified by stashing this PR's diff and
reproducing the same failure on clean `next` — pre-existing
local-environment issue, unrelated to the version bumps.

## Published & deprecated

Published to npm:
-
`@composio/{core,anthropic,claude-agent-sdk,cloudflare,google,langchain,llamaindex,mastra,openai,openai-agents,vercel}@0.8.0`

Deprecated on npm (all `@0.7.0`) with the message: *"0.7.0 has been
superseded by 0.8.0. Please upgrade: npm install <package>@0.8.0"*.

## Test plan

- [x] CI passes (build, typecheck, tests on Node 20.19.0).
- [x] After merge, `changesets-release/next` PR is generated cleanly
with no leftover changesets.
- [x] Verify the release publishes `@composio/*@0.8.0` (not `1.0.0`).
- [x] `@composio/*@0.7.0` deprecated on npm, redirecting users to
`0.8.0`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 11:56:43 +05:30