mirror of
https://github.com/HKUDS/OpenSpace.git
synced 2026-10-17 04:33:40 +00:00
246 lines
7 KiB
Markdown
246 lines
7 KiB
Markdown
# Codex Desktop Sidecar Evolution Integration
|
||
|
||
## Goal
|
||
|
||
This integration keeps the normal Codex Desktop workflow unchanged while moving OpenSpace skill capture and self-evolution onto a separate provider-backed sidecar path.
|
||
|
||
The target user experience is:
|
||
|
||
- Main coding still happens in Codex Desktop with the user's normal subscription login.
|
||
- OpenSpace does not take over the main task loop.
|
||
- Sidecar evolution can be invoked explicitly after a task and spend provider API tokens instead of the main Codex Desktop session.
|
||
|
||
## Short Answer: Was this mainly an API-level dual routing change?
|
||
|
||
No.
|
||
|
||
The final effect does **not** come from a simple in-process "dual route" inside one OpenSpace runtime where:
|
||
|
||
- coding uses Codex Desktop subscription auth, and
|
||
- evolution uses a provider API
|
||
|
||
That approach is not viable because Codex Desktop subscription login is not exposed to the Python process as a reusable API credential.
|
||
|
||
Instead, the final implementation uses **process-level split routing**:
|
||
|
||
- the main coding session remains in Codex Desktop
|
||
- self-evolution runs through an isolated OpenSpace sidecar with its own provider-backed MCP server
|
||
|
||
API compatibility work was still necessary, but it is only one part of the solution.
|
||
|
||
## Embedding Split Routing
|
||
|
||
The sidecar now also supports a separate skill-embedding route from the main LLM.
|
||
|
||
Recommended setup:
|
||
|
||
```bash
|
||
OPENSPACE_MODEL=gpt-5.4
|
||
OPENSPACE_LLM_API_KEY=sk-xxx
|
||
OPENSPACE_LLM_API_BASE=http://127.0.0.1:8080/v1
|
||
|
||
OPENSPACE_SKILL_EMBEDDING_BACKEND=local
|
||
OPENSPACE_SKILL_EMBEDDING_MODEL=BAAI/bge-small-en-v1.5
|
||
```
|
||
|
||
If you want a dedicated remote endpoint for skill embeddings instead of local
|
||
fastembed, set:
|
||
|
||
```bash
|
||
OPENSPACE_SKILL_EMBEDDING_BACKEND=remote
|
||
OPENSPACE_SKILL_EMBEDDING_API_KEY=sk-embed-xxx
|
||
OPENSPACE_SKILL_EMBEDDING_API_BASE=https://example.com/v1
|
||
OPENSPACE_SKILL_EMBEDDING_MODEL=openai/text-embedding-3-small
|
||
```
|
||
|
||
## What Was Implemented
|
||
|
||
### 1. OpenAI-compatible provider bridge for OpenSpace
|
||
|
||
File:
|
||
|
||
- `openspace/llm/client.py`
|
||
|
||
Why it was needed:
|
||
|
||
- The third-party relay worked with Codex's `/responses` path.
|
||
- OpenSpace uses LiteLLM / OpenAI-style chat completion flows.
|
||
- The relay was not reliable enough for OpenSpace's normal streaming path.
|
||
|
||
What changed:
|
||
|
||
- Added an OpenAI-compatible streaming fallback that talks directly to `/chat/completions`.
|
||
- Reconstructed streamed text, reasoning content, and tool calls into the shape OpenSpace already expects.
|
||
- Enabled this path through `OPENSPACE_LLM_OPENAI_STREAM_COMPAT`.
|
||
|
||
Effect:
|
||
|
||
- OpenSpace can use the relay provider for evolution workloads.
|
||
|
||
### 2. Evolution-only MCP sidecar
|
||
|
||
File:
|
||
|
||
- `openspace/evolution_mcp_server.py`
|
||
|
||
Why it was needed:
|
||
|
||
- The user wanted OpenSpace to handle only post-task evolution and skill capture.
|
||
- The main coding loop had to stay outside OpenSpace.
|
||
|
||
What changed:
|
||
|
||
- Added a separate MCP server exposing only `evolve_from_context`.
|
||
- This server builds context from the current workspace, conversation summary, and git diff.
|
||
- It captures reusable skills without becoming the main task executor.
|
||
|
||
Effect:
|
||
|
||
- OpenSpace now has a narrow sidecar role instead of replacing the host coding agent.
|
||
|
||
### 3. Sidecar-capable skill engine without full task recording
|
||
|
||
File:
|
||
|
||
- `openspace/tool_layer.py`
|
||
|
||
Why it was needed:
|
||
|
||
- The original skill evolution path assumed a fuller OpenSpace task/recording pipeline.
|
||
- The new sidecar path needed to create skills without enabling the normal OpenSpace recording flow.
|
||
|
||
What changed:
|
||
|
||
- Added `enable_skill_engine_without_recording`.
|
||
- Kept execution analysis tied to recording.
|
||
- Allowed skill evolution and skill store initialization in sidecar mode without enabling full task recordings.
|
||
|
||
Effect:
|
||
|
||
- Sidecar capture can work independently without creating full OpenSpace task sessions.
|
||
|
||
### 4. Isolated Desktop launcher overlay
|
||
|
||
File:
|
||
|
||
- `scripts/codex-desktop-evolution`
|
||
|
||
Why it was needed:
|
||
|
||
- The main Codex Desktop session had to keep the user's normal login and defaults.
|
||
- The sidecar config had to be added without polluting `~/.codex`.
|
||
|
||
What changed:
|
||
|
||
- Created an overlay `CODEX_HOME` at `~/.codex-openspace-desktop`.
|
||
- Copied the primary Desktop auth and config base into the overlay.
|
||
- Added only one extra MCP server: `openspace_evolution`.
|
||
- Scrubbed `OPENSPACE_*` variables before launching the main Codex process.
|
||
- Avoided inheriting arbitrary shell state or leaking sidecar credentials into the main coding session.
|
||
|
||
Effect:
|
||
|
||
- Main Codex Desktop remains normal.
|
||
- The sidecar is available only in the isolated overlay profile.
|
||
|
||
### 5. Agent instruction trigger for sidecar capture
|
||
|
||
File:
|
||
|
||
- `AGENTS.md`
|
||
|
||
Why it was needed:
|
||
|
||
- The sidecar should be callable naturally from the Desktop workflow.
|
||
- The user should not need to restate the full MCP call every time.
|
||
|
||
What changed:
|
||
|
||
- Added a repo-level instruction that maps phrases like `sidecar 自进化一下` to `openspace_evolution.evolve_from_context`.
|
||
- Limited the default behavior to:
|
||
- no code changes
|
||
- no main-task takeover
|
||
- at most one high-reuse skill by default
|
||
|
||
Effect:
|
||
|
||
- The sidecar behaves like a narrow post-task tool integrated into the normal Desktop workflow.
|
||
|
||
## Other Supporting Changes
|
||
|
||
### MCP stdout flush fix
|
||
|
||
File:
|
||
|
||
- `openspace/mcp_server.py`
|
||
|
||
What changed:
|
||
|
||
- Avoided a final stdout flush crash when the MCP stdio transport closes before Python exit.
|
||
|
||
### Missing dependency for MCP backend
|
||
|
||
Files:
|
||
|
||
- `pyproject.toml`
|
||
- `requirements.txt`
|
||
|
||
What changed:
|
||
|
||
- Added `websockets>=15.0.0`
|
||
- Added `openspace-evolution-mcp` as a console entrypoint
|
||
|
||
### Frontend dependency refresh
|
||
|
||
File:
|
||
|
||
- `frontend/package-lock.json`
|
||
|
||
What changed:
|
||
|
||
- Updated `lodash-es`
|
||
- Updated `vite`
|
||
|
||
This was a maintenance fix and is not part of the sidecar architecture itself.
|
||
|
||
## Architecture Summary
|
||
|
||
The final architecture is:
|
||
|
||
1. Codex Desktop remains the main coding agent.
|
||
2. Codex Desktop keeps using the user's normal subscription login.
|
||
3. A separate overlay profile adds an `openspace_evolution` MCP server.
|
||
4. That MCP server runs OpenSpace with provider-backed credentials.
|
||
5. OpenSpace uses the provider only for post-task evolution and skill capture.
|
||
|
||
This means the practical "dual routing" exists at the workflow/process boundary, not as a single shared in-process auth router.
|
||
|
||
## Usage
|
||
|
||
Launch the Desktop profile that includes the sidecar:
|
||
|
||
```bash
|
||
cd /Users/admin/PycharmProjects/openspace
|
||
./scripts/codex-desktop-evolution app
|
||
```
|
||
|
||
Inside that Desktop session, trigger sidecar capture with:
|
||
|
||
```text
|
||
sidecar 自进化一下
|
||
```
|
||
|
||
or the longer explicit form:
|
||
|
||
```text
|
||
对当前这轮工作做一次 sidecar 自进化。不要改代码,不要接管任务。请调用 openspace_evolution.evolve_from_context,基于当前对话、git diff 和关键改动,自动提炼 task/summary,最多生成 1 个高复用 skill,并告诉我 skill 名称、路径、为什么值得保留。
|
||
```
|
||
|
||
## Result
|
||
|
||
The implemented effect is:
|
||
|
||
- normal Codex Desktop coding stays unchanged
|
||
- OpenSpace self-evolution is available on demand
|
||
- provider token spend is isolated to the sidecar path
|
||
- the sidecar does not silently take over the main workflow
|