fix(cursor): register plugin in all discovery/resolution layers

Address PR review feedback from #1076 to make Cursor agent fully functional.

## Changes

### Plugin Registration (High Severity Fixes)
- **core/plugin-registry.ts**: Add cursor to BUILTIN_PLUGINS array for config-based resolution
- **cli/plugins.ts**: Import and register cursor in agentPlugins map for getAgent/getAgentByName
- **cli/detect-agent.ts**: Add cursor to AGENT_PLUGINS array for detectAvailableAgents()
- **web/services.ts**: Import and register cursor plugin for dashboard SessionManager/LifecycleManager
- **web/package.json**: Add @composio/ao-plugin-agent-cursor dependency

### Activity Detection Fix (Medium Severity)
- **cursor/index.ts**: Remove overly broad blocked detection patterns (error:/failed:)
  - Compiler errors, test failures, and linter output are normal tool output
  - Terminal-based detection can't distinguish between actionable agent errors and normal output
  - Follow Aider/OpenCode pattern: only Claude Code detects blocked (has native JSONL)
  - Added comment explaining why blocked detection is removed
- **cursor/index.test.ts**: Remove blocked detection test cases

## Why These Changes Are Needed

Before these fixes:
- `defaults.agent: cursor` in config would throw "Unknown agent plugin: cursor"
- `ao spawn --agent cursor` would fail
- Dashboard couldn't resolve cursor agent (SessionManager/LifecycleManager failures)
- `detectAvailableAgents()` never discovered Cursor
- False "blocked" states when agent encountered normal compiler errors

After these fixes:
- Cursor agent fully discoverable and usable via config and CLI
- Dashboard can spawn and manage Cursor sessions
- Activity detection matches other terminal-based agents (Aider, OpenCode)
- No false positives from normal tool output

## Testing
-  cursor plugin tests: 51/51 passing (reduced from 52 due to removed blocked tests)
-  core typecheck: clean
-  web typecheck: clean
-  CLI can import cursor plugin without errors

Addresses: ComposioHQ/agent-orchestrator#1076

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
harshitsinghbhandari 2026-04-09 19:17:41 +05:30
parent 464f771a76
commit 04f3a7c27f
8 changed files with 16 additions and 8 deletions

View File

@ -18,6 +18,7 @@ const AGENT_PLUGINS: Array<{ name: string; pkg: string }> = [
{ name: "claude-code", pkg: "@composio/ao-plugin-agent-claude-code" },
{ name: "aider", pkg: "@composio/ao-plugin-agent-aider" },
{ name: "codex", pkg: "@composio/ao-plugin-agent-codex" },
{ name: "cursor", pkg: "@composio/ao-plugin-agent-cursor" },
{ name: "opencode", pkg: "@composio/ao-plugin-agent-opencode" },
];

View File

@ -2,6 +2,7 @@ import type { Agent, OrchestratorConfig, PluginRegistry, SCM } from "@composio/a
import claudeCodePlugin from "@composio/ao-plugin-agent-claude-code";
import codexPlugin from "@composio/ao-plugin-agent-codex";
import aiderPlugin from "@composio/ao-plugin-agent-aider";
import cursorPlugin from "@composio/ao-plugin-agent-cursor";
import opencodePlugin from "@composio/ao-plugin-agent-opencode";
import githubSCMPlugin from "@composio/ao-plugin-scm-github";
@ -9,6 +10,7 @@ const agentPlugins: Record<string, { create(): Agent }> = {
"claude-code": claudeCodePlugin,
codex: codexPlugin,
aider: aiderPlugin,
cursor: cursorPlugin,
opencode: opencodePlugin,
};

View File

@ -38,6 +38,7 @@ const BUILTIN_PLUGINS: Array<{ slot: PluginSlot; name: string; pkg: string }> =
{ slot: "agent", name: "claude-code", pkg: "@composio/ao-plugin-agent-claude-code" },
{ slot: "agent", name: "codex", pkg: "@composio/ao-plugin-agent-codex" },
{ slot: "agent", name: "aider", pkg: "@composio/ao-plugin-agent-aider" },
{ slot: "agent", name: "cursor", pkg: "@composio/ao-plugin-agent-cursor" },
{ slot: "agent", name: "opencode", pkg: "@composio/ao-plugin-agent-opencode" },
// Workspaces
{ slot: "workspace", name: "worktree", pkg: "@composio/ao-plugin-workspace-worktree" },

View File

@ -322,11 +322,6 @@ describe("detectActivity", () => {
expect(agent.detectActivity("Press Enter to continue")).toBe("waiting_input");
});
it("returns blocked for error messages", () => {
expect(agent.detectActivity("error: failed to connect")).toBe("blocked");
expect(agent.detectActivity("failed: timeout exceeded")).toBe("blocked");
});
it("returns active for non-empty terminal output", () => {
expect(agent.detectActivity("cursor is processing files\n")).toBe("active");
});

View File

@ -178,9 +178,12 @@ function createCursorAgent(): Agent {
if (/proceed\?/i.test(tail)) return "waiting_input";
if (/Press Enter to continue/i.test(tail)) return "waiting_input";
// Check for error indicators
if (/error:/i.test(tail)) return "blocked";
if (/failed:/i.test(tail)) return "blocked";
// Note: "blocked" detection removed — compiler errors, test failures, and linter
// messages are extremely common in normal tool output. Unlike Claude Code (which
// has native JSONL with rich "error" types), terminal-based detection can't
// distinguish between actionable agent errors and normal tool output.
// If Cursor CLI provides native JSONL in the future, blocked detection can be
// added to getActivityState() based on JSONL entry types.
return "active";
},

View File

@ -29,6 +29,7 @@
"dependencies": {
"@composio/ao-core": "workspace:*",
"@composio/ao-plugin-agent-claude-code": "workspace:*",
"@composio/ao-plugin-agent-cursor": "workspace:*",
"@composio/ao-plugin-agent-opencode": "workspace:*",
"@composio/ao-plugin-runtime-tmux": "workspace:*",
"@composio/ao-plugin-scm-github": "workspace:*",

View File

@ -39,6 +39,7 @@ import {
// Static plugin imports — webpack needs these to be string literals
import pluginRuntimeTmux from "@composio/ao-plugin-runtime-tmux";
import pluginAgentClaudeCode from "@composio/ao-plugin-agent-claude-code";
import pluginAgentCursor from "@composio/ao-plugin-agent-cursor";
import pluginAgentOpencode from "@composio/ao-plugin-agent-opencode";
import pluginWorkspaceWorktree from "@composio/ao-plugin-workspace-worktree";
import pluginScmGithub from "@composio/ao-plugin-scm-github";
@ -81,6 +82,7 @@ async function initServices(): Promise<Services> {
// Register plugins explicitly (webpack can't handle dynamic import() in core)
registry.register(pluginRuntimeTmux);
registry.register(pluginAgentClaudeCode);
registry.register(pluginAgentCursor);
registry.register(pluginAgentOpencode);
registry.register(pluginWorkspaceWorktree);
registry.register(pluginScmGithub);

View File

@ -600,6 +600,9 @@ importers:
'@composio/ao-plugin-agent-claude-code':
specifier: workspace:*
version: link:../plugins/agent-claude-code
'@composio/ao-plugin-agent-cursor':
specifier: workspace:*
version: link:../plugins/agent-cursor
'@composio/ao-plugin-agent-opencode':
specifier: workspace:*
version: link:../plugins/agent-opencode