Commit Graph

903 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 d7d5d46f4d fix(ts/e2e): stabilize Tool Router e2e tests (#3605)
This PR:

- addresses the Mastra Tool Router E2E timeout seen in
https://github.com/ComposioHQ/composio/actions/runs/27758637694/job/82126898492
- addresses the Cloudflare Tool Router AI failure seen in
https://github.com/ComposioHQ/composio/actions/runs/27759741357/job/82130692480
- addresses the follow-up Cloudflare basic timeout seen in
https://github.com/ComposioHQ/composio/actions/runs/27760548665/job/82133483582
- uses the existing `TIMEOUTS.LLM_LONG` live-LLM budget for both Mastra
Tool Router zod-v3 and zod-v4 suites
- raises the HTTP MCP connection-phase timeout from Mastra's implicit 3s
default to 15s in both zod-v3 and zod-v4 suites
- keeps the Cloudflare AI test's `Output.object({ karma })` structured
output and validates the returned `response.karma`
- asserts that `HACKERNEWS_GET_USER` was called and that
`response.karma` equals the structured MCP tool output
- removes the extra Cloudflare `prepareStep` steering and
`findNumberProperty` extraction helper
- avoids explicit `@ai-sdk/mcp` HTTP client close calls in workerd
because the close path aborts the pending stream and Vitest reports it
as unhandled
- gives the live Cloudflare HackerNews compatibility test a 30s budget
while leaving the fast smoke tests on Vitest's default timeout
- fixes the zod-v3 test label so it reports `zod 3` instead of `zod 4`

Verification:

- `./ts/e2e-tests/_utils/node_modules/.bin/tsc --noEmit -p
ts/e2e-tests/runtimes/node/mastra-tool-router-zod-v3/tsconfig.json`
- `./ts/e2e-tests/_utils/node_modules/.bin/tsc --noEmit -p
ts/e2e-tests/runtimes/node/mastra-tool-router-zod-v4/tsconfig.json`
- `pnpm exec vitest run -t "should connect to MCP server"` from
`ts/e2e-tests/runtimes/cloudflare/cf-workers-tool-router-ai`
- `pnpm exec tsc --noEmit --target es2022 --lib es2022 --module es2022
--moduleResolution Bundler --resolveJsonModule
--allowSyntheticDefaultImports --strict --skipLibCheck --types
@cloudflare/workers-types src/index.ts` from
`ts/e2e-tests/runtimes/cloudflare/cf-workers-tool-router-ai`
- `pnpm exec vitest run -t "should fetch HackerNews user pg
successfully"` from `ts/e2e-tests/runtimes/cloudflare/cf-workers-basic`
- `pnpm exec vitest run` from
`ts/e2e-tests/runtimes/cloudflare/cf-workers-basic`
- `pnpm exec prettier --check
ts/e2e-tests/runtimes/cloudflare/cf-workers-basic/test/platform.spec.ts`
- `pnpm exec prettier --check
ts/e2e-tests/runtimes/cloudflare/cf-workers-tool-router-ai/src/index.ts
ts/e2e-tests/runtimes/cloudflare/cf-workers-tool-router-ai/test/tool-router-ai.spec.ts
ts/e2e-tests/runtimes/node/mastra-tool-router-zod-v3/e2e.test.ts
ts/e2e-tests/runtimes/node/mastra-tool-router-zod-v4/e2e.test.ts`
- `git diff --check`

CI:

- `E2E Test Typescript SDK` passed on
https://github.com/ComposioHQ/composio/actions/runs/27768379357

Note: the full Cloudflare AI test was not run locally because this
checkout's local `OPENAI_API_KEY` is invalid; CI has the repo secret for
that path.
2026-06-18 19:01:02 +04:00
jkomyno 0720b760e2 fix(docs): stabilize SDK reference auto PR checks 2026-06-18 16:13:17 +04:00
Alberto Schiabel 08f7072110 fix(ci): make CLI releases reliably ship binary assets (#3515)
## Summary

Permanently stops the recurring *"release published with zero assets →
install/upgrade 404s"* outage
(https://github.com/ComposioHQ/composio/issues/3408,
https://github.com/ComposioHQ/composio/issues/3269; regressions of
https://github.com/ComposioHQ/composio/issues/2625 and
https://github.com/ComposioHQ/composio/issues/1820 — 3 of the last 5
stable releases shipped empty).

**Root cause:** a concurrent-writer race. On a CLI version bump,
`changesets/action` creates an empty `@composio/cli@x.y.z` GitHub
Release in seconds, then the slower `build-cli-binaries.yml` fails its
`gh release create` because the release already exists — so binaries
never attach.

> 🥞 **Stacked on #3415** (`pi/exclude-cli-ts-release-42161920`), which
removes the *second writer* (changesets no longer creates CLI releases).
Review/merge #3415 first; this PR targets that branch. This PR hardens
the now-sole writer so a *partial* build can't publish either, and fixes
the monitoring that hid the failure.

## What changed (`build-cli-binaries.yml` + canary)

- **Draft → verify → publish.** The release is built as a **draft**,
then a gate asserts all 6 canonical assets are present **and** `state ==
"uploaded"` (loud `::error::` + fail otherwise), and only the final step
flips it to published. Drafts fire no `release: published` event and are
excluded from `/releases/latest`, so no install.sh / Homebrew / redirect
consumer can observe an incomplete release.
- **`fail-fast: false`** on the build matrix → a single platform failure
can never publish a partial set (`needs.build.result` is `success` only
if all legs pass).
- **Per-tag job-level `concurrency`** so re-runs / quick pushes can't
interleave uploads on the same tag, without serializing unrelated betas.
- **Beta-safe:** `--prerelease` is set on the draft so betas stay
prereleases through the publish flip.
- **`promote-stable` guard** uses `gh release view --json isDraft` (the
REST tags endpoint 404s on drafts) to resume an existing draft but
refuse an already-published tag.
- **Least privilege:** workflow defaults to `contents: read`; only the
release job opts up to `contents: write`.
- **Canary fix** (`cli.install-health-check.yml`): install the newest
stable **pinned** tag (`npm view @composio/cli version`) instead of the
asset-aware no-arg flow, which self-heals to the last good release and
kept the canary green through every outage.

## Idempotency note

Re-running a run that failed **before publish** is safe (reuse draft +
`--clobber`). An already-**published** tag is refused, not mutated — by
design.

## Testing

- Extended `test/release-workflow.test.ts` (run via `pnpm
test:release-workflow`) to lock in: draft→verify→publish ordering,
`fail-fast: false`, per-tag concurrency, preserved `--prerelease`, and
the pinned-tag canary. ✅ passes.
- `shellcheck` clean on the new `run:` scripts; YAML validated.
`actionlint` runs in CI.
- Runtime verification of the full green path happens on the next
release CI run.

## Out of scope (deferred to their own PRs)

Per reviewer consensus, these are real but *not this bug*: a per-release
npm↔GitHub asset auditor (needs a grace window), the Homebrew trigger
constraint (folds into #3355), `composio upgrade` missing-asset back-off
(https://github.com/ComposioHQ/composio/issues/3269), and a rollback
runbook.

---------

Co-authored-by: Rahul Tarak <cryogenicplanet@gmail.com>
2026-06-16 15:07:23 +04:00
XananasX 632757ec60 chore(ts/packages/core): add repository, bugs, and homepage metadata (#3573)
## Summary

Adds missing npm support metadata to `ts/packages/core/package.json` for
the `@composio/core` package:

- **`repository`**: adds full object with `type`, `url`, and `directory`
(monorepo package path)
- **`bugs.url`**: points to
`https://github.com/ComposioHQ/composio/issues`
- **`homepage`**: points to the package README in the repository

## Verification

```sh
npm view @composio/core@0.10.0 repository bugs homepage
# Currently returns empty for all three fields
```

No runtime code, dependencies, tests, or build configuration are
changed.

---------

Co-authored-by: XananasX7 <xananasandy@gmail.com>
Co-authored-by: Alberto Schiabel <jkomyno@users.noreply.github.com>
Co-authored-by: jkomyno <alberto@composio.dev>
2026-06-16 13:48:45 +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
Alberto Schiabel 6606c0da92 fix(ts): upgrade vitest for GHSA-5xrq-8626-4rwp (#3569)
This PR:

- closes #3502
- extracts the useful dependency and Cloudflare config slice from
https://github.com/ComposioHQ/composio/pull/3503 onto a clean `next`
branch
- upgrades `vitest` and `@vitest/ui` through the workspace catalog to
resolve the critical advisory
- switches the Mastra provider test dependency back to the catalog
- migrates the Cloudflare Worker Vitest configs to the
`cloudflareTest()` plugin API
- preserves existing `COMPOSIO_BASE_URL` bindings that were dropped in
#3503
- keeps Vitest 4 compatibility fixes scoped to constructor mocks, spy
cleanup, and temp-root project detection
- leaves Node, `mise`, release docs, and Effect catalog versions
unchanged; the commit includes @Dotify71 as `Co-authored-by`

Co-authored-by: Dushyant Acharya <dushyantacharya873@gmail.com>
2026-06-15 19:38:51 +04:00
Alberto Schiabel 74ce25812e fix(ci): repair CLI binary and docs sync workflows (#3568)
This PR:
- patches the Peekaboo sidecar build to rewrite the resolved
`swift-system` `AT_RESOLVE_BENEATH` Darwin guard before compiling on
GitHub macOS runners
- adds regression coverage for the `swift-system` checkout patch and
includes script tests in `@composio/cli-local-tools` Vitest
- switches the SDK docs sync workflow to
`anthropics/claude-code-action/base-action` so push-triggered automation
does not fail on unsupported event parsing
- moves Claude tool restrictions to `claude_args` and quotes
`$GITHUB_OUTPUT` writes so the workflow passes `actionlint`
- verifies the changed package and workflow with targeted tests, build,
formatting, YAML parsing, and `actionlint`
2026-06-15 15:27:21 +04:00
Alberto Schiabel 4fe776898a chore(toolchain): finish mise migration (#3493)
## Summary

Depends on #3492.

This completes the Phase 2 migration by removing the transitional
version-file layer and making `mise.toml` plus `mise.lock` the
repository toolchain source of truth. It also moves runtime test
matrices into `toolchain-versions.json`, so CI matrix changes are
explicit and reviewable without reintroducing `.nvmrc`, `.dvmrc`,
`.bun-version`, or `.python-version`.

The Node.js e2e matrix now starts at the latest Node 22 LTS line and
also covers the latest Node 24 and Node 25 lines. That removes Node 20
from the well-known e2e versions while keeping us covered on the
runtimes SDK users are moving toward.

## Rationale

Phase 1 introduced mise side by side with the existing version files to
keep the first PR low-risk. Phase 2 removes that compatibility layer so
there is one place to update tool versions. That avoids silent drift
between local setup, GitHub Actions, Docker E2E images, release docs,
and install-time checks.

The composite setup actions now install Node, Bun, Python, and uv
through mise by default, with explicit version overrides only where a
matrix needs them. New GitHub actions added in this PR are pinned by
release commit SHA and include the release version comment.

## What changed

- Deleted the transitional root/version files: `.nvmrc`, `.bun-version`,
`.dvmrc`, root `.python-version`, and `python/.python-version`.
- Removed `idiomatic_version_file_enable_tools` from `mise.toml` and
added a committed `mise.lock` for linux/macOS x64/arm64 tool resolution.
- Replaced `BYPASS_BUN_VERSION_CHECK` with `BYPASS_TOOLCHAIN_CHECK`, and
made Docker E2E image installs use that bypass because they receive
explicit build args instead of installing mise.
- Updated Node/Bun and Python/uv composite actions to default to mise,
remove `*-version-file` inputs, report resolved versions, and cache pnpm
after `corepack enable`.
- Centralized CI runtime matrices in `toolchain-versions.json` for TS
E2E, Python tests, and CLI npm fallback coverage.
- Updated the Node E2E matrix to `22.22.3`, `24.16.0`, and `25.9.0`,
removing Node 20 from the well-known runtime versions.
- Updated workflows, docs, E2E helpers, Dockerfiles, and release
guidance to reference `mise.toml` / `mise.lock`.

## Verification

- Verified latest Node 22/24/25 releases from the official Node dist
index: `22.22.3`, `24.16.0`, `25.9.0`
(https://nodejs.org/dist/index.json).
- `pnpm install --frozen-lockfile`
- `pnpm --filter @e2e-tests/utils typecheck`
- `pnpm --filter @e2e-tests/utils exec tsc --noEmit --target es2022
--module esnext --moduleResolution bundler --types bun
--resolveJsonModule --skipLibCheck --strict scripts/docker-build.ts`
- `bash -n ts/scripts/pre-install.sh && bun run
ts/scripts/pre-install/check-toolchain.ts && BYPASS_TOOLCHAIN_CHECK=1
bash ts/scripts/pre-install.sh`
- `pnpm exec prettier --check ...` on touched YAML/Markdown/TS/JSON
files
- `ruby -e "require \"yaml\"; ARGV.each { |f| YAML.load_file(f) }" ...`
on touched actions/workflows
- `mise lock --platform linux-x64,linux-arm64,macos-arm64,macos-x64 &&
git diff --exit-code mise.lock`
- `mise exec node@22.22.3 -- pnpm --filter @e2e-tests/utils typecheck`
- `mise exec node@22.22.3 -- pnpm --filter @e2e-tests/node-esm-basic
typecheck`
- `mise exec node@22.22.3 -- pnpm --filter @e2e-tests/node-cjs-basic
typecheck`
- `docker manifest inspect node:24.16.0-slim`
- `docker manifest inspect node:25.9.0-slim`
- `git diff --check`
2026-06-15 14:03:21 +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
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
Alberto Schiabel 07160d14fa feat(toolchain): introduce mise.toml as single source of truth (#3492)
## Summary

This PR starts Phase 1 of PLEN-1368 by adding `mise.toml` as the
repo-owned source for the primary toolchain versions:

- Node `20.20.2`
- Bun `1.3.10`
- Deno `2.6.7`
- Python `3.12`
- uv `0.8.19`

It also keeps pnpm corepack-driven through
`package.json#packageManager`, adds `devEngines` for Node/pnpm
visibility, documents the new `mise install && corepack enable && pnpm
install` bootstrap path, ignores `.mise.local.toml`, and fixes stale
release-doc prerequisites.

## Why

The repo already has real toolchain drift, not just duplicated version
strings. The internal release docs had stale Node/Bun/pnpm versions,
Deno is repeated across workflows, Dockerfiles, docs, and e2e helpers,
and Python local setup still has a separate `3.11` venv path while the
repo pins `3.12`.

A composite-action cleanup would improve CI, but it would not solve
local development. `mise.toml` gives us one file that declares the
versions and lets contributors install or switch them with one command.
That is the main value proposition here: make the repo declare its own
toolchain, then let CI consume the same declaration in the next phase.

## Rollout

This is deliberately additive. It does not remove `.nvmrc`,
`.bun-version`, `.python-version`, or `.dvmrc`, and it does not change
CI behavior yet. Contributors who do not use mise can keep working as
before; contributors who do use mise get managed Node/Bun/Deno/Python/uv
immediately.

Phase 2 can migrate the existing composite actions to
`jdx/mise-action@v4`. Phase 3 can remove the legacy version files and
add the lockfile once the transition is complete.

## Out of Scope

- Migrating CI to `jdx/mise-action@v4`
- Removing legacy version files
- Adding `mise.lock`
- Updating nested TS package/example `packageManager` fields that still
say `pnpm@10.28.0`; that is pre-existing metadata drift and should be
handled separately to avoid broadening Phase 1

## Verification

- `git diff --check origin/next...HEAD`
- `mise install && mise current`
- `pnpm --version` -> `10.28.2`
- `pnpm dlx prettier@3.8.1 --check package.json`

Refs: PLEN-1368
2026-06-04 23:50:59 -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
yosinn1-blip 832c65ee1d fix: correct typos 'sucessfull' and 'satus' in callbackUrl JSDoc (#3478)
## Summary

Fixes #3475

Two typos in the JSDoc comment for
`CreateConnectedAccountLinkOptionsSchema.callbackUrl` in
`ts/packages/core/src/types/connectedAccounts.types.ts`:

- `sucessfull` → `successful`
- `satus=failed` → `status=failed`
2026-05-26 14:27:02 +04:00
Rahul Tarak cf13e17817 Fix fish completion install paths (#3162)
## Summary
- Route fish shell completions to
`~/.config/fish/completions/composio.fish` instead of writing them into
the rc file.
- Keep fish PATH setup in `~/.config/fish/config.fish` while writing
completion blocks to the dedicated completions file.
- Sanitize fish completion lines to strip description text that can
break parsing, including multiline descriptions.
- Add tests covering fish install behavior and fish completion
sanitization.

Closes #3147 

## Testing
- `pnpm test --filter
ts/packages/cli/test/src/commands/install.cmd.test.ts`
- `pnpm test --filter
ts/packages/cli/test/src/effects/shell-completions.test.ts`
- Not run: full repository test suite
2026-05-26 14:26:27 +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 afa118c8fb fix(cli): make permission allows expire after one hour (#3449)
## Summary
- Rename the permission prompt action from “Allow for session” to “Allow
for 1 hr” in browser and native UI flows.
- Cache allow decisions with a 1-hour expiry in
`tool-permissions-cache.json`, with an in-memory fast path and
stale-entry pruning.
- Preserve existing permission snapshot cache behavior while keeping
cached allow decisions across CLI invocations until expiry.

## Tests
- `pnpm install --frozen-lockfile --ignore-scripts`
- `pnpm build:packages`
- `cd ts/packages/cli && pnpm run typecheck`

## Notes
- The decision enum remains `allow_session` for compatibility with
existing backend/native callback payloads; only the UX/caching semantics
now expose and enforce a 1-hour duration.
2026-05-19 00:19:31 -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 ff4085a78d feat(connected-accounts-py): namespace SHARED-connection surface under experimental (#3412)
## Summary

Moves the SHARED connected-accounts surface in the Python SDK under the
`composio.experimental` namespace, mirroring the wire-level restructure
of the same fields. Same precedent as `composio.experimental.tool` /
`composio.experimental.Toolkit` — the namespace tells callers the shape
may still change.

- `link()` and `session.authorize()`: flat `account_type` /
`acl_config_for_shared` kwargs → single `experimental={}` dict
- `composio.connected_accounts.update_acl(...)` →
`composio.experimental.update_acl(...)`
- `composio.connected_accounts.list(...)` now exposes
`account_type='PRIVATE' | 'SHARED' | 'ALL'` (flat — the wire keeps this
as a query param)
- `composio-client` bumped `1.38.0` → `1.39.0` so the generated typed
client carries the `Experimental` TypedDicts

Pinning a SHARED connection in a session (`connected_accounts={...}`)
and direct execute by `connected_account_id` are unchanged — only the
connection-create / patch / authorize surfaces moved.

## Caller migration

```python
# before
composio.connected_accounts.link(
    user_id, auth_config_id,
    account_type="SHARED",
    acl_config_for_shared={"allow_all_users": True},
)
composio.connected_accounts.update_acl("ca_abc", allow_all_users=True)
session.authorize(
    "github",
    account_type="SHARED",
    acl_config_for_shared={"allow_all_users": True},
)

# after
composio.connected_accounts.link(
    user_id, auth_config_id,
    experimental={
        "account_type": "SHARED",
        "acl_config_for_shared": {"allow_all_users": True},
    },
)
composio.experimental.update_acl("ca_abc", allow_all_users=True)
session.authorize(
    "github",
    experimental={
        "account_type": "SHARED",
        "acl_config_for_shared": {"allow_all_users": True},
    },
)

# new — list SHARED connections
shared = composio.connected_accounts.list(
    account_type="SHARED",
    user_ids=["user_creator"],
)
```

## Tests

- `link()` forwards the experimental block / omits it when not provided
/ preserves explicit empty lists
- `link()` maps the `acl_config_for_shared is only valid on SHARED` 400
to `ComposioAclOnlyForSharedError`; non-ACL 400s re-raise
- `experimental.update_acl()` body construction, deny-list handling,
`ValidationError` when no fields provided, `ValidationError` when no
client is bound, same 400-mapper as `link()`
- `authorize()` forwards the experimental block
- `list(account_type="SHARED")` delegates the flat filter through to the
client

## Test plan

- [ ] `nox -s fmt` and `nox -s chk` clean for `composio/` (pre-existing
4 mypy errors in `providers/langgraph` and `providers/langchain` are
unrelated — same on `origin/next`)
- [ ] `pytest tests/test_connected_accounts.py
tests/test_tool_router.py` → 136 passed
- [ ] Docs page update (separate PR — code samples already on the
experimental shape there; `account_type=` list filter example to be
added once this lands)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-13 19:22:07 +05:30
jkomyno cfa30ceef3 chore(connected-accounts): drop dead re-exports from Experimental.ts
Three types (LinkCreateParams, SessionLinkParams, ConnectedAccountListParamsRaw)
were imported and re-exported but had zero external consumers. The named-type
imports of LinkCreateParams / SessionLinkParams were only referenced in a
descriptive comment; ConnectedAccountListParamsRaw wasn't used at all.

Rewrote the comment so it documents the structural-typing rationale without
referencing the now-unimported namespace names.

Typecheck clean, 919/919 tests pass.
2026-05-13 15:47:58 +02:00
venkat82 123b8d6f34 fix(connected-accounts): address review feedback on #3424
Six code-review nits from the multi-agent review on PR #3424:

P1.1 — `experimental_updateAcl(composio, id, opts)` doesn't match the
`experimental_createTool` precedent it cited. That export is a *pure
factory* (returns a value object, no client, no I/O), whereas
`updateAcl` takes a client and performs a network call. Costs the
current shape was paying:
- Awkward call site (composio passed as first arg every time)
- Bypassed `telemetry.instrument(this, 'ConnectedAccounts')` — the
  proxy only wraps class methods
- Diverged from the Python sibling (#3412) which mounts at
  `composio.experimental.update_acl(id, opts)`

Moved to `composio.experimental.updateAcl(id, opts)` as a method on a
new `Experimental` class. Top-level `experimental_*` prefix is now
reserved for stateless factories, matching the Python parity.

P2.2 — `ToolRouterSession.authorize` was re-inlining the same
camelCase→snake_case wire mapping that `ConnectedAccounts.link` already
encoded. Extracted `serializeExperimentalForWire` /
`serializeAclConfigForWire` into `models/Experimental.ts` and imported
both call sites from there. Next field added to the experimental block
now lands in one place.

P2.3 — `AuthorizeOptionsSchema` was a structural duplicate of
`ConnectedAccountExperimentalSchema`. Composed from the canonical
schema instead.

P2.4 — Substring match `acl_config_for_shared is only valid on SHARED`
extracted to `ACL_ONLY_FOR_SHARED_ERROR_FRAGMENT` in
`models/Experimental.ts` and reused from `ConnectedAccounts.link`,
`ToolRouterSession.authorize`, and `Experimental.updateAcl`. Single
source of truth so a server-side message tweak doesn't silently
downgrade three call sites to generic BadRequestError.

P2.5 — `ConnectedAccountsErrors.ts` `possibleFixes` strings on
`ComposioSharedAccessDeniedError` and
`ComposioSharedConnectionNotAccessibleError` were pointing at
`composio.connectedAccounts.updateAcl()` (method now removed). Rewrote
to `composio.experimental.updateAcl()` so the typed-error hint
compiles.

P2.6 — Telemetry bypass auto-fixed by P1.1. `Experimental` is now an
instrumented class; methods on it get the same telemetry proxy as
`ConnectedAccounts`.

P2.7 — Inline `import('../types/connectedAccounts.types')` type at the
old `serializeExperimentalForWire` declaration is gone with the
function move. Sibling models use plain top-level imports.

Tests: 919/919 pass, typecheck clean.

**Push-back on remaining findings:**
- P2.8 (`ComposioFailedToCreateConnectedAccountLink` missing `Error`
  suffix) — pre-existing naming, not introduced by this PR. Rename with
  back-compat alias is bigger scope; deferred.
- P2.9 (TS Zod caps vs Python no caps) — the TS caps are a UX win
  (clear ValidationError vs server 400). Better direction is adding the
  caps to Python in a follow-up, not dropping them from TS.
- All P3 nice-to-haves deferred.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 18:39:18 +05:30
venkat82 44972d90ad Merge remote-tracking branch 'origin/next' into feat/connected-accounts-experimental-py
# Conflicts:
#	python/composio/utils/schema_converter.py
2026-05-13 18:30:01 +05:30
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
Rahul Tarak 69e6ef6495 chore(cli): drop darwin-x64 local tools, bundles, and enhanced controls (#3421)
## Summary
- Remove darwin-x64 from local toolkit platform lists (beeper-imessage,
peekaboo, chrome-devtools) and drop the corresponding bundled binary
path entries.
- Skip building native Swift sidecar binaries (peekaboo, imessage-cli,
composio-native-ui) for darwin-x64; remove the bun-darwin-x64 matrix
entry from `build-cli-binaries.yml` so we no longer produce a darwin-x64
CLI bundle.
- Locally force-disable enhanced controls / elicitation on darwin-x64 in
`tool-permissions.ts` even when the backend opts the org in (logs a
debug message).

## Test plan
- [x] `pnpm --filter @composio/cli typecheck`
- [x] `pnpm --filter @composio/cli-local-tools typecheck`
- [ ] CI green on the build workflow (no darwin-x64 job runs)

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 02:34:36 -07:00
Rahul Tarak d6bf4370d7 feat(cli): add enhanced permission cache gating (#3419)
## Summary
- Add a CLI consumer permission cache backed by
`~/.composio/tool-permissions-cache.json` with a 5 minute TTL.
- Read enhanced-controls state from `/api/v3.1/org/consumer/config` and
resolve stored connected-account permissions from
`/api/v3.1/consumer/permissions/resolve`.
- Thread consumer cache scope through Tool Router session creation so
CLI link sessions use the enhanced public link URL when enhanced
controls are enabled.
- Add execute/run-compatible gating: CLI execute checks cached
permission config before the Tool Router execute call and opens the
bundled macOS native UI sidecar for ask modes.
- Use the sidecar `--callback-file` JSON decision channel, pass the
selected connection `word_id` to the UI account line, and treat no
response within 30 seconds as denial.
- Fall back to browser approval when the native sidecar is
unavailable/non-macOS.

## Stack
- Base PR: #3418 (`pi/swift-sidecar-ui-dfceb48e`) — native UI sidecar.
- This PR: CLI permissions cache and native sidecar gating integration.

## Tests
- `pnpm --filter @composio/cli typecheck`
- `pnpm exec eslint
ts/packages/cli/src/effects/create-tool-router-session.ts
ts/packages/cli/src/services/tools-executor.ts
ts/packages/cli/src/services/native-ui-sidecar.ts
ts/packages/cli/src/services/tool-permissions.ts`
- `pnpm --filter @composio/cli build`
- `pnpm --filter @composio/cli-local-tools build:composio-native-ui --
--target darwin-arm64`
- Earlier on this branch: `pnpm install --frozen-lockfile`, `pnpm
build:packages`

## Notes / Risks
- Native approval UI is macOS-only. Non-macOS or missing sidecar
binaries fall back to the temporary browser approval page.
- The CLI gates locally and does not pass `experimental.permissions`
into Tool Router sessions yet, because direct CLI execute does not
provide MCP elicitation transport. It does pass
`experimental.link_url_overwrite` for enhanced link flows.
- On a cold cache, the first consumer Tool Router session creation
refreshes permission state synchronously; fresh cache entries are
returned immediately while a background refresh updates disk for future
calls.
2026-05-13 01:37:20 -07:00
Rahul Tarak 7477aa9566 feat(cli): scaffold native UI sidecar (#3418)
## Summary
- Scaffold a macOS Swift `composio-native-ui` sidecar with a borderless
floating native overlay and Metal-driven pulsing/liquid-glass visual
treatment.
- Add a hidden/dev `composio dev native-ui` command that spawns the
bundled sidecar from CLI local-tool assets.
- Wire the sidecar into local-tool binary build scripts and CLI binary
release packaging checks for Darwin artifacts.

## Tests
- `pnpm install --frozen-lockfile`
- `pnpm typecheck`
- `pnpm --filter @composio/cli build`
- `pnpm --filter @composio/cli-local-tools build:composio-native-ui --
--target darwin-arm64`
- Manual launch via
`COMPOSIO_LOCAL_TOOLS_BIN_DIR=../cli-local-tools/local-tools-binaries
bun dist/bin.mjs dev native-ui --timeout 35`

## Notes / Risks
- This is a scaffold/prototype and is intentionally opened as a draft
PR.
- The visual effect uses a transparent AppKit panel,
`NSVisualEffectView`, and a Metal overlay. True optical distortion of
arbitrary content behind the window would require a different approach
such as ScreenCaptureKit/backdrop sampling and associated permissions.
- Generated sidecar executables remain ignored; release jobs rebuild
them before packaging artifacts.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 01:30:14 -07:00
Alberto Schiabel 126b79cdbf docs: update CLAUDE.md for composio SDK (#3396)
# Description
Weekly automated CLAUDE.md refresh. Updates stale CLAUDE.md files to
reflect recent codebase changes.

Files updated:
- `CLAUDE.md` — 403 -> 97 lines. Rewrote to current state: removed stale
`examples/` top-level entry, added `cli-keyring` and `cli-local-tools`
packages, noted default branch is `next`, kept the Bun version-mismatch
gotcha and the e2e Docker note, pointed agents at `AGENTS.md`,
`docs/CLAUDE.md`, and `ts/packages/cli/AGENTS.md`.
- `ts/packages/cli/AGENTS.md` (which `ts/packages/cli/CLAUDE.md`
symlinks to) — 379 -> 197 lines. Replaced the outdated command table
(the `manage` namespace was removed in #2937 and many new top-level
commands were added: `agent`, `auth-configs`, `connected-accounts`,
`orgs`, `projects`, `toolkits`, `tools`, `triggers`, `logs`, `init`,
`install`, `signup`, `proxy`, `listen`, `run`, `dev`, `artifacts`,
`config`, `local-tools`). Added a note that `@composio/cli-keyring`
powers OS credential storage. Dropped duplicate Effect / Clack
"Reference Source" sections (already consolidated). Kept the
load-bearing pieces: required typecheck, output-conventions (stdout vs
stderr), Effect patterns, release workflow.
- `docs/CLAUDE.md` — unchanged. Already current (last updated
2026-04-21) and within the 30-70 line target at 107 lines; the
`.claude/` subtree matches what it documents.

# How did I test this PR
- Verified line counts: root 97 (target 60-150), `docs/CLAUDE.md` 107
(unchanged), `ts/packages/cli/AGENTS.md` 197.
- Cross-checked the CLI command table against
`ts/packages/cli/src/commands/` (`ls ts/packages/cli/src/commands/`) and
against recent commits (`02e72116d`, `3415a4a15`, `d1ef6b25e`,
`67867aea1`).
- Cross-checked `ts/packages/` listing against the structure block in
the root `CLAUDE.md` (cli, cli-keyring, cli-local-tools, core,
json-schema-to-zod, providers, ts-builders).
- Confirmed `ts/packages/cli/CLAUDE.md` is still the symlink to
`AGENTS.md` after rewriting.

Origin: cron-update-claude-docs /
[zen-cron-248f7d9ad575](https://zen-api-production-4c98.up.railway.app/dashboard/#/chat/zen-cron-248f7d9ad575)
Triggered by: karan@composio.dev | Source: cron
Session:
https://zen-api-production-4c98.up.railway.app/dashboard/#/chat/zen-cron-248f7d9ad575
2026-05-12 18:05:54 +02:00
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
venkat82 a202b9ea90 docs(connected-accounts): drop residual "at create time" wording in error class
Two more spots leaking the same immutability wording removed from the
upstream API description:

- ComposioAclOnlyForSharedError possibleFixes said "Set accountType:
  'SHARED' at create time". Reworded to "Use accountType: 'SHARED'".
- ComposioSharedConnectionNotAccessibleError JSDoc said "caught at
  session-create time". Replaced "caught" framing with "Raised at
  session-create time" — "session-create time" stays because it's
  user-facing (tells the SDK caller when the error is raised, i.e.
  during session.create() / session.patch()).

Also tightened the surrounding wording — class doc cross-references the
actual SDK method names (`toolRouter.session.create()` /
`session.patch()`) instead of abstract phrasing.

No behaviour changes — docstring cleanup only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 01:28:34 +05:30
venkat82 10b914fd43 docs(connected-accounts): drop residual internal references
Final sweep caught three more spots leaking internal detail:

- `CreateConnectedAccountLinkOptionsSchema.aclConfigForShared` JSDoc
  said "the backend rejects ACL on a PRIVATE connection". Replaced with
  "raises ComposioAclOnlyForSharedError on a PRIVATE connection" —
  describes the SDK contract from the caller's perspective.
- `UpdateConnectedAccountAclParamsSchema` JSDoc had a parallel
  "Backend rejects ACL writes on a PRIVATE connection" sentence.
  Reworded the same way.
- Test comment in toolRouter.test.ts had a "Bugbot-flagged gap"
  annotation referencing internal review tooling. Replaced with a
  neutral description of what the tests verify.

No behaviour changes — JSDoc / comment cleanup only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 01:26:09 +05:30
venkat82 382ffe5ee9 docs(connected-accounts): drop "set at creation time only" wording
Two more JSDoc spots — `ConnectedAccountTypes` and the `accountType` field
on `CreateConnectedAccountLinkOptionsSchema` — claimed `accountType` was
"set at creation time only — cannot be changed later". The wording has
been removed from the corresponding API parameter description upstream,
so the SDK shouldn't echo it independently.

No behaviour changes — JSDoc-only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 01:14:40 +05:30
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 14a5b52b60 fix(connected-accounts): validate ACL input in ToolRouterSession.authorize()
Bugbot caught a parity gap (cursor[bot] review on #3392): authorize()
accepted aclConfigForShared and passed it straight to the wire, while
ConnectedAccounts.link() validated through CreateConnectedAccountLinkOptionsSchema
(which enforces the 1000-entry list cap and 1..256 char user_id length from
ConnectedAccountAclConfigSchema). Callers using authorize() with oversized
lists or invalid user IDs got opaque backend 400s instead of clear SDK-level
ValidationError, breaking the changeset's claim that "The SDK enforces these
caps via Zod at the input boundary."

Added a local `AuthorizeOptionsSchema` (callbackUrl + alias + accountType +
aclConfigForShared) and a safeParse + ValidationError throw at the top of
authorize(), mirroring the pattern in link() / updateAcl(). The shape is
identical to link's options sub-shape so the same caps apply.

Tests (toolRouter.test.ts):
  - forwards accountType + nested acl_config_for_shared on the wire
  - throws ValidationError when allowedUserIds exceeds 1000 entries
  - throws ValidationError when a user_id exceeds 256 chars
  - throws ValidationError when a user_id is empty

915 tests pass (4 new + 911 prior). pnpm typecheck clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 00:26:17 +05:30
venkat82 49a23fed24 fix(cli): add account_type to SessionLinkResponse test mocks
@composio/client@0.1.0-alpha.71 (just bumped in 483af3607) tightened
SessionLinkResponse to require `account_type: 'PRIVATE' | 'SHARED'` —
the four test mocks of `client.toolRouter.session.link` no longer
satisfy the typed surface.

CI was failing Build + Typecheck + every E2E job with the same 3 errors
at connected-accounts.link.cmd.test.ts:287, 341, 373 (cascade — Typecheck
fails first, Build/E2E inherit). All fixed by adding `account_type: 'PRIVATE'
as const` at the four mock sites (3 in connected-accounts.link.cmd.test.ts,
1 in test-layer.ts default).

PRIVATE chosen as the default because that matches what the backend now
returns when account_type isn't supplied on input.

Lesson: when bumping @composio/client, run `pnpm -r typecheck` at the
workspace root, not just `pnpm --filter @composio/core typecheck`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 00:06:17 +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
Zen Agent f273e95f92 docs: update CLAUDE.md for composio SDK
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 09:53:07 +00:00
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
Dhawal Upadhyay e14e61c051 Fix provider release versions to 0.9.0 2026-05-07 20:43:42 +05:30
github-actions[bot] 49629a7b9d Release: update version 2026-05-07 13:59:55 +00:00
Rahul Tarak a4c9e0ada2 refactor(cli): define local tools as custom toolkits (#3356)
## Summary
- Stacks this local-tools PR on top of #3360, which contains the core
duplicate custom-toolkit child slug fix.
- Refactors `@composio/cli-local-tools` to define bundled local toolkits
with core `experimental_createTool` / `experimental_createToolkit`
handles instead of hand-rolled schema objects.
- Keeps Tool Router v3.1 inline custom toolkit definitions attached to
session create/search/execute calls so bundled local tools remain
searchable/executable.
- Adds CLI/local-tools test coverage for forwarding local custom toolkit
payloads and for core custom toolkit handles from the local registry.

## Stack
- Base PR: #3360 (`fix(core): qualify custom toolkit child slug
lookups`)
- This PR should now show only CLI/local-tools changes relative to
#3360.

## Tests
- `pnpm --filter @composio/core typecheck`
- `pnpm --filter @composio/cli-local-tools typecheck`
- `pnpm --filter @composio/cli typecheck`
- `pnpm --filter @composio/cli-local-tools test`
- `pnpm --filter @composio/cli-local-tools build`
- `pnpm --filter @composio/cli exec vitest run
test/src/commands/tools/tools.search.cmd.test.ts
test/src/commands/tools/tools.execute.cmd.test.ts`
- `pnpm --filter @composio/core exec vitest run
test/models/customTool.test.ts test/models/toolRouter.test.ts
test/models/customToolRouting.test.ts`
2026-05-07 05:29:06 -07:00
Rahul Tarak d31efcb160 fix(core): qualify custom toolkit child slug lookups (#3360)
## Summary
- Fixes SDK custom-tool mapping for custom toolkits that reuse the same
child tool slug, e.g. `APP_A.VERSION` and `APP_B.VERSION`.
- Adds toolkit-qualified lookup (`toolkit + original slug`) while
keeping bare original-slug lookup only when it is unambiguous.
- Throws a client-side ambiguity error for manual
`session.execute('VERSION')` when multiple custom toolkit tools share
`VERSION`, instead of falling through to backend execution.
- Keeps `session.customToolkits()` / `session.customTools()` mapped to
the backend-assigned final slugs for duplicate child names.

## Repro / validation
Validated locally with a temporary Tool Router script (not committed)
using two dummy custom toolkits that both define `VERSION`:

- Current `next` SDK fails before high-level use with `Custom tool slug
collision: original slug "VERSION" maps to multiple final slugs...`.
- Tool Router backend accepts the same payload and returns both
`LOCAL_DUMMY_APP_A_REPRO_VERSION` and `LOCAL_DUMMY_APP_B_REPRO_VERSION`.
- This branch builds the SDK map successfully and high-level SDK
execution routes each final slug to its own internal handler:
- `LOCAL_DUMMY_APP_A_REPRO_VERSION` -> `{ app: "A", version: "1.0.0" }`
- `LOCAL_DUMMY_APP_B_REPRO_VERSION` -> `{ app: "B", version: "2.0.0" }`

## Tests
- `pnpm --filter @composio/core typecheck`
- `pnpm --filter @composio/core exec vitest run
test/models/customTool.test.ts test/models/toolRouter.test.ts
test/models/customToolRouting.test.ts`
2026-05-07 05:28:11 -07:00