Skip to content

fix(tracker-linear): fall back to direct transport when @composio/core is missing#2010

Open
harshitsinghbhandari wants to merge 2 commits into
AgentWrapper:mainfrom
harshitsinghbhandari:fix/tracker-linear-composio-fallback
Open

fix(tracker-linear): fall back to direct transport when @composio/core is missing#2010
harshitsinghbhandari wants to merge 2 commits into
AgentWrapper:mainfrom
harshitsinghbhandari:fix/tracker-linear-composio-fallback

Conversation

@harshitsinghbhandari
Copy link
Copy Markdown
Contributor

Summary

Fixes #2009.

tracker-linear's create() picks its transport by sniffing env: if COMPOSIO_API_KEY is set it routes through the Composio SDK, otherwise the direct LINEAR_API_KEY API. But @composio/core is an optional dependency that isn't installed with the plugin, so any user with COMPOSIO_API_KEY exported (common — AO is a Composio project, so contributors set it globally for unrelated work) hit a hard "Composio SDK (@composio/core) is not installed" failure on every tracker call, even with a valid LINEAR_API_KEY. There was no fallback.

This PR:

  • Makes the Composio transport throw a typed ComposioSdkMissingError when @composio/core can't be loaded.
  • Adds withComposioFallback: when the SDK is missing and a LINEAR_API_KEY is present, the tracker transparently falls back to the direct transport instead of failing.
  • Emits the tracker.dep_missing event only when there is genuinely no fallback (no LINEAR_API_KEY), so a successful fallback no longer raises a false error-level event.
  • Isolates COMPOSIO_API_KEY / COMPOSIO_ENTITY_ID in the direct-transport test suite (test/index.test.ts) so it's deterministic regardless of the developer's shell. (Previously this suite failed wholesale on machines with COMPOSIO_API_KEY exported; CI stayed green only because it deliberately omits the var.)

Test plan

  • New test/composio-fallback.test.ts: falls back to direct transport when @composio/core is missing but LINEAR_API_KEY is set; does not emit dep_missing on successful fallback; still throws when no LINEAR_API_KEY is available.
  • Existing activity-events.test.ts (dep_missing MUST emit) and composio-transport.test.ts (mocked SDK) unchanged and green.
  • Full suite: pnpm --filter @aoagents/ao-plugin-tracker-linear test → 81 passed.
  • typecheck and eslint clean on changed files.

🤖 Generated with Claude Code

…e is missing

A bare COMPOSIO_API_KEY (often exported globally for unrelated Composio
work) hijacked transport selection into the Composio SDK path, which
hard-failed because @composio/core is an optional dependency not installed
with the plugin — breaking the tracker even when a valid LINEAR_API_KEY was
present. The Composio transport now throws a typed ComposioSdkMissingError;
when a LINEAR_API_KEY is available the tracker transparently falls back to
the direct transport. The dep_missing event fires only when no fallback
exists. Also isolate COMPOSIO_API_KEY in the direct-transport test suite so
it is deterministic regardless of the developer's shell.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 22, 2026

Greptile Summary

Adds a typed ComposioSdkMissingError and a withComposioFallback wrapper so that users with COMPOSIO_API_KEY globally exported but @composio/core absent can still reach Linear via LINEAR_API_KEY instead of getting a hard failure; the dep_missing telemetry event is now only emitted when no fallback is possible.

  • src/index.ts: Extracts emitDepMissingOnce (using the existing recordTransportActivityEvent guard so logging errors can never surface to callers), introduces ComposioSdkMissingError as a typed sentinel, and wraps the composio transport in withComposioFallback inside create().
  • test/composio-fallback.test.ts: New regression suite covering fallback success, absence of the dep_missing event on successful fallback, hard-fail when no LINEAR_API_KEY, and correct error identity when activity logging itself throws.
  • test/index.test.ts: Isolates COMPOSIO_API_KEY/COMPOSIO_ENTITY_ID in the direct-transport suite so tests are deterministic on machines where those vars are globally exported.

Confidence Score: 5/5

Safe to merge — the fallback logic is correct, the once-per-process event guard is properly shielded by recordTransportActivityEvent, and the test suite covers all four key scenarios including the activity-log-throws edge case.

The withComposioFallback wrapper correctly handles the fallback path, emitDepMissingOnce calls recordTransportActivityEvent which already swallows logging exceptions, and env-var isolation in the direct-transport test suite removes the previously non-deterministic behaviour on developer machines. No new defects were identified in the changed code.

No files require special attention.

Important Files Changed

Filename Overview
packages/plugins/tracker-linear/src/index.ts Fallback logic, error extraction, and dep_missing guard all look correct; emitDepMissingOnce calls recordTransportActivityEvent which already has the try-catch shield.
packages/plugins/tracker-linear/test/composio-fallback.test.ts New regression suite covering fallback, no-dep_missing-on-fallback, no-key-throws, and activity-log-throws scenarios. Relies on @composio/core being absent rather than a mocked dynamic import, an explicit authorial choice noted in the file comment.
packages/plugins/tracker-linear/test/index.test.ts Adds save/restore of COMPOSIO_API_KEY and COMPOSIO_ENTITY_ID around each test so the direct-transport suite is deterministic on developer machines with those vars globally set.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["create()"] --> B{"COMPOSIO_API_KEY set?"}
    B -- No --> C["createDirectTransport()"]
    B -- Yes --> D["createComposioTransport() + withComposioFallback()"]
    D --> E["Call composio transport"]
    E --> F{"import @composio/core"}
    F -- Success --> G["Execute via Composio SDK"]
    F -- ERR_MODULE_NOT_FOUND --> H["throw ComposioSdkMissingError"]
    H --> I{"LINEAR_API_KEY set?"}
    I -- Yes --> J["createDirectTransport() cached"]
    J --> K["active = direct, call direct transport"]
    K --> L["Return result, no dep_missing event"]
    I -- No --> M["emitDepMissingOnce()"]
    M --> N["re-throw ComposioSdkMissingError"]
    G --> O["Return result"]
    C --> P["Execute via Linear HTTPS API"]
    P --> O
Loading

Reviews (2): Last reviewed commit: "fix(tracker-linear): address review — gu..." | Re-trigger Greptile

Comment thread packages/plugins/tracker-linear/src/index.ts
Comment thread packages/plugins/tracker-linear/src/index.ts
…ize fallback transport

- emitDepMissingOnce now routes through recordTransportActivityEvent so a
  throwing activity sink can't replace the ComposioSdkMissingError that
  callers (and tests) pattern-match on.
- Memoize the direct transport in withComposioFallback so concurrent
  first-calls reuse one instance instead of creating redundant ones.
- Add regression test: SDK-missing error still surfaces when logging throws.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@harshitsinghbhandari
Copy link
Copy Markdown
Contributor Author

Both addressed in 3aed256:

  1. emitDepMissingOnce logging guard — now routes through recordTransportActivityEvent, so a throwing activity sink can't escape the catch and replace the ComposioSdkMissingError. Added a regression test (surfaces the SDK-missing error even when activity logging throws).
  2. Concurrent fallback instances — memoized the direct transport (direct ??= createDirectTransport()), so concurrent first-calls reuse a single instance. The synchronous catch-body assignment completes before the next await yields, so ??= guarantees one instance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tracker-linear: ambient COMPOSIO_API_KEY breaks Linear tracker when @composio/core isn't installed

1 participant