Commit Graph

857 Commits

Author SHA1 Message Date
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
Dhawal Upadhyay fdf4e85350 Refine ToolRouter preload and generated attach support (#3364)
## Summary
- Add public direct-tools preset constants (`SessionPreset.DIRECT_TOOLS`
in TS, `SESSION_PRESET_DIRECT_TOOLS` in Python) and internal
`preload.tools = "all"` constants.
- Reuse the create-time inline custom tool preload preparation path for
`use(..., customTools/customToolkits)` attach flows, including
existing-session `preload.tools = "all"` handling for attached customs.
- Use generated Stainless attach types where available; keep a narrow
Python additive type for the custom-definition `preload` hint until
Stainless exposes that field.
- Clean up user-facing preload docs/comments, direct custom tool
description prefix, and direct/preload examples with normalized
`LOCAL_*` assertions.
- Add separate changesets for preload behavior and the direct-tools
preset.

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

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

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

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

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

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

After this PR, the same file compiles cleanly.

## Changes

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

## Test plan

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

## Usage

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

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

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

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

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

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

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

While here:
- fix the JSDoc on `connectedAccounts` (was `Record<string, string>`)
- drop the inaccurate `@composio/client 0.1.0-alpha.70` bump line from
  the v31-connected-accounts-array changeset (this PR didn't perform
  that bump; it landed in a separate commit on `next`)
2026-05-07 12:35:03 +02:00
Abir Taheer 2598e15b14 fix(core): parse update config through Zod schema like create does
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 03:32:26 -07:00
Abir Taheer 22c205235d refactor(core): define ToolRouterUpdateSessionConfigSchema with Zod
Same pattern as ToolRouterCreateSessionConfigSchema — own Zod schema
with no defaults and nullable clearing fields. No longer references
create config types that have incompatible defaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 03:29:53 -07:00
Alberto Schiabel 0fd9196515 feat(connected-accounts): support REVOKED status added in Apollo #9550 (#3344)
# Description

Apollo (hermes#9550) added a new \`REVOKED\` value to the
connected-account status enum, alongside the existing \`INITIALIZING |
INITIATED | ACTIVE | FAILED | EXPIRED | INACTIVE\`. The Apollo
public-API response schema is \`z.nativeEnum(ConnectionStatusEnum)\`, so
any list / get / status-update endpoint can now return \`REVOKED\` and
clients must handle it.

This PR fixes every hand-written place in the TS and Python SDKs that
hard-coded the prior 6-value set and would either (a) fail-fast against
\`REVOKED\` (Zod / Effect Schema parse error) or (b) silently
misclassify it.

## TypeScript SDK — BREAKING fixes

| File | Change | Severity |
|---|---|---|
| \`ts/packages/core/src/types/connectedAccounts.types.ts\` | Add
\`REVOKED\` to \`ConnectedAccountStatuses\` and
\`ConnectedAccountStatusSchema\` (\`z.enum\`) | BREAKING —
\`z.enum().parse()\` would throw on responses containing the new value |
| \`ts/packages/cli/src/models/connected-accounts.ts\` | Add \`REVOKED\`
to Effect \`Schema.Literal(...)\` for \`ConnectedAccountItem.status\` |
BREAKING — \`Schema.Literal()\` enforces literal matching |
| \`ts/packages/cli/src/services/tool-router-session-connections.ts\` |
Add \`'REVOKED'\` case to the \`normalizeConnectedAccountStatus\` switch
| BREAKING — silent fall-through to \`'INACTIVE'\` masked the new status
|
| \`ts/packages/cli/src/services/composio-clients.ts\` | Replace the
explicit 6-value cast with a generic that resolves to whatever the
Stainless client currently accepts | Forwards \`REVOKED\` to Apollo;
auto-tracks future Stainless regenerations |
| \`ts/packages/core/src/models/ConnectedAccounts.ts\` | Cast
\`statuses\` to the Stainless params type so \`REVOKED\` (already
accepted by Apollo) flows through until \`@composio/client\` is
regenerated | Adapter shim; remove once \`@composio/client\` is
regenerated |

## Python SDK — DEGRADED fixes

| File | Change | Severity |
|---|---|---|
| \`python/composio/core/models/webhook_events.py\` | Add \`REVOKED\` to
\`ConnectionStatusEnum\` | DEGRADED — code that checked \`status in
ConnectionStatusEnum\` would have failed to recognise the new value |
| \`python/composio/core/models/connected_accounts.py\` |
\`ConnectionRequest.wait_for_connection\` now fails fast on terminal
non-ACTIVE states (\`FAILED\` / \`EXPIRED\` / \`INACTIVE\` /
\`REVOKED\`) instead of looping until the 60s timeout | DEGRADED —
REVOKED used to look identical to "still pending" until the timeout
fired |

The Stainless-generated \`@composio/client\` (TS) and
\`composio_client\` (Python) packages will pick up \`REVOKED\`
automatically when regenerated; this PR covers the hand-written SDK code
only.

# How did I test this PR

- \`pnpm typecheck\` (root) — clean (\`9 successful, 9 total\`)
- \`pnpm test\` (root) — 694 passed, 1 skipped (75 test files); examples
validation \`Validated 22 example packages\`
- Python: \`ruff check\` and \`ruff format --check\` clean on both
edited files
- Smoke-tested Python imports + enum membership:
  \`\`\`
  PYTHON: imports OK, REVOKED present: REVOKED
  \`\`\`

Triggered by: sarthak@composio.dev | Source: web
Session:
https://zen-api-production-4c98.up.railway.app/dashboard/#/chat/zen-14dc825802da
2026-05-07 12:19:19 +02:00
Abir Taheer 51ae6da240 fix: default requireExplicitSelection to true when multiAccount is enabled
Also makes multiAccount fields optional in ToolRouterUpdateSessionConfig
(PATCH semantics) and adds a PATCH-safe transform that doesn't inject
create-time defaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 03:14:53 -07:00
jkomyno d0fb8ed312 refactor(cli): extract decodeConnectedAccountListWithFallback helper
Three commands (`connected-accounts.list`, `connections.list`,
`connections.remove`) carried verbatim copies of the same 13-line
`Effect.catchTag('ParseError', ...)` forward-compat guard. Future
changes to the warning message or fallback semantics would have to
touch all three sites in lockstep.

Lift the guard into `src/effects/decode-connected-account-list.ts` so
each call site collapses to a single `yield*`. The helper carries the
WHY (forward-compat with Apollo enum additions) and the safety
justification for the cast in one place.

Surfaced by Cursor Bugbot on commit afb33deef.
2026-05-07 11:45:06 +02:00
Abir Taheer 09223e95b2 fix: restore catalog: specifier for @composio/client dependency
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 02:43:44 -07:00
Abir Taheer cd269efa06 refactor(core): remove unnecessary preloadedCustomToolSlugs recomputation
Custom tools are set at create time only — PATCH can't change them,
so recomputing the preloaded slugs list after update is dead code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 02:39:55 -07:00
Abir Taheer aeae303449 refactor(core): drop redundant Zod schema for update config
Stainless client already validates SessionPatchParams. Replace the
ToolRouterUpdateSessionConfigSchema with a plain interface and coerce
connectedAccounts string→string[] in the transform layer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 02:36:27 -07:00
Abir Taheer a17ecef33a docs: add custom tool session reuse examples 2026-05-07 02:29:34 -07:00
Abir Taheer a9977af2c1 docs: add session.update() examples for TypeScript and Python
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 02:28:10 -07:00
Abir Taheer b0f005ca14 fix(core): omit absent inline custom fields 2026-05-07 02:26:14 -07:00
Abir Taheer 5a133dce13 fix(core): address PR review comments for session.update()
- Fix stale preloadedCustomToolSlugs after update by recomputing them
- Add ToolRouterUpdateSessionConfigSchema for runtime validation
- Add PATCH-safe transform variants that don't inject defaults
- Add 11 unit tests for transformToolRouterUpdateParams

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 02:24:51 -07:00
Abir Taheer dd98d52f01 fix(core): attach existing tool router sessions 2026-05-07 02:11:26 -07:00
jkomyno afb33deef3 docs(cli): one-liner safety justification next to schema-bypass casts 2026-05-07 11:01:36 +02:00
Abir Taheer 254fb245df feat(core): add session.update() for partial session config updates
Exposes the existing PATCH /api/v3/tool_router/session/{id} endpoint
through the SDK so users can modify session config after creation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 02:01:09 -07:00
jkomyno 5bdaafd506 docs: trim verbose comments across the REVOKED-rollout commits
Net −56 lines of comment prose. Earlier commits added multi-paragraph
blocks explaining each forward-compat guard, sentinel choice, and
schema-bypass cast — most of that is rederivable from the surrounding
code. Keep one-line WHY notes where the intent is non-obvious; drop
the prose elsewhere.
2026-05-07 10:59:43 +02:00
jkomyno 1db88b00e8 docs(cli): annotate why the schema-bypass cast is safe at degrade points
The `as ConnectedAccountListResponse` cast in the `catchTag` fallback
of three commands (`connected-accounts.list.cmd.ts`,
`connections.list.cmd.ts`, `connections.remove.cmd.ts`) lies about the
schema brand to render a payload that just failed `decodeUnknown`. The
existing comment block explained _why we degrade_, but not _why the
cast is safe enough_.

Add a one-line per-site note recording that downstream renderers /
selectors only read whitelisted, non-credential-bearing fields
(`id`, `alias`, `word_id`, `toolkit.slug`, `status`, ...) — `state`
and `data` are never indexed, so an unvalidated raw payload cannot
leak tokens through this path.

Surfaced by kieran-typescript review of PR #3344 (P3#3).
2026-05-07 10:55:48 +02:00
jkomyno 0639997cc5 docs(connected-accounts): collapse the duplicated REVOKED-scope invariant
The same OAuth2-only / S2S_OAUTH2-next scope was documented in two
adjacent comment blocks: above `Oauth2RevokedConnectionDataSchema` and
above `S2SOauth2ConnectionDataSchema`. Keep the canonical one on the
S2S_OAUTH2 schema (where it's load-bearing — the arm there is the
preemptive one) and reduce the OAuth2 comment to a one-line
cross-reference.

Surfaced by code-simplicity review of PR #3344 (P3#2).
2026-05-07 10:54:59 +02:00
jkomyno 0c9cb82fed fix(cli): apply unknown-status guard to connections list/remove commands
`connections.list.cmd.ts` and `connections.remove.cmd.ts` decode the
same `ConnectedAccountListResponse` schema as
`connected-accounts.list.cmd.ts` but were yielding the decode without
a `catchTag` — a future server-side status addition would brick those
commands for every user, including users not filtering by status.

Mirror the forward-compat guard from `connected-accounts.list.cmd.ts`:
catch `ParseError`, warn pointing at `composio upgrade`, fall back to
the raw response.

Surfaced by pattern-recognition review of PR #3344 (P2#4) — keeping
the guard scoped to one command of three would reproduce the same
bug Apollo's next enum addition (queued S2S_OAUTH2 revocation) is
about to cause.
2026-05-07 10:49:19 +02:00
jkomyno c1a5cdef0c refactor(cli): drop unused generic from resolveConnectedAccountSelection
`resolveConnectedAccountSelection<T extends SelectableConnectedAccount>`
returned `T | undefined` to preserve the input narrowing, but no caller
relies on the narrowed type — `listen.cmd.ts:141` and
`tools.execute.cmd.ts:947` both feed the result through generic Pick
shapes. Drop the type parameter and take/return the wider type
directly.

Addresses code-simplicity review of PR #3344 (P2#3).
2026-05-07 10:48:28 +02:00
jkomyno 476564badf refactor(cli): replace Effect.gen + flatMap with Effect.map + tap
The earlier change to `tool-router-session-connections.ts` lifted the
synchronous map body into `Effect.flatMap(... Effect.gen ...)` purely
to host one `yield* Effect.logDebug(...)` call. Restore the simpler
`Effect.map` shape and use `Effect.tap` to emit the diagnostic — the
inner block stays purely synchronous, mirroring how peer services in
the same directory structure handle map-then-log flows.

Also adds a one-line comment on line 122 noting that the second pass
over `items` (the raw, pre-normalization shape) is intentional —
`auth_config` and `is_disabled` metadata are dropped from the
normalized projection.

Net: −15 lines, fewer nesting levels, easier diff to read.

Addresses code-simplicity review of PR #3344 (P2#2).
2026-05-07 10:47:41 +02:00
jkomyno 9d524c4ae7 fix(cli): use Effect.logDebug in tool-router-session service
`tool-router-session-connections.ts` is a service-layer module — peer
services (`composio-clients.ts`, `project-context.ts`,
`env-lang-detector.ts`, `js-package-manager-detector.ts`) all route
diagnostic signal through `Effect.logDebug`, while user-facing warnings
are emitted via `ui.log.warn` from command layers. The previous
`Effect.logWarning` was a CLI outlier; align with the service-layer
convention.

Addresses pattern-recognition review of PR #3344 (P2#1).
2026-05-07 10:46:46 +02:00
jkomyno a7c2d16bf3 test(connected-accounts): cover REVOKED rollout and forward-compat
Adds the test coverage flagged in PR #3344 review item P1#5:

TS (`ts/packages/core/test/connectedAccounts/connectedAccountAuthStates.test.ts`):
- `Oauth2RevokedConnectionDataSchema` parses with and without optional
  `revoked_at`
- `ConnectionDataSchema` discriminated union accepts REVOKED for both
  OAUTH2 and S2S_OAUTH2 (the preempted Apollo arm from P1#1)
- `transformConnectedAccountResponse` does not silently drop `state`
  for REVOKED on either auth scheme
- Closure test on the `ConnectionStatuses` enum so a future Apollo
  addition trips a CI failure on purpose

TS CLI (`connected-accounts.list.cmd.test.ts`):
- Server returning an unrecognized status no longer crashes the
  command — the `catchTag('ParseError')` fallback warns the user to
  run `composio upgrade` and renders the row anyway

Python (`tests/test_connected_accounts.py`):
- `wait_for_connection` fails fast on FAILED / EXPIRED / REVOKED
  (parameterized) — the contract added by this PR
- `wait_for_connection` does NOT treat INACTIVE as terminal, so a
  connection briefly transiting through INACTIVE still recovers to
  ACTIVE — guards against the regression called out in P1#2
2026-05-07 10:29:14 +02:00
jkomyno 0b1b33d24a fix(cli): use 'UNKNOWN' sentinel for unrecognized connection statuses
`normalizeConnectedAccountStatus` was coercing any value outside the
known status set to `'INACTIVE'`. That misled selection helpers like
`resolveDefaultConnectedAccountsByToolkit`, since `'INACTIVE'` carries
a specific meaning ("user-disabled"). When Apollo introduces a new
status (REVOKED was added in PR #9550; S2S_OAUTH2 revocation is
queued), every account with that status would silently be tagged as
user-disabled rather than honestly labeled as unrecognized.

Introduce a `'UNKNOWN'` sentinel exposed via a new
`SelectableConnectedAccount` type (a CLI-internal widening of
`ConnectedAccountItem` that the selection helpers accept). Selection
still picks only `'ACTIVE'` accounts, so any other label is filtered
out; `'UNKNOWN'` rows are not selectable, but the original server
value is logged via `Effect.logWarning` so users notice and can
upgrade.

Addresses PR #3344 review comment P2#8.
2026-05-07 10:24:40 +02:00
jkomyno 58a0705005 refactor(cli): use ConnectedAccountListParams['statuses'] for the cast
Replace the conditional-type incantation
  `Parameters<typeof client.connectedAccounts.list>[0] extends infer P
   ? P extends { statuses?: infer S } ? S : never : never`
at two call sites with a direct cast to
`ConnectedAccountListParams['statuses']`, which `@composio/client`
exports for exactly this purpose. ConnectedAccounts.ts:114 was already
using the shorter form.

Two issues with the previous form: (a) the comment claimed it "widens"
the type, but `as` cannot add values, so the framing was misleading;
(b) if Stainless ever renames or moves the `statuses` field, the
conditional silently resolves to `never` and swallows the cast — the
direct member access fails loudly at the typecheck instead.

Trim the duplicated multi-line comments to one line per call site.

Addresses PR #3344 review comment P2#7.
2026-05-07 10:20:46 +02:00
jkomyno 62157215d7 fix(cli): degrade gracefully when list response has unknown status
`Schema.decodeUnknown(ConnectedAccountListResponse)` was yielded
without a `catchTag`, so any new server-side connection status
(REVOKED was added in Apollo PR #9550, S2S_OAUTH2 revocation is queued
next) would brick `composio dev connected-accounts list` for the
entire user base — even users only filtering on `--status ACTIVE`.

Catch `ParseError`, log a warning pointing at `composio upgrade`, and
fall back to the raw server response so the table still renders.

Addresses PR #3344 review comment P1#4.
2026-05-07 10:19:09 +02:00
jkomyno 0827ec0955 fix(connected-accounts): add REVOKED arm to S2SOauth2ConnectionDataSchema
The top-level ConnectedAccountStatusSchema accepts REVOKED for any auth
scheme, but only Oauth2ConnectionDataSchema had a matching arm. For the
12 other unions, a REVOKED payload would fail discriminated-union
parsing and `transformConnectedAccountResponse` would silently drop the
entire `state` object — refresh tokens / api keys / oauth tokens would
vanish without surfacing an error.

Today this can't fire (per Apollo, REVOKED is OAuth2-only) but
`S2S_OAUTH2` is on the near-term roadmap. Add the REVOKED arm
preemptively and an inline invariant comment so the next reviewer
doesn't have to re-derive the OAuth2-only scope.

Addresses PR #3344 review comment P1#1.
2026-05-07 10:15:02 +02:00
Zen Agent 55dee9f2bd fix(connected-accounts): accept REVOKED in nested connection-data schema
Codex round 2 caught that the top-level `ConnectedAccountStatusSchema`
already accepted REVOKED, but the nested `state.val.status` validation
went through `ConnectionStatuses` in `connectedAccountAuthStates.types.ts`
which still only knew the prior 6 values. As a result:

- `transformConnectedAccountResponse` would silently drop the `state` of
  any REVOKED account (the discriminated union has no REVOKED variant).
- `webhookEvents.types.ts` (which uses `z.nativeEnum(ConnectionStatuses)`
  for `data.status`) would reject any webhook payload whose top-level
  account status is REVOKED.

Fix:
- Add REVOKED to the `ConnectionStatuses` enum (transitively fixes the
  webhook schema via `nativeEnum`).
- Add `Oauth2RevokedConnectionDataSchema` mirroring the Apollo-side
  schema at `connectionDataScheme.ts` (shape = INITIATING + literal
  REVOKED status + optional `revoked_at` timestamp), wire it into the
  `Oauth2ConnectionDataSchema` discriminated union, and export the
  inferred type. Apollo's PR 9550 only added a REVOKED variant to the
  OAuth2 scheme; other auth schemes (ApiKey / Basic / Bearer / etc.) do
  not produce REVOKED at runtime so they don't need a new variant.
2026-05-07 10:13:41 +02:00
Zen Agent 22dc98a286 fix(connected-accounts): handle REVOKED in waitForConnection + CLI list filter
Codex review of zen/connection-status-revoked-118z84 flagged two TypeScript
paths that did not yet handle the new REVOKED status:

- `ConnectionRequest.waitForConnection` only treated FAILED and EXPIRED
  as terminal error states, so a revoked connection would poll until the
  full timeout instead of failing immediately. Add REVOKED to
  `terminalErrorStates`. (This is the TS analog of the Python
  `wait_for_connection` fix already in this PR.)
- The `composio dev connected-accounts list --status` flag's
  `Options.choice` whitelist did not include 'REVOKED', so users could
  not filter for revoked accounts. Add it. The Stainless-typed list call
  underneath needs the same cast workaround used in
  `services/composio-clients.ts` so REVOKED forwards through until
  @composio/client is regenerated.
2026-05-07 10:13:41 +02:00
Zen Agent 0b32b158f3 feat(connected-accounts): support REVOKED status added in Apollo PR #9550
Apollo (hermes#9550) added REVOKED to the connected-account status enum.
The SDK had several places that hard-coded the prior 6 values and would
either fail-fast against the new value (BREAKING) or silently misclassify
it (DEGRADED).

TypeScript SDK (BREAKING fixes):
- Add REVOKED to `ConnectedAccountStatusSchema` / `ConnectedAccountStatuses`
  so `z.enum().parse(...)` no longer throws on responses that include the
  new status.
- Add REVOKED to the CLI's Effect `Schema.Literal(...)` for
  `ConnectedAccountItem.status`.
- Add REVOKED to the `normalizeConnectedAccountStatus` switch so a
  REVOKED account is no longer silently coerced to INACTIVE.
- Fix the connected-account-list raw-cast to forward REVOKED into the
  Stainless-generated client (the cast widens to whatever
  `client.connectedAccounts.list` accepts so it auto-tracks regenerations).

Python SDK (DEGRADED fixes):
- Add REVOKED to `ConnectionStatusEnum` so consumers checking
  `event.data.status in ConnectionStatusEnum` recognise the value.
- Update `ConnectionRequest.wait_for_connection()` to fail fast on
  terminal non-ACTIVE statuses (FAILED / EXPIRED / INACTIVE / REVOKED)
  instead of polling until the 60s timeout.

The Stainless-generated `@composio/client` and `composio_client` packages
will pick up the new enum value automatically when regenerated; this PR
covers the hand-written SDK code only.
2026-05-07 10:13:41 +02:00
Abir Taheer bd36c57aaf fix: accept string | string[] for connectedAccounts, coerce to array
Non-breaking: existing callers passing a single string per toolkit
continue to work. The string is silently coerced to a single-element
array to match the v3.1 wire format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 22:57:40 -07:00
Abir Taheer b336a089ee refactor: simplify inline custom tools payload construction
Remove conditional spreads/checks — undefined properties are stripped
during JSON serialization anyway.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 22:00:04 -07:00
Abir Taheer d20c887e1b fix: update connectedAccounts to array type, add .use() docs
- TS SDK: connectedAccounts schema changed from Record<string, string>
  to Record<string, string[]> matching @composio/client alpha.70
- Fixed toolRouter.test.ts to use array format
- Docs: added "Attaching custom tools to an existing session" section
  to custom-tools-and-toolkits.mdx showing composio.use() with customs
- Docs: updated all connectedAccounts examples to array format
- Docs: updated users-and-sessions to note custom tools can be attached
  to existing sessions via .use()
- Docs: removed TypeScript-only qualifier for custom tools

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 21:57:50 -07:00
Abir Taheer 3fc9cfc305 fix: update connected_accounts to array type for v3.1 multi-account
Bumps @composio/client to 0.1.0-alpha.70 which changes
connected_accounts from Dict[str, str] to Dict[str, List[str]].
Updated both TS and Python SDK types to match.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 21:52:23 -07:00
Abir Taheer b172ec9de2 fix(core): pass inlineCustomToolsPayload and preloadedCustomToolSlugs in use()
The TS use() was missing these two metadata fields that create() passes
to ToolRouterSession. Without them:
- Custom tools with preload:true wouldn't appear in session.tools()
- Inline custom tool schemas wouldn't be re-injected on execute/search

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 21:29:04 -07:00
Abir Taheer 7534447373 feat(core): add custom tools support to session.use() via v3.1 attach
composio.use(id, { customTools, customToolkits }) now POSTs to
/api/v3.1/tool_router/session/{id}/attach with inline experimental.custom_*
when customs are provided, hydrating the session and binding the local
tools map. Without options it keeps using the v3 retrieve endpoint.

Also caches the original CustomTool[] / CustomToolkit[] arrays on
CustomToolsMap so the SDK has the data needed to re-inject inline customs
on subsequent v3.1 search/execute/execute_meta calls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 20:24:17 -07:00
Dhawal Upadhyay b850d2cfdf Use gpt-5.2 for custom tools agent example 2026-05-06 19:06:37 +05:30
Dhawal Upadhyay eae717ac20 Clean up inline custom tools payload typing 2026-05-06 18:44:40 +05:30
Dhawal Upadhyay 8254999914 Address ToolRouter example review comments 2026-05-06 18:28:44 +05:30