Status: draft, 2026-05-25. Machine-readable companion: ide-command-api-map.yaml.
The current autonomy failure had two separate causes:
- The VS Code autopilot daemon was not running on
/run/user/1000/koru-autopilot-vscode.sock, so Koru had no live bridge for pushing prompts into IDE chat. - The latest autonomy trace skipped with
chat_activityforSTARTER-281because a recentmessage.sentevent was still inside the cooldown window.
After starting KORU_AUTOPILOT_INSTANCE=vscode koru autopilot daemon --idempotent,
koru autopilot status --explain reports ok=true, daemon 0.1.279, VS Code
plugin 0.1.78, protocol 1, and these plugin capabilities:
ide.commandschat.focuschat.pastechat.submitchat.eventschat.historyprobe.ladder
The plugin also reported 404 matching IDE commands. Today those commands are used internally by the probe ladder; the gap is that they are not exported as a stable API that an OpenRouter planner, an IDE LLM, or deterministic heuristics can inspect.
Koru should treat IDE control as a layered API, not as one magic "wake LLM" operation:
| Layer | Direction | Best Use | Current Entry |
|---|---|---|---|
| Planfile | Koru/LLM -> work queue | choose and close work | planfile ticket ... |
| MCP | IDE LLM -> Koru | read queue, run gates, get context | koru mcp-serve |
| Plugin socket | Koru -> IDE | push prompt to chat with trace | koru autopilot drive |
| IDE native commands | plugin -> IDE | focus/open/paste/submit/tasks | vscode.commands.executeCommand, JetBrains ActionManager |
| OS injector | Koru -> desktop | last-resort typing/keys | wtype, ydotool, xdotool, clipboard |
| OpenRouter | Koru -> hosted LLM | planning, semantic review | OPENROUTER_API_KEY, strategy prompt |
The strategy rule is simple: use the most semantic and observable layer that can do the job, then fall back only when diagnostics explain why the preferred layer cannot work.
The command facade exposed to LLMs and heuristics should be small and stable, even if each IDE has many native command IDs behind it.
| API command | State | Purpose |
|---|---|---|
koru.autonomy.status.explain |
stable | read daemon/plugin/backend health before drive |
koru.autonomy.trace |
stable | explain skip_code, decision, evidence, next step |
koru.planfile.queue.list |
stable | find runnable tickets |
koru.planfile.ticket.transition |
stable | mark done/input/fail after verification |
koru.ide.chat.drive |
stable | push text to IDE chat through daemon/plugin |
koru.ide.chat.handoff |
stable | build current Koru brief and send it to chat |
koru.mcp.list_tickets |
stable | IDE LLM reads queue via MCP |
koru.mcp.run_quality_gates |
stable | IDE LLM runs bounded gates via MCP |
koru.ide.commands.list |
proposed | expose plugin-observed IDE commands as an allowlist |
koru.ide.command.execute |
proposed | execute one allowlisted native IDE command |
koru.ide.workspace.open_file |
proposed | open a file/range in the IDE |
koru.ide.workspace.apply_edit |
proposed | apply structured workspace edits through IDE APIs |
koru.ide.diagnostics.list |
proposed | return IDE diagnostics without shell probing |
koru.ide.task.run |
proposed | run IDE task/debug configuration deterministically |
The YAML companion records parameters, side effects, and provider audience for each command.
Preferred transport: VS Code-family plugin socket.
Important native commands:
- Open chat:
workbench.action.chat.open - Focus input:
workbench.action.chat.focusInput,chat.action.focus,workbench.chat.action.focusLastFocused - Paste/type:
workbench.action.chat.insertText,workbench.action.chat.typeText,aichat.typeText, clipboard paste,type - Submit:
workbench.action.chat.submit,workbench.action.chat.acceptInput,workbench.action.chat.send,workbench.action.chat.sendMessage,workbench.action.interactive.accept - Repair:
workbench.action.reloadWindow,koruAutopilot.connect
Gap: message.received is not universally available, so Koru cannot always
read the IDE LLM's answer.
Preferred transport: Cursor VSIX/plugin socket.
Important native commands:
- Open/focus Composer:
composer.openComposer,composer.openAsPane,composer.focusComposer,cursor.composer.open,cursor.composer.focus - Paste/type:
cursor.action.chat.typeText,composer.typeText,aichat.typeText - Submit:
composer.sendToAgent,composer.acceptComposerStep,composer.startComposerPrompt,composer.startComposerPrompt2,composer.submit,aichat.submit - Host fallback: prefer
Ctrl+Return; plainReturncan insert a newline.
Cursor has the best partial observation story because the plugin can poll
Cursor chat-history SQLite and verify new user bubbles in cursorDiskKV.
Hazards:
composer.openAsPanecan toggle the panel hidden.- Clipboard-reading commands can paste stale user clipboard content.
- Host-key success can be a false positive on Wayland.
Preferred transport: VSCodium plugin socket, with host clipboard/key fallbacks.
Current strategy trusts fewer registered submit commands. It prefers
host-level Ctrl+Return before plain Return because workbench.action.chat.submit
and plain host keys can report success without sending.
Preferred transport: Windsurf native plugin path.
Important native commands:
- Atomic send:
windsurf.sendTextToChat - Open/focus Cascade:
windsurf.cascadePanel.open,windsurf.cascadePanel.focus,windsurf.action.openChat,windsurf.chat.open,windsurf.cascade.open,windsurf.panel.chat,cascade.focus,windsurf.action.showCascade - Focus input:
windsurf.action.focusChatInput,windsurf.chat.focusInput,windsurf.cascade.focusInput,cascade.focusInput - Paste/type:
windsurf.action.chat.typeText,windsurf.action.cascade.typeText,windsurf.chat.typeText,windsurf.cascade.typeText,cascade.typeText - Submit:
windsurf.action.cascade.submit,windsurf.action.submitCascade,windsurf.action.submitChat,windsurf.action.chat.submit,windsurf.chat.submit,windsurf.cascade.submit,cascade.submit
Best path: use windsurf.sendTextToChat as an atomic send when registered.
Preferred transport: Antigravity native plugin path.
Important native commands:
- Atomic send:
antigravity.sendPromptToAgentPanel - Open agent:
antigravity.openAgent,antigravity.agentSidePanel.open,antigravity.agentSidePanel.focus
Best path: require the atomic send command for submit. If it is absent, report the missing capability rather than trying unsafe generic paste loops.
Preferred transport: JetBrains plugin socket when installed; otherwise OS injector.
Current scaffold:
- Opens AI Assistant with
ActionManageraction IDs:AIAssistant.OpenAIAssistantToolWindow,AIAssistant.Chat.OpenChat,AiAssistant.OpenAiAssistantToolWindow,Grazie.OpenAssistant - Pastes by system clipboard plus AWT
Ctrl+V - Submits by AWT
Enter
Gaps to close:
- Use a real JSON encoder for
hello.capabilities. - Add command list/introspection for JetBrains action IDs.
- Add diagnostics/task APIs and stable chat lifecycle events.
Preferred current transport: OS injector. Koru has process detection and keyboard policy, but no native plugin bridge in this repo.
The better integration is a small Zed extension/RPC that implements the same facade: open chat, paste/send, observe result, list diagnostics, run task.
OpenRouter should not attempt GUI control. It should consume the command map as context and produce one of these outputs:
- a planfile ticket plan,
- a proposed
koru.yamlstrategy patch, - a semantic review of a patch,
- a recommendation to use IDE LLM/MCP/plugin only when local status says the bridge is healthy.
IDE-side LLMs should prefer MCP for pull operations and plugin socket for push operations:
- Use MCP to read tickets/context and run quality gates.
- Use filesystem edits or IDE edit APIs for code changes.
- Use
koru.ide.chat.driveonly for handoff/prompt continuation. - Use native IDE commands only through an allowlist exported by the plugin.
- Always run
koru.autonomy.status.explainbeforekoru.ide.chat.drive. - If the trace says
chat_activity, do not paste again; wait for cooldown or change the policy explicitly. - Prefer planfile tickets over broad discovery while runnable tickets exist.
- Prefer MCP for IDE LLM pull, plugin socket for Koru-to-chat push, and OS injector only as fallback.
- Prefer atomic vendor send commands where available:
windsurf.sendTextToChat,antigravity.sendPromptToAgentPanel. - Never retry submit blindly. Require
operation_trace,winning_submit, or a known observation event. - If
message.receivedis absent, treat IDE LLM completion as uncertain and rely on ticket state, file diffs, gates, and explicit operator/LLM handoff. - OpenRouter stays headless: planning, review, strategy, not GUI drive.
Extend KIDE v2 with a KIDE-004 capability layer:
command.ide.commands.listcommand.ide.command.executecommand.workspace.open_filecommand.workspace.apply_editcommand.diagnostics.listcommand.task.runevent.chat.message.received
The plugin already sees native commands through vscode.commands.getCommands(false).
The next step is to export a safe, categorized allowlist with schemas and
verification requirements instead of sending only a raw count in hello.