Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions docs/developers/daemon-client-adapters/ide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# IDE Daemon Adapter Draft

## Goal

Let the VS Code companion extension dogfood Mode B by connecting from the
extension host to `qwen serve` through `DaemonSessionClient`.

The webview must not call the daemon directly. The extension host owns daemon
URL, token, session id, and SSE replay state, then forwards sanitized app events
to the webview.

## Proposed Entry Point

VS Code settings:

```json
{
"qwen-code.experimentalDaemon.enabled": true,
"qwen-code.experimentalDaemon.url": "http://127.0.0.1:4170",
"qwen-code.experimentalDaemon.token": ""
}
```

Environment fallback for local dogfood:

```bash
QWEN_IDE_DAEMON_URL=http://127.0.0.1:4170 code .
```

## Minimal Flow

1. Extension host creates `DaemonClient`.
2. Fetch `/capabilities` and verify workspace compatibility.
3. Create or attach with `DaemonSessionClient.createOrAttach()`.
4. Subscribe to `session.events()` in the extension host.
5. Translate daemon events into existing webview messages.
6. Send user prompts through `session.prompt()`.
7. Route cancel/model switch through `session.cancel()` and
`session.setModel()`.
8. Route permission decisions through `session.respondToPermission()`.

## Relationship To Existing ACP Connection

The first implementation introduces a sibling connection path, not replace
`AcpConnection`:

```text
QwenAgentManager
current default -> AcpConnection -> qwen --acp child
experimental -> DaemonIdeConnection -> qwen serve HTTP/SSE
```

Both paths should feed the same higher-level webview callbacks where practical.
If an event cannot be faithfully mapped yet, the daemon path should surface a
clear unsupported-state warning rather than silently pretending parity.

This PR adds `DaemonIdeConnection` as the locally verifiable extension-host
adapter spike. It is not wired into the default `QwenAgentManager` path yet, so
existing VS Code behavior remains ACP subprocess based.

## Event Mapping Contract

| Daemon event | IDE handling |
| ---------------------------------------- | -------------------------------------------- |
| `session_update` / `agent_message_chunk` | Existing assistant stream callback |
| `session_update` / `agent_thought_chunk` | Existing thinking stream callback |
| `session_update` / `tool_call` | Existing tool-call update callback |
| `permission_request` | Existing approval UI callback |
| `permission_resolved` | Close/update approval UI |
| `model_switched` | Existing model-state callback where possible |
| `session_died` | Disconnect UI + reconnect affordance |

Unknown events must be ignored or logged as debug metadata.

## Runtime Locality UX

The extension must make daemon locality visible:

- workspace/files are daemon-host paths
- MCP servers run on the daemon host
- skills load from the daemon filesystem
- provider credentials are resolved in the daemon process environment

Do not imply that local VS Code extensions, local browser profile, local
localhost services, or local SSH/kube credentials are automatically available to
the daemon.

## Explicit Non-Goals

- No default migration away from `AcpConnection`.
- No webview direct-to-daemon transport.
- No daemon-side file CRUD through the IDE until file service boundaries land.
- No reverse RPC for editor/browser/clipboard yet.
- No full remote-control integration.

## Merge Safety

- Default off behind setting/env.
- Additive sibling connection path.
- Existing VS Code ACP subprocess path unchanged.
- Daemon token never crosses into webview JavaScript.

## Validation Plan

- Unit-test daemon session factory connection and SSE event consumption.
- Unit-test daemon event to existing extension-host callback mapping.
- Unit-test prompt, cancel, model switch, and permission response forwarding.
- Unit-test settings/env resolution when the feature flag is wired.
- Smoke-test local extension host against `qwen serve`:
- prompt streams into chat
- cancel works
- permission UI can resolve a request
- SSE reconnect uses tracked `Last-Event-ID`

## Blockers Before Default Migration

- Typed daemon event schema.
- Daemon-stamped client identity.
- Session-scoped permission route.
- Read-only runtime diagnostics.
- FileSystemService boundary and safe file read routes.
- Output sink refactor for CLI/TUI parity.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/vscode-ide-companion/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@
},
"dependencies": {
"@agentclientprotocol/sdk": "^0.14.1",
"@qwen-code/sdk": "*",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] Declaring @qwen-code/sdk here does not make the hidden dynamic import available in the packaged VSIX. The extension script still uses vsce package --no-dependencies, .vscodeignore only includes dist/ plus a few assets, and daemonIdeConnection.ts intentionally hides import('@qwen-code/sdk') from esbuild. That means local builds/tests can pass, but an installed extension will not contain node_modules/@qwen-code/sdk, so enabling the daemon adapter fails at runtime before it can connect. Please either bundle the SDK with a normal import/esbuild path or explicitly include the runtime dependency in the VSIX packaging.

— gpt-5.5 via Qwen Code /review

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] "@qwen-code/sdk": "*" resolves to any version. Since the SDK is dynamically imported at runtime, a breaking change in the SDK's export shape would silently produce a runtime "Loaded @qwen-code/sdk does not expose daemon clients" error instead of a clear install-time version mismatch.

Consider pinning to a minimum major version or using a caret range (^1.0.0) to get install-time warnings on breaking changes.

"@qwen-code/webui": "*",
"@modelcontextprotocol/sdk": "^1.25.1",
"cors": "^2.8.5",
Expand Down
4 changes: 4 additions & 0 deletions packages/vscode-ide-companion/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import { registerNewCommands } from './commands/index.js';
import { ReadonlyFileSystemProvider } from './services/readonlyFileSystemProvider.js';
import { isWindows } from './utils/platform.js';

// Keep the dormant daemon IDE adapter on the VSIX bundle path without wiring it
// into the active extension flow yet.
export { createSdkDaemonSessionFactory as __daemonIdeSessionFactoryForBundle } from './services/daemonIdeConnection.js';

const CLI_IDE_COMPANION_IDENTIFIER = 'qwenlm.qwen-code-vscode-ide-companion';
const INFO_MESSAGE_SHOWN_KEY = 'qwenCodeInfoMessageShown';
export const DIFF_SCHEME = 'qwen-diff';
Expand Down
Loading
Loading