mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
f1a0b60c6c
* chore: unify product name display to OpenDesign repo-wide Rename the user-facing product name from "Open Design" to "OpenDesign" across documentation, web/desktop UI strings, i18n locales, prompts, plugin/skill/design-system content, changelogs, and specs, matching the landing-page rename already in flight. Deliberately excluded so installed-app identity stays untouched: - packages/release PRODUCT_NAME and everything derived from it (app bundle names, installer/registry identity, updater feeds) - tools/pack, tools/release, apps/packaged, landing page (PR #6983) - mocks/ recorded traces and LICENSE - test fixtures and comments that encode real on-disk install paths (Open Design.app, Program Files\Open Design, AppData paths) Validated with pnpm guard, pnpm typecheck, and targeted web/desktop/host test runs over the renamed strings. * fix(web): keep two-letter author monogram for CamelCase brand names authorInitials split words on whitespace only, so the renamed single-token author name "OpenDesign" produced a one-letter monogram. Treat lower-to-upper CamelCase boundaries as word breaks so the avatar fallback stays "OD". * fix(release): restore spaced Windows source artifact names in win.ps1 tools/pack still derives builder outputs from the unchanged PRODUCT_NAME ("Open Design"), so the asset script must look for the spaced filenames. Matches the mac/mac-intel/linux asset scripts, which were already kept on the original names. --------- Co-authored-by: Joey <236967869+joeylee12629-star@users.noreply.github.com>
219 lines
11 KiB
Markdown
219 lines
11 KiB
Markdown
# Runtime Adapter Current State
|
|
|
|
## Purpose
|
|
|
|
The runtime adapter layer lets the daemon drive locally installed AI-agent
|
|
CLIs through one OpenDesign run contract. It resolves and probes each CLI,
|
|
builds a runtime-specific invocation, delivers the composed prompt, normalizes
|
|
the runtime's output, and publishes run events to the web UI and `od` CLI.
|
|
|
|
## Current Source Layout
|
|
|
|
The implementation is split by responsibility:
|
|
|
|
- `apps/daemon/src/runtimes/registry.ts` owns the built-in registry and appends
|
|
configured local profiles.
|
|
- `apps/daemon/src/runtimes/defs/*.ts` contains one runtime definition per
|
|
built-in adapter. `apps/daemon/src/agents.ts` is now only a compatibility
|
|
re-export surface; it is not the definition source.
|
|
- `apps/daemon/src/runtimes/types.ts` defines `RuntimeAgentDef` and the detected
|
|
agent shape.
|
|
- `apps/daemon/src/runtimes/detection.ts`, `launch.ts`, `executables.ts`,
|
|
`invocation.ts`, and `models.ts` own probing, executable resolution, process
|
|
launch normalization, and model validation.
|
|
- `apps/daemon/src/routes/runs.ts` owns `POST /api/runs`, run event streaming,
|
|
cancellation, and the compatibility `POST /api/chat` route. The current run
|
|
execution pipeline is assembled in `apps/daemon/src/server.ts`.
|
|
- `apps/daemon/src/runtimes/claude-stream.ts`, `json-event-stream.ts`,
|
|
`qoder-stream.ts`, and `apps/daemon/src/copilot-stream.ts` normalize
|
|
runtime-specific structured output.
|
|
- `apps/daemon/src/agent-protocol/` owns the shared JSON-line transport and the
|
|
ACP and pi RPC protocol implementations.
|
|
|
|
## Registered Runtimes
|
|
|
|
`BASE_AGENT_DEFS` in `apps/daemon/src/runtimes/registry.ts` currently contains
|
|
26 built-in adapter definitions. `AGENT_DEFS` also appends valid local profiles
|
|
loaded by `readLocalAgentProfileDefs()`, so an installation can expose more
|
|
entries than the built-in list.
|
|
|
|
| Stream / protocol | Built-in adapter IDs | CLI binaries |
|
|
|---|---|---|
|
|
| `claude-stream-json` | `claude`, `amp`, `codebuddy` | `claude`, `amp`, `codebuddy` |
|
|
| `json-event-stream` | `codex`, `opencode`, `byok-opencode`, `cursor-agent`, `mimo` | `codex`, `opencode-cli`, `opencode-cli`, `cursor-agent`, `mimo` |
|
|
| `acp-json-rpc` | `amr`, `devin`, `hermes`, `trae-cli`, `kimi`, `kiro`, `kilo`, `vibe`, `reasonix` | `vela`, `devin`, `hermes`, `traecli`, `kimi`, `kiro-cli`, `kilo`, `vibe-acp`, `reasonix` |
|
|
| `pi-rpc` | `pi` | `pi` |
|
|
| `qoder-stream-json` | `qoder` | `qodercli` |
|
|
| `copilot-stream-json` | `copilot` | `copilot` |
|
|
| `plain` | `grok-build`, `qwen`, `deepseek`, `aider`, `antigravity`, `atomcode` | `grok`, `qwen`, `deepseek`, `aider`, `agy`, `atomcode` |
|
|
|
|
There is no registered `gemini` adapter. The standalone Gemini CLI runtime was
|
|
retired when Kimi ACP was restored. The JSON event parser still understands a
|
|
Gemini-shaped stream as an internal compatibility path for buffered
|
|
Antigravity output; that parser branch does not make Gemini a selectable
|
|
runtime.
|
|
|
|
## Runtime Definition Contract
|
|
|
|
Each `RuntimeAgentDef` supplies the declarative data and hooks needed by the
|
|
shared orchestration layer. The main groups are:
|
|
|
|
- Identity and executable discovery: `id`, `name`, `bin`, `fallbackBins`,
|
|
`versionArgs`, and optional help/capability probes.
|
|
- Model discovery and validation: `fallbackModels`, optional `listModels` or
|
|
`fetchModels`, optional `reasoningOptions`, and custom-model policy.
|
|
- Invocation: `buildArgs()`, optional runtime environment, prompt delivery
|
|
mode, image support, and prompt-size constraints.
|
|
- Output normalization: `streamFormat` and, for the shared JSON event stream,
|
|
`eventParser`.
|
|
- Integrations: MCP discovery/injection metadata, authentication probes,
|
|
inactivity timeouts, and install/documentation metadata.
|
|
- Session continuity: CLI resume, captured stream session IDs, or ACP
|
|
`session/load`, depending on the runtime.
|
|
|
|
Local profiles inherit a built-in adapter and may override identity, binary,
|
|
arguments, environment, models, and version/help probes. A profile cannot
|
|
replace a built-in ID, and an unknown explicit base adapter is rejected.
|
|
|
|
## Detection Flow
|
|
|
|
The batch entry point is `detectAgents()`; `detectAgentsStream()` yields the
|
|
same results as each probe settles.
|
|
|
|
1. Iterate over the registry, including valid local profiles.
|
|
2. Use `resolveAgentLaunch()` to select the executable that the run path will
|
|
actually launch. This includes configured binaries, compatible fallback
|
|
binaries, and wrapper/native-launch normalization.
|
|
3. Run the version probe. A missing, non-executable, or broken target is
|
|
reported as unavailable with a diagnostic.
|
|
4. For an invocable runtime, probe advertised CLI capabilities, models, and
|
|
authentication concurrently.
|
|
5. Use live model discovery when it succeeds; otherwise expose the adapter's
|
|
fallback model list and mark its source as `fallback`.
|
|
6. Refresh the model-validation cache from the result surfaced to clients.
|
|
|
|
A failed probe is isolated to that adapter instead of removing every agent
|
|
from the picker. Detection results include availability, resolved path,
|
|
version, models and their source, stream metadata, authentication state, and
|
|
diagnostics where applicable.
|
|
|
|
## Run Flow
|
|
|
|
`apps/daemon/src/routes/runs.ts` exposes the durable run API:
|
|
|
|
- `POST /api/runs` creates and starts a run.
|
|
- `GET /api/runs/:id/events` streams persisted and live run events.
|
|
- `POST /api/runs/:id/cancel` cancels the run.
|
|
- `POST /api/chat` remains the compatibility chat/SSE entry point.
|
|
|
|
For a local runtime, the shared execution pipeline:
|
|
|
|
1. Resolves `RuntimeAgentDef` from the request's `agentId` and validates the
|
|
selected model, reasoning option, project, attachments, and tool bundle.
|
|
2. Resolves the project working directory. This spec MUST NOT define daemon
|
|
data paths; read root [`AGENTS.md`](../../AGENTS.md) → **Daemon data
|
|
directory contract**.
|
|
3. Composes the daemon system prompt, applicable conversation context, active
|
|
skill and design-system content, attachments, and the current user turn.
|
|
4. Stages active skill side files inside the project when possible and builds
|
|
the runtime-specific allowed-directory set. Design-system bodies are read
|
|
by the daemon and folded into the prompt.
|
|
5. Resolves the same launch target used by detection, calls `buildArgs()`,
|
|
normalizes the platform-specific invocation, and starts the child with an
|
|
argument array and `shell: false`.
|
|
6. Delivers the prompt through the adapter's declared input mode and routes
|
|
stdout through the matching stream or protocol handler.
|
|
7. Publishes normalized agent events and lifecycle state to the run service,
|
|
while tracking cancellation, retries, inactivity, artifacts, usage, and
|
|
native-session recovery.
|
|
|
|
## Prompt Delivery and Session Continuity
|
|
|
|
Prompt delivery is no longer one universal argv strategy:
|
|
|
|
- Stdin-capable adapters declare `promptViaStdin`. Claude uses
|
|
`promptInputFormat: 'stream-json'`; other stdin adapters receive text.
|
|
- `grok-build` and `atomcode` opt into daemon-created prompt files.
|
|
- ACP and pi RPC sessions send the prompt through their protocol handshake.
|
|
- Any remaining argv-based adapter is checked against platform command-line
|
|
budgets before launch.
|
|
|
|
Session continuity is likewise adapter-specific:
|
|
|
|
- Claude, Codebuddy, and OpenCode can continue a CLI-owned session.
|
|
- Codex captures the thread ID from `thread.started` and resumes that explicit
|
|
thread on later turns.
|
|
- Pi persists and reuses its RPC session file.
|
|
- AMR can capture an ACP durable session ID and resume with `session/load`.
|
|
- Other adapters continue to receive daemon-composed conversation context.
|
|
|
|
The daemon owns the resume identity checks and transparent reseed behavior, so
|
|
runtime definitions only declare the mechanism they support.
|
|
|
|
## Output Normalization
|
|
|
|
The run pipeline selects a handler from `streamFormat`:
|
|
|
|
| Format | Current handler |
|
|
|---|---|
|
|
| `claude-stream-json` | `createClaudeStreamHandler()` in `runtimes/claude-stream.ts` |
|
|
| `json-event-stream` | `createJsonEventStreamHandler()` in `runtimes/json-event-stream.ts`, parameterized by `eventParser` |
|
|
| `qoder-stream-json` | `createQoderStreamHandler()` in `runtimes/qoder-stream.ts` |
|
|
| `copilot-stream-json` | `createCopilotStreamHandler()` in `copilot-stream.ts` |
|
|
| `pi-rpc` | `attachPiRpcSession()` from `agent-protocol/pi-rpc/` |
|
|
| `acp-json-rpc` | `attachAcpSession()` from `agent-protocol/acp/` |
|
|
| `plain` | guarded stdout/stderr forwarding |
|
|
|
|
Structured handlers normalize runtime output into events such as `status`,
|
|
`text_delta`, `thinking_start`, `thinking_delta`, `tool_use`, `tool_result`,
|
|
`usage`, and `error`. The shared run layer applies role-marker and substantive-
|
|
output guards before completing the run.
|
|
|
|
## ACP and Pi Protocol Layout
|
|
|
|
The old flat `apps/daemon/src/acp.ts` and `apps/daemon/src/pi-rpc.ts` modules no
|
|
longer exist. Their current boundary is:
|
|
|
|
```text
|
|
apps/daemon/src/agent-protocol/
|
|
├── core/ shared JSON-line transport
|
|
├── acp/ ACP model detection, session setup, RPC, and update mapping
|
|
├── pi-rpc/ pi model parsing, session lifecycle, and event mapping
|
|
└── index.ts explicit public re-exports
|
|
```
|
|
|
|
`core/` is the shared foundation. ACP and pi RPC do not import each other's
|
|
internals. See `apps/daemon/src/agent-protocol/README.md` for the module's
|
|
public surface and dependency rules.
|
|
|
|
## Safety and Lifecycle Guarantees
|
|
|
|
The current adapter layer includes these protections:
|
|
|
|
- Detection and execution resolve the same launch target; a run does not fall
|
|
back to blindly spawning `def.bin` after detection fails.
|
|
- Child processes use argument arrays with `shell: false`; platform wrapper
|
|
normalization is centralized in the invocation layer.
|
|
- Model IDs, reasoning choices, attachment paths, prompt budgets, working
|
|
directories, and optional external MCP injection are validated before use.
|
|
- Structured protocol stages and the overall run have timeout/inactivity
|
|
guards.
|
|
- `POST /api/runs/:id/cancel` propagates cancellation to the active child or
|
|
protocol session, with forced shutdown as a fallback.
|
|
- Native session IDs are persisted only for adapters that declare a resume
|
|
mechanism, and stale resume targets are cleared and reseeded once.
|
|
|
|
## Current Architecture Boundary
|
|
|
|
The runtime layer has already moved beyond a single `agents.ts` object array:
|
|
definitions, probing, executable resolution, prompt budgeting, parsers, and
|
|
subprocess protocols are separate modules. It remains intentionally centered
|
|
on declarative `RuntimeAgentDef` records plus shared run orchestration rather
|
|
than independent adapter classes implementing `detect()`, `run()`, `cancel()`,
|
|
and `resume()`.
|
|
|
|
Cancellation, retries, persistence, SSE/AG-UI delivery, and most resume policy
|
|
are run-service concerns. Runtime definitions own only the CLI-specific facts
|
|
and hooks needed by that shared pipeline. Cloud/API provider execution remains
|
|
a separate provider path; `byok-opencode` is the local OpenCode bridge.
|